useReducer与useContext

158 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第25天,点击查看活动详情

什么是useReducer

用于状态管理的api,useState是使用useReducer构建的。

区别:useReducer管理公共状态,useState管理私有内容状态

const  [state,dispatch] =useReducer(reducer,initState,init惰性初始化) 

一共有三个参数,一个是reducer函数,它有两个参数(state,action),返回新的state。

initState是初始化state的状态

init是惰性初始化

情况1.

当该公共状态为简单数据类型时

let initState = 0

定义reducer函数

 function reducer(state,action){
      return state;
  }

定义UI组件:

点击increase/decrease按钮,派发action指令

 function App(){
   const [state,dispatch] = useReducer(reducer,initState)
   return (
       <div>
           <p>count: {state}</p>  
           <button onClick={()=>dispatch("increase")}>count++</button>  
           <button onClick={()=>dispatch("decrease")}>count--</button>  
       </div>
   )
}

在reducer中改变state,这里的state默认为useReducer第二个参数initState

 switch (action) {
      case "increase":
          return state+1
      case "decrease":
          return state-1
      default:
          break;
  }

情况2.

初始数据是个对象

let initState = {count:0}

定义reducer

 function reducer(state,action){
           return state;
}

定义UI组件

 function App(){
    const [state,dispatch] = useReducer(reducer,initState)
    return (
        <div>
    <p>count: {state.count}</p>  
    <button onClick={()=>dispatch({type:"increase"})}>count++</button>  
    <button onClick={()=>dispatch({type:"decrease"})}>count--</button>  
        <button onClick={()=>dispatch({type:"reset",payload:initState})}>count惰性初始化</button>  
         </div>
     )
  }

派发action对象

switch (action.type) {
      case "increase":
          return {count:state.count+1} //返回一个对象去覆盖
      case "decrease":
          return {count:state.count-1}
      case "reset":
          return {count:action.payload.count+5} //在初始数据的基础上+5
      default:
          break;
  }

注意,不可以使用return state.count = state.count+1的方式更改count数据

因为初始对象拥有一个地址,只改变里面的count是不能做到重新渲染该对象的,只能声明新的对象产生新的地址才能更改对象里的数据。这是钩子函数内部优化的机制。

使用useContext

在类组件中传统的父子间传值方式就是使用props,通过props接收到父组件传递过来的数据

image.png

从App父组件开始传递数据到子组件中,传统的props方法我们就不在此赘述。

直接使用useContext钩子函数

通过createContext方法返回Provider,Consumer两个组件

使用方法与context上下文相同,将需要传递数据的组件用Provider包裹起来,在接收数据的子组件中使用Consumer包裹并用对象返回值的形式接收。

单一数据

function App(){
           const [theme,setTheme] = useState("red")
           return(
            <div>
               <p>theme color : {theme}</p> 
               <button onClick={()=>{setTheme("green")}}>改变主题颜色</button>  
          //使用value传递数据
              <Provider value={theme}>
               <Child1  /> 
               <Child2 /> 
               </Provider>
            </div>
           )
         }

Child1子组件:

class Child1 extends Component{
            render(){
                return(
                //使用Consumer组件包裹
                 <Consumer>
                 //对象形式
                 {
                 //接收传递过来的value,拿到theme
                   (theme)=>{
                   //以返回值的形式,返回出带有theme的UI组件
                     return (
                         <div >
                          <p>theme1 color :{theme}</p> //可以接收到了
                          <Child3 /> 
                          <Child4  /> 
                        </div> 
                        )
                   }
                 }        
                 </Consumer>
                )
            }
        }

Child3子组件:

class Child3 extends Component{
            render(){
                return(
                //使用consumer包裹
                 <Consumer>
                 {
                 //接收theme
                   (theme)=>{
                     return (
                         <div >
                            <p>theme1-3 color :{theme}</p>
                        </div> 
                        )
                   }
                 }        
                 </Consumer>
                )
            }
        }

这是使用provider传递一个值的情况,如果使用provider传递多个值又是怎样呢。

多个数据

那就用createContext创建两个provider,consumer

 const ColorContext = createContext("grey")
 const FontContext = createContext("粗体")

使用方法:

同样,在想要传递值的组件外包裹这两个Provider组件

 <ColorContext.Provider value={theme}> //传递theme
     <FontContext.Provider value={font}> //传递字体
         <Child1 />
         <Child2 />
     </FontContext.Provider>
 </ColorContext.Provider>

在两个子组件中使用Consumer接收数据:

以Child1为例

 class Child1 extends Component {
 render() {
     return (
         <ColorContext.Consumer> //接收theme数据
             {//要包裹住,因为里面在执行函数
                 (theme) => {
                     return (
                         <FontContext.Consumer> //接收font数据
                             { //要包裹住,因为里面在执行函数
                              (font) => {
                                  return(
                                  <div >
                                     <p>theme1 color :{theme}</p>
                                     <p>theme1 font :{font}</p>
                                     <Child3  />
                                     <Child4  />
                                  </div>
                                  )
                                }
                             }
                         </FontContext.Consumer>
                     )
                 }
             }
         </ColorContext.Consumer>
     )
 }
}

结合react-context

useContext使用方法可以说是和context使用方法基本相同,我之前也有些过context上下文的使用,大家可以一看,奉上链接:react-context上下文