一、useContetxt
react父组件传值给子组件,可以直接在组件上传值。如果有多个子组件或者还带有孙子组件,那么普通传值就会很繁琐,useContext能够实现组件状态的穿透
- 父组件创建一个 context 状态,我这里用 ts 给了个类型,没用到 ts 可以忽略
import { createContext, useState, useContext } from 'react';
type ContextType = {
count: number
};
const ConfigContext = createContext({} as ContextType);
- 创建俩内部的子组件和一个孙子组件,然后用创建的 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>
);
};
- 外部创建的子组件使用父组件的值
需要把父组件创建的 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 而不是回调函数 。
- 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;
- 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;