react之useContext、useReducer

一、useContetxt

react父组件传值给子组件,可以直接在组件上传值。如果有多个子组件或者还带有孙子组件,那么普通传值就会很繁琐,useContext能够实现组件状态的穿透

  1. 父组件创建一个 context 状态,我这里用 ts 给了个类型,没用到 ts 可以忽略
import { createContext, useState, useContext } from 'react';

type ContextType = {
  count: number
};
const ConfigContext = createContext({} as ContextType); 
  1. 创建俩内部的子组件和一个孙子组件,然后用创建的 ConfigContext 容器包裹
    ConfigContext.Provider 有个 value 属性就是你要传的值
    子组件使用 useContext 获取父组件传的值
// 孙子组件
const Son = () => {
  const { count } = useContext(ConfigContext); // 这里引入父组件的值
  return (
    <div>孙子组件: {count}</div>
  );
};
// 子组件
const Child = () => {
  const { count } = useContext(ConfigContext); // 这里引入父组件的值
  return (
    <div>
      子组件: {count}
      <Son />  // 孙子组件
    </div>
  );
};
const Second = () => {
  const { count } = useContext(ConfigContext);
  return (
    <div>子组件: {count}</div>
  );
};

// 父组件
const ContextDemo = () => {
  const [count, setCount] = useState<number>(0);
  return (
    <div>
      父组件:
      <ConfigContext.Provider value={{
        count: count
      }}>
        <Button onClick={() => setCount(count + 1)}>加1</Button>
        <Second />
        <Child />
      </ConfigContext.Provider>
    </div>
  );
};
  1. 外部创建的子组件使用父组件的值
    需要把父组件创建的 context 状态 export 出去
    然后子组件内引用
// 父组件内
export const ConfigContext = createContext({} as ContextType);  // export出去

// 外部子组件内
import { useContext } from 'react';
import { ConfigContext } from './index';

// 然后使用
const { count } = useContext(ConfigContext);

二、useReducer

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

  1. useReducer接受两个参数:reducer 和 state
    reduce 函数,有两个参数 state 状态和 action 触发类型和更新值
import {  useReducer } from 'react';
import { Button } from 'antd';

type StateType = {
  count: number,
  name: string,
  age: number
}
type ActionType = {
  type: 'add' | 'desc' | 'editName',
  payload?: any
}

// 状态
const states: StateType = {
  count: 1,
  name: '张三',
  age: 25
};

// 状态处理通知函数
const reducer: React.Reducer<StateType, ActionType> = (state, action) => {
  const { type, payload } = action;
  
  switch (type) {
    case 'add': 
      return { ...state, count: state.count + 1 };
    case 'desc': 
      return { ...state, count: state.count - 1 };
    case 'editName':
      return { ...state, name: payload};
  
    default:
      return state;
  }
};

const ReducerDemo = () => {

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

  return (
    <div>
      <p>{ state.count }</p>
      <p>{ state.name }</p>
      <Button onClick={() => dispatch({ type: 'add' })}>加</Button>
      <Button onClick={() => dispatch({ type: 'desc' })}>减</Button>
      <Button onClick={() => dispatch({ type: 'editName', payload: '李四' })}>改名</Button>
      <Child />
    </div>
  );
};

export default ReducerDemo;
  1. state初始值需要计算或者需要引用其他值,不推荐 state = initialState

React 不使用 state = initialState 这一由 Redux 推广开来的参数约定。有时候初始值依赖于 props,因此需要在调用 Hook 时指定。如果你特别喜欢上述的参数约定,可以通过调用 useReducer(reducer, undefined, reducer) 来模拟 Redux 的行为,但我们不鼓励你这么做。

  • useReducer可以接受三个参数 reducer(reducer, initState, initFn),改版如下
// 状态
// const states: StateType = {
//   count: 1,
//   name: '大龙帅',
//   age: 25
// };
const initFn = (state: StateType): StateType => {
  state.name = '王二';
  return state;
};

 const initState: StateType = {
   count: 0,
   name: '张三',
   age: 25
 };
 const [ state, dispatch ] = useReducer(reducer, initState, initFn);

useContext搭配useReducer实现组件状态共享

import React, { useReducer, useContext, createContext } from 'react';
import { Button } from 'antd';

// useReducer
interface StateType {
  count: number
}
type ActionType = {
  type: 'add' | 'desc',
  payload?: any
}
const states: StateType = {
  count: 0
};
const reducer: React.Reducer<StateType, ActionType> = (state, action) => {
  const { type } = action;

  switch (type) {
    case 'add':
      return { ...state, count: state.count + 1 };
    case 'desc':
      return { ...state, count: state.count - 1 };
  
    default:
      return state;
  }
};

// useContext
type ContextType = {
  state: StateType,
  dispatch: React.Dispatch<ActionType>
}
const ConfigContext = createContext({} as ContextType);

// 子组件
const Child = () => {
  const { state, dispatch } = useContext(ConfigContext);

  return (
    <div style={{ marginTop: 20 }}>
      <p>子组件:{ state.count }</p>
      <Button onClick={() => dispatch({ type: 'add' })}>加</Button>
      <Button onClick={() => dispatch({ type: 'desc' })}>减</Button>
    </div>
  );
};

// 父组件
const Test = () => {
  
  const [state, dispatch] = useReducer(reducer, states);

  return (
    <div>
      <p>父组件: { state.count }</p>
      <Button onClick={() => dispatch({ type: 'add' })}>加</Button>
      <Button onClick={() => dispatch({ type: 'desc' })}>减</Button>

      <ConfigContext.Provider value={{
        state,
        dispatch
      }}>
        <Child />
      </ConfigContext.Provider>
    </div>
  );
};

export default Test;
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值