hook的出现使得函数组件中有了自己的状态可以维护,但是组件之间的传参还是有点复杂,这里我们使用useContext和useReducer这两个钩子来实现组件之前的通信,并且可以管理全局状态
目录结构
提供全局数据(context.js)
给provider注入的value属性包括
- state: 全局状态
- dispatch: 事件触发器
- getValue: 根据key获取state的函数
- setValue: 根据key设置state的函数
import React, { useReducer } from 'react';
import {initState, reducer} from './reducer';
export const Context = React.createContext({});
export function ConfigContext({children}) {
const [state, dispatch] = useReducer(reducer, initState);
const getValue = (key) => state[key];
const setValue = (key, value ) => {
return dispatch({type: 'SET_VALUE', key, value });
}
let ctx = {
state,
dispatch,
getValue,
setValue,
};
return <Context.Provider value={ctx} >{children}</Context.Provider>
}
状态处理函数(reducer.js)
提供一个初始状态,并返回最新的状态;
// 初始状态
export const initState = {
count: 0,
};
export function reducer(state,{type, ...payload}){
switch(type) {
case 'SET_VALUE':{
return {
...state,
[payload.key]:[payload.value],
};
}
default: {
return state;
}
}
}
组件使用(设置状态值)
import React, { Fragment, useContext } from 'react';
import {Context} from '../context';
export default function Count() {
const {state:{count}, setValue} = useContext(Context);
return (
<Fragment>
<button onClick={() => setValue("count", +count+1)}> + 1 </button>
<button onClick={() => setValue("count", +count-1)}> - 1 </button>
</Fragment>
);
}
组件使用(获取状态值)
import React, { Fragment, useContext } from 'react';
import {Context} from '../context';
export default function Show() {
const {state:{count}} = useContext(Context);
return (
<Fragment>
<div>{`现在的数字是${count}`}</div>
</Fragment>
);
}
index.js
import React from 'react';
import {ConfigContext} from './context';
import Count from './components/count';
import Show from './components/show';
export default function Page() {
return <ConfigContext>
<Count/>
<Show/>
</ConfigContext>
}
演示
注意事项
到这里就可以实现全局状态的管控了,但是需要注意的是,只要引用了Context的组件,不管你依赖的数据是否发生变化,只要state发生变化,这个组件就会从新渲染,如果数据量比较大的话,渲染性能比较差。优化的话可以使用memo等方法进行优化;
优化前
import React, { Fragment, useContext } from 'react';
import {Context} from '../context';
export default function Test() {
const {state:{id}} = useContext(Context);
return (
<Fragment>
<div>我的id没有发生变化{id}</div>
</Fragment>
);
}
优化后
import React, { Fragment, useContext } from 'react';
import {Context} from '../context';
const Sub = React.memo(props => {
console.log('我被渲染了', props)
return (
<div>现在的id是{props.id}</div>
);
});
export default function Test() {
const {state:{id}} = useContext(Context);
return (
<Sub id={id}/>
);
}