react hook 筆記(一)
1.useState,setState
區別:useState不會自動合併更新物件,想要合併跟新物件可以使用函式式結合運算子:
setState(prevState => { // 也可以使用 Object.assign return {...prevState, ...updatedValues}; });
useState 初始state的時候可以是任意的資料型別,setState初始state的時候state只能是物件;setState改變某個state的後,會將之前其他的state和改變後的這個state合併更新。
this.setState(((prevState, props)=>{return { count: prevState.count+1 } })) 或 this.setState({count: this.state.count+1})
相同:setState,useState拿到最新的state後,重新渲染頁面,頁面展示最新的資料狀態。
(“監聽”資料變化,然後重新渲染頁面,這裡應該用到了觀察者模式)import{useImmer}from"use-immer"; useImmer這個hook和useState功能差不多,但更接近 setStae, 如果你的state是一個物件的話,用useImmer更方便,可以合併更新物件。他也返回一個state和更新state的方法,只是這個方法接收一個函式,該函式的引數是當前的state,可以對state直接操作,併合並更新該state。而不像setStae使用函式式結合運算子。
// 以下程式碼塊純屬自己亂寫,沒有看原始碼是怎麼寫的。等有時間去學習一下原始碼,先隨便寫一點自己的理解
function useState (state, setState) { const listeners = [] const subscribe= (listener) => listeners.push(listener) const getState = () => state const setState = (state)=>{ listeners.forEach((listener) => listener()) return state } return [getState, setState , subscribe ] } function setState(newState){ return newState } const state= useState(state, setState) state.subscribe(()=> renderApp(state.getState())) // 監聽資料變化 renderApp(state.getState()) // 首次渲染頁面 state.useState(newSTate) // n次渲染
const [data, setData] = useImmer([{key:'account',value:1}]) setData(draft=>{ const obj = {key:'pwd',value:1} draft.splice(1,0,obj) }) const [data, setData] = useState([{key:'account',value:1}]) setData(preState=>{ const obj = {key:'pwd',value:1} return [obj, ...preState] }) data // [{key:'account',value:1},{key:'pwd',value:1}]
2.useRef
函式元件不同於class元件,class元件對不用於頁面渲染的引數,可以用this機制儲存起來。如:用來獲取介面的引數
this.fetchParms ={pageIndex:1, pageSize:30} const tableData = { rowKey: (r, i) => `dayReport${i.toString()}`, dataSource: tableList.records, columns: nextColumns, pagination: { current: tableList.current, total: tableList.total, pageSize: 30, size: 'small', showTotal: (total) => `共 ${total} 條資料`, onChange: (pageIndex, pageSize) => { this.fetchParms ={pageIndex, pageSize} fetchList(dailyWorkReportListApi); }, }, loading: { spinning, tip: '載入中...' }, };
函式元件 設定初始值constfetchParms ={pageIndex:1, pageSize:30} ,後續在上面onChange函式中fetchParms.pageIndex =pageIndex;fetchParms.pageSize =pageSize修改。若元件重新渲染(useState等)後,fetchParms 還是拿到的初始值。這時候設定初始值的時候需要用到useRef,不會出現這種現象。
const fetchParms = useRef({}) fetchParms.current = {pageIndex:1, pageSize:30} onChange函式中 fetchParms.current = {pageIndex, pageSize} fetchList(xxApi){ xxApi(fetchParms.current).then(res=>{ useState(res.result || {}) // 初始為{}物件的目的是為了防止遇到如20001等狀態的的時候,後端不返回result健而報錯 }) }
3.useEffect
useEffect(()=>{},[deps]),執行副作用操作,如果依賴項是[],和componentDidMount作用一樣,如果返回一個函式,防止記憶體洩漏,清除函式會在元件解除安裝前執行。另外,如果元件多次渲染(通常如此),則在執行下一個 effect 之前,上一個 effect 就已被清除。如果依賴項是[]則和
componentWillUnmount作用一樣,清除函式會在元件解除安裝前執行。 4.useMemoconst memoizedValue = useMemo(() => { ...執行某些操作 return value; },[deps]);
依賴deps不發生改變,就會保留之前的memoizedValue的值,
把“建立”函式和依賴項陣列作為引數傳入useMemo
,它僅會在某個依賴項改變時才重新計算 memoizedValue 值。這種優化有助於避免在每次渲染時都進行高開銷的計算。
記住,傳入useMemo
的函式會在渲染期間執行。請不要在這個函式內部執行與渲染無關的操作,諸如副作用這類的操作屬於useEffect
的適用範疇,而不是useMemo
。
5.useCallback
const showDetail= useCallback( (value) => { ...執行某些操作 }, [deps], ); const columns = [{ title: '新增候選人總量', dataIndex: 'candidateTotal', width: 130, align: 'center', render: (text, record) => <a onClick={() => showDetail(12, record)}>{text}</a>, }],
返回一個memoized回撥函式。
把內聯回撥函式及依賴項陣列作為引數傳入useCallback
,它將返回該回調函式的 memoized 版本,該回調函式僅在某個依賴項改變時才會更新。當你把回撥函式傳遞給經過優化的並使用引用相等性去避免非必要渲染(例如shouldComponentUpdate
)的子元件時,它將非常有用。
7.useContext
const MyContext = createContext({})
useContext 等同於static contextType = MyContext或 <MyContext.Consumer>{value=>{}}</MyContext.Consumer>。它的值仍然取的是離他最近的<MyContext.Provider value={}>中value的值