实现useReducer
On this page
大家好,我是万维读客的讲师曹欢欢。上节课我们增加了useState功能,这节我们在此基础之上,增加一个useReducer的功能,作为useState的包装能力。
useReducer介绍
useReducer 是一个 React Hook,它允许你向组件里面添加一个用于管理状态的 reducer, 类似简化版的redux。具体可以参考官方useReducer文档:https://zh-hans.react.dev/reference/react/useReducer,下面给个例子:
function reducer(state, action){
switch(action.type){
case 'add': return state + 1;
case 'sub': return state - 1;
default: return state;
}
}
function App() {
const [count, dispatch] = Treact.useReducer(reducer, 10);
const handleClick = (type) => {
dispatch({type})
}
return (<div>
<div>{count}</div>
<span onClick={()=>handleClick('add')}>+</span>
<span onClick={()=>handleClick('sub')}>-</span>
</div>
);
}
编写测试用例
我们在上一节的基础上,增加测试用例如下:
describe('fiber useReducer test', () => {
it('render useReducer', async () => {
const globalObj = {};
function reducer(state, action){
switch(action.type){
case 'add': return state + 1;
case 'sub': return state - 1;
default: return state;
}
}
function App() {
const [count, dispatch] = Treact.useReducer(reducer, 10);
globalObj.count = count;
globalObj.dispatch = dispatch;
return (
<div>{count}</div>
);
}
const container = document.createElement('div');
const root = Treact.createRoot(container);
await Treact.act(() => {
root.render(<App />);
expect(container.innerHTML).toBe('');
});
await Treact.act(() => {
globalObj.dispatch({type: 'add'});
});
console.log('globalObj.count', globalObj.count);
expect(globalObj.count).toBe(11);
await Treact.act(() => {
globalObj.dispatch({type: 'sub'});
});
console.log('globalObj.count', globalObj.count);
expect(globalObj.count).toBe(10);
});
})
然后我们看控制台提示测试用例报错,找不到useReducer方法,下面我们就来补全这个方法。
实现useReducer
我们增加useReducer函数,基于我们上一节实现的useState,其实这里就比较简单了。代码如下:
export function useReducer(reduce, initialState) {
const [state, setState] = useState(initialState);
const dispatch = (action) => {
setState(state => reduce(state, action))
}
return [state, dispatch]
}
检查控制台测试用例执行通过,代码功能完成。
stdout | treact05/jsx.test.jsx > fiber useReducer test > render useReducer
globalObj.count 11
stdout | treact05/jsx.test.jsx > fiber useReducer test > render useReducer
globalObj.count 10
✓ treact05/jsx.test.jsx (3)
✓ async render Function Component (1)
✓ render with act
✓ fiber useState test (1)
✓ render useState
✓ fiber useReducer test (1)
✓ render useReducer
Test Files 1 passed (1)
Tests 3 passed (3)
Start at 14:25:25
Duration 40ms