useContext & useReducer 单独使用与结合使用

useContext

在理解useContext之前 肯定会想到最简单的也最麻烦的props传值

useContext 和 props的共同与不同点

共同点:

1、如果需要在组件之间共享状态,可以使用useContext(),也可以使用props,props和useContext都可以实现组件之间的共享状态

不同点:

1、props存在的问题是props传输的值需要层层传输,即下层组件想要共享状态,必须是上层组件一个一个传下来的,不能跳级传。但是useContext,无论有多少层子组件,他们都可以直接获取最上层定义的值,所以下层组件要共享状态无需通过上传组件的传输。

他们的使用也要根据情况来区分,如果在只有一层或两层的少量层数组件通信时,采用props是比较简单的方法,而当有多层组件,且有写组件用不到这个状态时,写useContext是比较好的方法。可以免去一层层写props的麻烦。

说得再多还不如用代码演示一下让人理解更深刻。

props传的例子就不写了,这次着重展示useContext如何传递

//这个被称作父组件其实不太准确,因为其可能包了很多个子组件 子组件又包了很多个子孙组件 所以应该叫做最外层组件
const Father = () => {  
  //首先,在最外层组件(App)中创建 const AppContext = React.createContext({})
  const AppContext = React.createContext({});// 表示初始值是个对象
  return (
    //下层组件如果内部需要引用这个状态的话,需要使用const {userName} = userContext(AppContext),这样就可以简单的得到上层组件定义的状态(值)
    //此时最外层组件已经将username这个值传入成功了
    <AppContext.Provider value={
    
    {username:'我是最外层组件传来的数据'}}>
      <div>我是最外层组件组件</div>
      <Child1></Child1>
      <Child2></Child2>
    </AppContext.Provider>
  )
}

export default Father;

于是在Child1和Child2组件中获得userName的方式如下

扫描二维码关注公众号,回复: 15342601 查看本文章
const Child1 = () => {
  //useContext()钩子函数用来引入Context对象,从中获取username属性
  const {username} = useContext(AppContext)
  return (
    <>
      <div>{username}</div>
      
    </>
  )
}

const Child2 = () => {
  const {username} = useContext(AppContext)
  return (
    <>
      <div>{username}</div>
      <Child3></Child3>
    </>
  )
}

其中Child2中还嵌套了子组件Child3 如果是props传递 此时就要在子组件Child3上加入props 会比较麻烦,但是使用useContext就会十分方便 和Child1和Child2组件获得username的方式一样

const Child3 = () => {
  //useContext()钩子函数用来引入Context对象,从中获取username属性
  const {username} = useContext(AppContext)
  return (
    <>
      <div>{username}</div>
    </>
  )
}

总结: Child3子组件,或者更下层组件想要引用这个状态的话,之间加上 const {userName} = useContex(AppContex)即可获得。不需要父组件的传输 ,比props方便不少。

useReducer

首先,useReducer 是 useState 的替代方案,或是优化方案

const [state, dispatch] = useReducer(reducer, initialArg);

上面就是useReducer的定义方式

useReducer的参数有两个:

1、reducer函数

2、是初始化的state。返回值为最新的state和dispatch函数(用来触发reducer函数,计算对应的state)

为了更好的理解useReducer 我把useState转成useReducer

首先先用useState写一个demo 我从其他地方copy来的哈哈哈哈

//useState 计数器
function Counter({initialCount}) {
  const [count, setCount] = useState(initialCount);
  return (
    <>
      Count: {count}
      <button onClick={() => setCount(initialCount)}>Reset</button>
      <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
      <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
    </>
  );
}

如上所示 这是一个简单的加减复位的一个demo,接下来我们将它改写成useReducer

const initialState = {count: 0};
const reducer = (state, action) => {
  switch (action.try) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
};
//useReducer 计数器
const Counter ({initialCount}) => {
    const [count, setCount] = useReducer(reducer,initialCount) 
    return (
        Count: {count}
        <button onClick={() => setCount({try: 'increment'})}>+</button>
        <button onClick={() => setCount({try: 'decrement'})}>-</button>
    )
}

如上就是useReducer的写法,但是这个例子中 state只有一个 当state比较多的时候,采取es6的解构赋值就行

const reducer = (state, action) => {
  switch (action.try) {
    case 'increment':
//此时就是es6的解构赋值的应用
      return {...state, count: state.count + 1};
    case 'decrement':
      return {...state, count: state.count - 1};
    default:
      throw new Error();
  }
};

其实这里还涉及到了不可变数据,此时的state对象必须是immutable,具体的不可变数据我再写一章。

终于终于!!!! 王炸来了!!!!!

useState + useContext 结合实现类似redux的数据管理效果

在某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递 dispatch 而不是回调函数 。

首先 创建一个context对象

Context.tsx

import { createContext } from "react";
//定义了一个ReducerContext,这样任何组件需要使用只需要引入就可以了
export const ReducerContext = createContext(null);

index.tsx


import { useReducer } from 'react';
import ChildComponent from './ChildComponent';
import { ReducerContext } from './Context';
//定义useReducer的两个参数 addData 和 addCount
const addData = {
    count: 0,
}

const addCount = (state, action) => {
    switch (action.type) {
        case 'increment':
            return {
                ...state,
                count: state.count + 1,
            }
        case 'decrement':
            return {
                ...state,
                count: state.count - 1,
            }

        default:
            throw new Error();
    }
}

const index = () => {
    const [state, dispatch] = useReducer(addCount, addData);

    return (
        <div>
            <ReducerContext.Provider value={state,dispatch}>
                <ChildComponent />
            </ReducerContext.Provider>
        </div>
    )
}

export default index;

ChildComponent.tsx


import { memo } from "react";
import Child1 from './Child1';
import Child2 from './Child1';

const ChildComponent = () => {
    const state = useContext(ReducerContext)
    return (
        <>
            Count:{state.count}
            <Child1 />
            <Child1 />
        </>
    )
}
export default memo(ChildComponent);

Child1.tsx

import { useContext } from "react";
import { ReducerContext } from "./Context";

const Child1 = () => {
    const dispatch = useContext(ReducerContext)

    return (
        <>
            <button onClick={()=>dispatch({type:'increment'})}>+</button>
            <button onClick={()=>dispatch({type:'decrement'})}>-</button>
        </>
    )
}

export default Child1;

以上就是一套完整的useReducer和useContext结合使用的基础思路吧 有错误及时帮忙指正哈

看起来有点复杂累赘 但是是分情况使用的:

1、页面state很简单,可以直接使用useState,比较简洁

2、面state比较复杂(state是一个对象或者state非常多散落在各处)请使用userReducer,方便统一管理

3、页面组件层级比较深,并且需要子组件触发state的变化,可以考虑useReducer + useContext

猜你喜欢

转载自blog.csdn.net/hhhhhhaaaaaha/article/details/129175597
今日推荐