React基礎筆記(三)

這是我參與8月更文挑戰的第3天,活動詳情查看:8月更文挑戰vue

前言

本文做爲本人學習總結之用,同時分享給你們,適合入門的react小白
由於我的技術有限,若是有發現錯誤或存在疑問之處,歡迎指出或指點!不勝感謝!react

redux

基本理解

學習文檔

redux

  1. redux是一個專門用於作管理狀態的JS庫 (不是react插件庫)
  2. 它能夠用在react, angular, vue等項目中, 但基本與react配合使用。
  3. 做用: 集中式管理react應用中多個組件的狀態。

什麼狀況下須要使用redux

  1. 某個組件的狀態,須要讓其餘組件能夠隨時拿到(共享)。
  2. 一個組件須要改變另外一個組件的狀態(通訊)。
  3. 整體原則:能不用就不用, 若是不用比較吃力才考慮使用。

工做流程

redux的三個核心概念

action

  1. 動做的對象git

  2. 包含兩個屬性github

    • type:標識屬性, 值爲字符串, 惟一, 必要屬性
    • data:數據屬性, 值類型任意, 可選屬性
  3. 編碼:ajax

//該模塊是用於定義action對象中type類型的常量值,目的只有一個:便於管理的同時防止單詞寫錯
import {ADD_PERSON} from '../constant'
//專門爲組件生成action對象
export const addPerson = data => ({type:ADD_PERSON,data})
複製代碼

reducer

  1. 用於初始化狀態、加工狀態。
  2. 加工時,根據舊的state和action, 產生新的state的純函數
const initState = xx
export default function xxxReducer(preState =initState, action) {
  const {type ,data} = action
  switch (type) {
    case JIA:
      return preState+1
    default :
      return preState
  }
}
複製代碼

store

  1. 將state、action、reducer聯繫在一塊兒的對象
  2. 如何獲得此對象?
//引入createStore,專門用於建立redux中最爲核心的store對象
import {createStore} from 'redux'
//引入爲Count組件服務的reducer
import countReducer from './count_reducer'
//暴露store
export default createStore(countReducer)
複製代碼
  1. 此對象的功能?npm

    • getState(): 獲得state
    • dispatch(action): 分發action, 觸發reducer調用, 產生新的state
    • subscribe(listener): 註冊監聽, 當產生了新的state時, 自動調用
componentDidMount(){
    //檢測redux中狀態的變化,只要變化,就調用render
    store.subscribe(()=>{
            this.setState({})
    })
}
store.dispatch(createIncrementAction(value*1))
store.getState()
複製代碼

redux核心api

createStore

做用:建立包含指定reducer的store對象redux

6.3.2 store對象

  1. 做用: redux庫最核心的管理對象
  2. 它內部維護着:
    1. state
    2. reducer
  3. 核心方法:
    1. getState()
    2. dispatch(action)
    3. subscribe(listener)
  4. 具體編碼:
    1. store.getState()
    2. store.dispatch({type:'INCREMENT', number})
    3. store.subscribe(render)

applyMiddleware()

做用:應用上基於redux的中間件(插件庫)api

combineReducers()

做用:合併多個reducer函數數組

redux異步

1. redux默認是不能進行異步處理的,緩存

2. 某些時候應用中須要在redux 中執行異步任務(ajax, 定時器)

必須下載插件:

npm install --save redux-thunk

在store.js文件中引入redux-thunk,用於支持異步action
import thunk from 'redux-thunk'

react-redux (關鍵)

理解

  1. 一個專門的react插件
  2. 專門簡化react應用中使用redux

react-redux組件拆分兩大類

UI組件

  • 只負責 UI 的呈現,不帶有任何業務邏輯    
  • 經過props接收數據(通常數據和函數)
  • 不使用任何 Redux 的 API
  • 通常保存在components文件夾下

容器組件

  • 負責管理數據和業務邏輯,不負責UI的呈現
  • 使用 Redux 的 API
  • 通常保存在containers文件夾下

相關API

  1. Provider:讓全部組件均可以獲得state數據
import {Provider} from 'react-redux'
<Provider store={store}>
  <App/>
