useReducer + useContext 之 基础学习

1 篇文章 0 订阅

useReducer + useContext ≈ Redux

useContext

目的

context的中文解释是 上下文(context),useContext 就是为了解决跨组建偷传值的问题。
createContext 能够创建一个 React 的上下文(context),然后订阅了这个上下文的组件中,可以拿到上下文中提供的数据或者其他信息。

使用

1、用createContext 把值包裹起来,传给’themeContext’
2、通过父组件’themeContext.provide’传递给子组件,
<themeContext.provide value={xxx}>

</themeContext.provide>
不管多少个
3、通过useContext来使用
const theme = useContext(themeContext);
就可以取到theme.xxx 的值了

const themes = {
  light: {
    foreground: "#000000",
    background: "#eeeeee"
  },
  dark: {
    foreground: "#ffffff",
    background: "#222222"
  }
};

const ThemeContext = React.createContext(themes.light);

function App() {
  return (
    <ThemeContext.Provider value={themes.dark}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return (
    <button style={{ background: theme.background, color: theme.foreground }}>
      I am styled by theme context!
    </button>
  );
}

useReducer 的使用

目的

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

拓展一下:
useReducer 理念和 redux 类似: (state, action) => newState的reducer,并返回当前的 state 以及与其配套的 dispatch 方法

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

使用

1、定义 :

const [state, dispatch] = useReducer(reducer, initialState)

state - 要用的数据
dispatch - dispatch 函数,可以理解为一个稳定标识的方法
reducer - reducer函数
initialState - 初始数据

2、定义 reducer 函数

function reducer(state,action){
    switch (action.type){
        case 'increment';
            return  {count :state.count + 1};
        case 'decrement'
            return {count :state.count - 1 };
        default :
            throw new Error();
    }   
}

3、用

const initialState = {count: 0};

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
        **  
        <button onClick={() => dispatch({type: 'decrement'})}>-</button>
        <button onClick={() => dispatch({type: 'increment'})}>+</button>
        **
    </>
  );
}

在定义的时候 ,可以直接将initialState传进去咯

const [state, dispatch] = useReducer(reducer, {count: 0});
第三个参数 init 初始化

你可以选择惰性地创建初始 state。为此,需要将 init 函数作为 useReducer 的第三个参数传入,这样初始 state 将被设置为 init(initialArg)。

理解:init 是一个讲值初始化的方法

function init(initialCount) {
  return {count: initialCount};
}

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    case 'reset':
      return init(action.payload);
    default:
      throw new Error();
  }
}

function Counter({initialCount}) {
  const [state, dispatch] = useReducer(reducer, initialCount, init);
  return (
    <>
      Count: {state.count}
      <button
        onClick={() => dispatch({type: 'reset', payload: initialCount})}>
        Reset
      </button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
    </>
  );
}

这里 action.type = reset 的是
调用action.payload 即 init方法

跳过dispatch的情况:

官方解读是:

如果 Reducer Hook 的返回值与当前 state 相同,React 将跳过子组件的渲染及副作用的执行。(React 使用 Object.is 比较算法 来比较 state。)
需要注意的是,React 可能仍需要在跳过渲染前再次渲染该组件。不过由于 React 不会对组件树的“深层”节点进行不必要的渲染,所以大可不必担心。如果你在渲染期间执行了高开销的计算,则可以使用 useMemo 来进行优化。

这个主要场景还是渲染优化,能对基本的渲染减少一点开销。

useReducer + useContext

各路说法都说useReducer + useContext 要一起用,为什么要一起用呢? 比如这里,如果需要传递方法:
<MyContext.Provider value={{ setStep, setCount, setNumber, fetchData }} />
是不是Context Provider 提供的方法越来越多,越来约臃肿。

那通过useReducer包装,通过dispatch 触发的,就能大大简化。

import React, { useReducer } from 'react';
import Child from './Child';
import { MyContext } from './context-manager';

const initState = { count: 0, step: 0, number: 0 };

const reducer = (state, action) => {
    switch (action.type) {
        case 'stepInc': return Object.assign({}, state, { step: state.step + 1 });
        case 'numberInc': return Object.assign({}, state, { number: state.number + 1 });
        case 'count': return Object.assign({}, state, { count: state.step + state.number });
        default: return state;
    }
}

export default (props = {}) => {
    const [state, dispatch] = useReducer(reducer, initState);
    const { step, number, count } = state;

    return (
        <MyContext.Provider value={{ dispatch }}>
            <Child step={step} number={number} count={count} />
        </MyContext.Provider>
    );
}

因此此时子组件只需要拿到 dispatch 即可修改父组件的 state。

import React, { useContext, memo } from 'react';
import { MyContext } from './context-manager';

export default memo((props = {}) => {
    const { dispatch } = useContext(MyContext);

    return (
        <div>
            <p>step is : {props.step}</p>
            <p>number is : {props.number}</p>
            <p>count is : {props.count}</p>
            <hr />
            <div>
                <button onClick={() => { dispatch({ type: 'stepInc' }) }}>step ++</button>
                <button onClick={() => { dispatch({ type: 'numberInc' }) }}>number ++</button>
                <button onClick={() => { dispatch({ type: 'count' }) }}>number + step</button>
            </div>
        </div>
    );
});

参考文档:
http://www.ptbird.cn/react-createContex-useContext.html

Reducer - redux Reducer

小小地拓展一下redux,对理解useReducer有帮助,阅读材料:https://www.redux.org.cn/docs/recipes/reducers/NormalizingStateShape.html

基础概念:

(previousState, action) => newState

reducer的基本结构:
一个应用应该只有一个单一的reducer函数(理论上)
reducer第一次被调用的时候,state的是值需要传入一个默认值
reducer需要根据之前的state 和 dispatch 的action来决定做什么 (纯函数概念)
没有改变,需要返回当前的state

关于数据
大部分应用的数据类型分为三类:
1、需要修改的展示的 或者使用的数据 —域数据(domain data)
2、特定于应用某个行为的数据 — 应用状态(App state)
3、控制UI展示的 — UI状态(UI state)

回到之前说的,理论上一个应用应该有一个单一的reducer 是不可能(维护)的,Redux reducer 也仅仅是一个函数,所以将reducer中的一些逻辑拆分出去,然后在父函数中调用这个新函数。

reducer: 任何符合(prestate,action )-> newState 格式的函数
root reducer: 顾名思义
通常作为 createStore 第一个参数的函数。他是唯一的一个在所有的 reducer 函数中必须符合 (state, action) -> newState 格式的函数。
slice reducer: 一个负责处理状态树中一块切片数据的函数,通常会作为 combineReducers 函数的参数。
case function: 一个负责处理特殊 action 的更新逻辑的函数。可能就是一个 reducer 函数,也可能需要其他参数才能正常工作。
higher-order reducer: 一个以 reducer 函数作为参数,且/或返回一个新的 reducer 函数的函数(比如: combineReducers, redux-undo)。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值