react 第三节

87 阅读3分钟

React 状态管理:redux recoll jotal mobx zustand

React State

React 为我们提供了创建状态与更新状态的Api -- useState

const [count, setCount] = useState(0)
//count:变量 变更可驱动视图更新实现组件的渲染  setCount更新count数据
setCount(count+1)  等同于  setCount(c => c+1 )
组件中使用: {count}
就近取值:

class function  state值修改 两种不同的写法

import React ,{useState} from "react";
class App extends React.Component {  
    constructor (props) {    
        super(props);    
        // const [count, setCount] = useState(0)    
        this.state = {      count:0    }  }  
        render () {    
            return (    
            <div>      
                <p>Count:{this.state.count}</p>      
                <button onClick={()=>this.setState({count:this.state.count+1})}>点击</button>
            </div>    
                )  
    }}

import React, { useState } from 'react';
function App() {  
    // Declare a new state variable, which we'll call "count"  
    const [count, setCount] = useState(0);  
    return (    
    <div>      
        <p>You clicked {count} times</p>      
        <button onClick={() => setCount(count + 1)}> Click me </button>    
   </div>  );}
export default App;

useReducer

import React, { useReducer } from 'react';function App() {  // Declare a new state variable, which we'll call "count"  function reducer(state,action){    switch(action.type){      case 'incremented_age': {          return {            ...state,            age: state.age+2                }      }      case "hobby_change":{        return {          ...state,          hobby:action.payload        }      }    }      }  const [state, dispatch] = useReducer(reducer,{age:23,hobby:'象棋'})  return (    <div>      <p>You clicked {state.age} times</p>      <button onClick={() => dispatch({type:'incremented_age'})}>        Click me      </button>      <p> {state.hobby} </p>      <button onClick={() => dispatch({type:'hobby_change',payload:'跳棋'})}>        Click me      </button>    </div>  );}export default App;

useContext

方式1 
// pageContext page
import React from 'react'
export const PageContext = React.createContext({ //创建句柄
    title:"Page title"
})

import { useContext } from "react";
import { PageContext } from "./PageContext";

export const PageTitle = () => {
  const { title } = useContext(PageContext); //获取句柄传递的数据
  return (
    <div>
      {/* <PageContext.Consumer>{({ title }) => title}</PageContext.Consumer> */}
      {title}
    </div>
  );
};
父 数据提供者
<PageContext.Provider value = {{title:"Page title"}}>
    <PageTitle />
<PageContext.Provider />
后代使用者
<PageContext.Consumer>
    {({title}) =>{title}}
<PageContext.Consumer/>

 面试官会问:Context 有什么缺点,怎么改进?

Context 因为是整个大对象,只要数据变更,以下所有内容有可能会需要 rerender。

  1. 细化 context value,拆分,PageContext、WorkSpaceContext

  2. 通过定义 xxxProvider,将数据更新局限在 children 层,不再是 PageContext.Provider,而是 PageProvider

    export const FormProvider = < TFieldValues extends FieldValues, TContext = any, TTransformedValues extends FieldValues | undefined = undefined,

    ( props: FormProviderProps<TFieldValues, TContext, TTransformedValues>, ) => { const { children, ...data } = props; return ( <HookFormContext.Provider value={data as unknown as UseFormReturn}> {children} </HookFormContext.Provider> ); }; 原来的写法会影响到同级节点,同级节点一个变化,父级组件会rerender ,所以现在套了个壳子, 不存在同级节点,所以影响rerender

Redux

  1. 单一数据流
  2. state只读
  3. 使用纯函数执行状态修改

单向数据流:reducer ->产出state ->更新操作 view视图 ->触发actions  -> 影响 Dispatcher ->修改Reducer 中state ->  影响view层

any : 允许赋值为任意类型,可访问任意属性和方法

void : 表示没有任何类型返回

redux 源码解析

源码部分: createStore 包含 getState dispatch subscribe 方法

function createStore(reducer,enhancer) {     if (typeof enhancer !== 'undefined') {        if (typeof enhancer !== 'function') {          throw new Error('Expected the enhancer to be a function.');        }            return enhancer(createStore)(reducer);      }    let state;    const nextListeners = [];       const getState = ()=>{        return state;    }    const dispatch = (action)=>{        state = reducer(state,action);        for(const listener of nextListeners){            listener()        }    }    const subscribe = (listener)=>{        nextListeners.push(listener);        //订阅函数的返回值一般都是Unsubscribe        return ()=>{            const index = nextListeners.indexOf(listener);            nextListeners.splice(index,1);        }    }      return {      subscribe,      getState,      dispatch    }  }  export default createStore
combineReducer 和 applyMiddleware

这两个方法用于增强 redux 能力,比如我们一个状态仓库可能存储了多个模块的状态,这些模块的 reducer 相对分散,这时我们想有一个方法合并这些分散 reducer,实现比较简单,运用函数柯里化,定义一个 stateMap,然后依次调用对应 reducer 并将得到的值写入 stateMap 中,完成更新。

combineReducer部分 :将合并的reducer 拆解成一个一个的然后在合并
export default function combineredux(reducers) {        //  获取到所有 reducer key    const reducerKeys = Object.keys(reducers);    // 返回新的 reducer 函数    return function combination(      state={}, action    ) {            //  定义新的 state,该 state 用来存储合并之后的所有模块 state      const nextState = {};               // 遍历所有 reducer      for (let i = 0; i < reducerKeys.length; i++) {        const key = reducerKeys[i];        const reducer = reducers[key];        // 先获取到之前 state 的值,这个是 createStore 中存储的值,该值是一个 state tree,包括了所有模块 state        const previousStateForKey = state[key];        // 执行 reducer,出入的 state 参数正式上次存储的 state,调用后会获得计算后新的 state        const nextStateForKey = reducer(previousStateForKey, action);        // 将对应模块 state 挂到 nextState 对象上,且以 key 为键        nextState[key] = nextStateForKey;      }        /**       *       * 返回合并后的 state       * */      return nextState;    };  }
 利用函数柯里化实现函数的中间件
import React ,{useSyncExternalStore}from 'react';import createStore from './redux/redux';import combineredux from './redux/combineredux';const personReducer = (state = { name: "heyi", age: 0 }, action) => {  switch (action.type) {    case "incremented_age": {      return {        ...state,        age: state.age + 1,      };    }    default:      return state  }   };const PageReducer = (state = { title: "home" }, action) => {  switch (action.type) {    case "change_title": {      return {        ...state,        title: action.payload,      };    }    default:      return state  }};// 函数柯里化function compose(...funcs) {  return funcs.reduce((a, b) => (...args) => a(b(...args)));}function applyMiddleware(...middlewares) {  return (createStore) => (    reducer  ) => {    const store = createStore(reducer);    let dispatch;    const middlewareAPI = {      getState: store.getState,      dispatch: (action, ...args) => dispatch(action, ...args),    };    const chain = middlewares.map(middleware => middleware(middlewareAPI));    dispatch = compose(...chain)(store.dispatch);    return {      ...store,      dispatch,    };  };}const store =createStore(  combineredux({ person: personReducer, page: PageReducer })  );store.dispatch({type:"incremented_age"})store.dispatch({type:"change_title",payload:'haha'})console.log(store.getState())function App() { //当下外部状态和react 状态对接,快照处理const state = useSyncExternalStore(store.subscribe,store.getState)  return (    <div>      {/* <button onClick={() => store.dispatch({ type: "incremented_age" })}>        Click me{state.person.age}      </button> */}      <button onClick={() => store.dispatch({ type: "change_title" ,payload:'haley'})}>        Click me{state.page.title}      </button>    </div>  );}export default App;

react 状态管理库

redux  mobx  recoil   zustand  valtio  jotal  hox