</Provider>
複製代碼
  1. connect:用於包裝 UI 組件生成容器組件
  2. mapStateToprops:將外部的數據(即state對象)轉換爲UI組件的標籤屬性
  3. mapDispatchToProps:將分發action的函數轉換爲UI組件的標籤屬性
//引入Count的UI組件
import CountUI from '../../components/Count'
//引入action
import {
	createIncrementAction,
} from '../../redux/count_action'

//引入connect用於鏈接UI組件與redux
import {connect} from 'react-redux'

/* 1.mapStateToProps函數返回的是一個對象; 2.返回的對象中的key就做爲傳遞給UI組件props的key,value就做爲傳遞給UI組件props的value 3.mapStateToProps用於傳遞狀態 */
function mapStateToProps(state){
	return {count:state}
}

/* 1.mapDispatchToProps函數返回的是一個對象; 2.返回的對象中的key就做爲傳遞給UI組件props的key,value就做爲傳遞給UI組件props的value 3.mapDispatchToProps用於傳遞操做狀態的方法 */
function mapDispatchToProps(dispatch){
	return {
		jia:number => dispatch(createIncrementAction(number))
	}
}

//使用connect()()建立並暴露一個Count的容器組件
export default connect(mapStateToProps,mapDispatchToProps)(CountUI)

//簡寫
export default connect(
	state => ({count:state}),
  {jia:createIncrementAction}
)(Count)
複製代碼

setState

  1. setState(stateChange, [callback])------對象式的setState

    1.stateChange爲狀態改變對象(該對象能夠體現出狀態的更改)
    2.callback是可選的回調函數, 它在狀態更新完畢、界面也更新後(render調用後)才被調用
    複製代碼
  2. setState(updater, [callback])------函數式的setState

    1.updater爲返回stateChange對象的函數。
    2.updater能夠接收到state和props。
    3.callback是可選的回調函數, 它在狀態更新、界面也更新後(render調用後)才被調用。
    複製代碼

總結:

  1. 對象式的setState是函數式的setState的簡寫方式(語法糖)
  2. 使用原則:
    • 若是新狀態不依賴於原狀態 ===> 使用對象方式
    • 若是新狀態依賴於原狀態 ===> 使用函數方式
    • 若是須要在setState()執行後獲取最新的狀態數據,
      要在第二個callback函數中讀取
//對象式的setState
//1.獲取原來的count值
const {count} = this.state
//2.更新狀態
this.setState({count:count+1},()=>{
  console.log(this.state.count);
})
//console.log('12行的輸出',this.state.count); //0 

//函數式的setState
this.setState( (state,props) => ({count:state.count+1}),() =>{
  console.log(state.count)
})
複製代碼

lazyLoad 路由懶加載

//1.經過React的lazy函數配合import()函數動態加載路由組件 ===> 路由組件代碼會被分開打包
   const Login = lazy(()=>import('@/pages/Login'))
   
   //2.經過<Suspense>指定在加載獲得路由打包文件前顯示一個自定義loading界面
   fallback包裹一個通常組件也能夠
   <Suspense fallback={<h1>loading.....</h1>}>
        <Switch> <Route path="/xxx" component={Xxxx}/> <Redirect to="/login"/> </Switch>
    </Suspense>
複製代碼

Hooks

  1. React Hook/Hooks是什麼?

    • Hook是React 16.8.0版本增長的新特性/新語法
    • 可讓你在函數組件中使用 state 以及其餘的 React 特性
  2. 三個經常使用的Hook

    • State Hook: React.useState()
    • Effect Hook: React.useEffect()
    • Ref Hook: React.useRef()
  3. State Hook

    • State Hook讓函數組件也能夠有state狀態, 並進行狀態數據的讀寫操做
    • 語法: const [xxx, setXxx] = React.useState(initValue)
    • useState()說明:
      參數: 第一次初始化指定的值在內部做緩存
      返回值: 包含2個元素的數組, 第1個爲內部當前狀態值, 第2個爲更新狀態值的函數\
  4. setXxx()2種寫法:

    • setXxx(newValue): 參數爲非函數值, 直接指定新的狀態值, 內部用其覆蓋原來的狀態值
    • setXxx(value => newValue): 參數爲函數, 接收本來的狀態值, 返回新的狀態值, 內部用其覆蓋原來的狀態值
  5. Effect Hook

    • Effect Hook 可讓你在函數組件中執行反作用操做(用於模擬類組件中的生命週期鉤子)
    • React中的反作用操做:
      • 發ajax請求數據獲取
      • 設置訂閱 / 啓動定時器
      • 手動更改真實DOM
    • 語法和說明:
    useEffect(() => { 
      // 在此能夠執行任何帶反作用操做
      return () => { // 在組件卸載前執行
        // 在此作一些收尾工做, 好比清除定時器/取消訂閱等
      }
    }, [stateValue]) // 若是指定的是[], 回調函數只會在第一次render()後執行
    複製代碼
    • 能夠把 useEffect Hook 看作以下三個函數的組合

      componentDidMount()
      componentDidUpdate()
      componentWillUnmount()

  6. Ref Hook

    • Ref Hook能夠在函數組件中存儲/查找組件內的標籤或任意其它數據
    • 語法: const refContainer = useRef()
    • 做用:保存標籤對象,功能與React.createRef()同樣
function Demo(){
	const [count,setCount] = React.useState(0)
	const myRef = React.useRef()

	React.useEffect(()=>{
		let timer = setInterval(()=>{
			setCount(count => count+1 )
		},1000)
		return ()=>{
			clearInterval(timer)
		}
	},[])

	//加的回調
	function add(){
		//setCount(count+1) //第一種寫法
		setCount(count => count+1 )
	}

	//提示輸入的回調
	function show(){
		alert(myRef.current.value)
	}

	//卸載組件的回調
	function unmount(){
		ReactDOM.unmountComponentAtNode(document.getElementById('root'))
	}

	return (
		<div> <input type="text" ref={myRef}/> <h2>當前求和爲:{count}</h2> <button onClick={add}>點我+1</button> <button onClick={unmount}>卸載組件</button> <button onClick={show}>點我提示數據</button> </div>
	)
}
複製代碼

Fragment

做用:能夠不用必須有一個真實的DOM根標籤了

<Fragment key={1}>  //能參與遍歷
  <input type="text"/>
  <input type="text"/>
</Fragment>
或者
<>
  <input type="text"/>
  <input type="text"/>
</>
複製代碼

Context

一種組件間通訊方式, 經常使用於【祖組件】與【後代組件】間通訊

  1. 建立Context容器對象:

  const XxxContext = React.createContext()

  1. 渲染子組時,外面包裹xxxContext.Provider, 經過value屬性給後代組件傳遞數據:

  <xxxContext.Provider value={數據}>子組件</xxxContext.Provider>

  1. 後代組件讀取數據:

    //第一種方式:僅適用於類組件
     static contextType = xxxContext  // 聲明接收context
     this.context // 讀取context中的value數據
    
    
    //第二種方式: 函數組件與類組件均可以
    <xxxContext.Consumer>
    {
      value => ( // value就是context中的value數據
          要顯示的內容
      )
    }
    </xxxContext.Consumer>
    複製代碼

注意:在應用開發中通常不用context, 通常都它的封裝react插件

//建立Context對象
const MyContext = React.createContext()
const {Provider,Consumer} = MyContext
export default class A extends Component {

	state = {username:'tom',age:18}
	render() {
		const {username,age} = this.state
		return (
			<div className="parent"> <h3>我是A組件</h3> <h4>個人用戶名是:{username}</h4> <Provider value={{username,age}}> <B/> </Provider> </div>
		)
	}
}

class B extends Component {
	render() {
		return (
			<div className="child"> <h3>我是B組件</h3> <C/> </div>
		)
	}
}

 /* class C extends Component { //聲明接收context static contextType = MyContext render() { const {username,age} = this.context return ( <div className="grand"> <h3>我是C組件</h3> <h4>我從A組件接收到的用戶名:{username},年齡是{age}</h4> </div> ) } } */

function C(){
	return (
		<div className="grand"> <h3>我是C組件</h3> <h4>我從A組件接收到的用戶名: <Consumer> {value => `${value.username},年齡是${value.age}`} </Consumer> </h4> </div>
	)
}
複製代碼

組件優化 Component

項目的2個問題

  1. 只要執行setState(),即便不改變狀態數據, 組件也會從新render()

  2. 只當前組件從新render(), 就會自動從新render子組件 ==> 效率低

效率高的作法:

只有當組件的state或props數據發生改變時才從新render()

緣由:

Component中的shouldComponentUpdate()老是返回true

解決:

  辦法1:

重寫shouldComponentUpdate()方法
 比較新舊state或props數據, 若是有變化才返回true, 若是沒有返回false
複製代碼

  辦法2:  

使用PureComponent
 PureComponent重寫了shouldComponentUpdate(), 只有state或props數據有變化才返回true
 注意:
    只是進行state和props數據的淺比較, 若是隻是數據對象內部數據變了, 返回false  
    不要直接修改state數據, 而是要產生新數據
    
複製代碼

項目中通常使用PureComponent來優

import React, { PureComponent } from 'react'
export default class Parent extends PureComponent {
	state = {carName:"奔馳c36",stus:['小張','小李','小王']}
	addStu = ()=>{
		const {stus} = this.state
		this.setState({stus:['小劉',...stus]})
	}

	changeCar = ()=>{
		this.setState({carName:'邁巴赫'})
	}
	/* shouldComponentUpdate(nextProps,nextState){ // console.log(this.props,this.state); //目前的props和state // console.log(nextProps,nextState); //接下要變化的目標props,目標state return !this.state.carName === nextState.carName } */
	render() {
		console.log('Parent---render');
		const {carName} = this.state
		return (
			<div className="parent"> <h3>我是Parent組件</h3> {this.state.stus}&nbsp; <span>個人車名字是:{carName}</span><br/> <button onClick={this.changeCar}>點我換車</button> <button onClick={this.addStu}>添加一個小劉</button> <Child carName="奧拓"/> </div>
		)
	}
}

class Child extends PureComponent {
	/* shouldComponentUpdate(nextProps,nextState){ console.log(this.props,this.state); //目前的props和state console.log(nextProps,nextState); //接下要變化的目標props,目標state return !this.props.carName === nextProps.carName } */
	render() {
		console.log('Child---render');
		return (
			<div className="child"> <h3>我是Child組件</h3> <span>我接到的車是:{this.props.carName}</span> </div>
		)
	}
}
複製代碼

\

render props

如何向組件內部動態傳入帶內容的結構(標籤)?

  Vue中:

     使用slot技術, 也就是經過組件標籤體傳入結構  <AA><BB/></AA>

  React中:

- 使用children props: 經過組件標籤體傳入結構
- 使用render props: 經過組件標籤屬性傳入結構, 通常用render函數屬性
複製代碼

children props

  <A>
    <B>xxxx</B>
  </A>
  {this.props.children}
複製代碼

  問題: 若是B組件須要A組件內的數據, ==> 作不到

render props

  <A render={(data) => <C data={data}></C>}></A>

  A組件: {this.props.render(內部state數據)}

  C組件: 讀取A組件傳入的數據顯示 {this.props.data}

錯誤邊界

理解:

錯誤邊界:用來捕獲後代組件錯誤,渲染出備用頁面

特色:

只能捕獲後代組件生命週期產生的錯誤,不能捕獲本身組件產生的錯誤和其餘組件在合成事件、定時器中產生的錯誤

使用方式:

getDerivedStateFromError配合componentDidCatch
// 生命週期函數,一旦後臺組件報錯,就會觸發
//當Parent的子組件出現報錯時候,會觸發getDerivedStateFromError調用,並攜帶錯誤信息
static getDerivedStateFromError(error){
  console.log('@@@',error);
  return {hasError:error}
}

componentDidCatch(){
  console.log('此處統計錯誤,反饋給服務器,用於通知編碼人員進行bug的解決');
}
複製代碼
相關文章
相關標籤/搜索