让我们看一下使用useReducer
钩子进行状态管理的以下两种方法,它们都具有相同的作用:单击添加按钮到+ 1,然后单击减去按钮到-1:
const myReducer = (state, action) => {
switch (action.type) {
case 'add':
return {
count: state.count + 1
}
case 'subtract':
return {
count: state.count - 1
}
default:
return state
}
}
const Reducer = () => {
const [state, dispatch] = useReducer(myReducer, { count: 0 });
return (
<>
<button onClick={() => dispatch({ type: 'add' })}>Add</button>
<button onClick={() => dispatch({ type: 'subtract' })}>Subtract</button>
<p>{state.count}</p>
</>
)
}
const Reducer2 = () => {
const [state, setState] = useReducer(
(state, newState) => ({ ...state, ...newState }),
{ count: 0 }
);
return (
<>
<button onClick={() => setState({count: state.count + 1})}>Add</button>
<button onClick={() => setState({count: state.count - 1})}>Subtract</button>
<p>{state.count}</p>
</>
)
}
哪个是管理国家的更好方法?我更喜欢2,因为它更简单,它允许我们以“类组件”的方式管理状态。我不明白为什么需要1:它需要一个switch语句,它很复杂;如果要添加状态,则需要一种新的情况。这一切看起来都很麻烦。
编辑:我知道这是一个简单的示例,不需要使用useReducer
,而useState
更好,但是我真正想讨论的是,当存在多个状态时,哪个是更好吗?
答案 0 :(得分:1)
Switch语句通常在useReducer
中用作redux中的reducer的残余。
第二个示例是在函数组件中使用近似this.setState
的好方法,因为useState
仅为单个值而设计,因为没有旧状态和新的。在此答案的结尾,我将其扩展了一步。
关于哪个问题最适合管理useReducer
中的状态,这实际上取决于您要使用它的方式以及使用方式。您不仅限于这两种类型的事物:您可以在其中使用任何东西。我很幸运在useReducer
中使用redux toolkit的createSlice作为Immer的类型安全的简化器,以简化不可变性。
我不明白为什么需要1:它需要一个switch语句,该语句 很复杂如果要添加状态,则需要一个新案例
如果为状态的每个部分编写一个减速器用例,则可以。这非常麻烦,我肯定会以其他方式来做。使用第一种方法的最佳方法是,当您需要处理更复杂的情况时,或者使用通用方法来处理更多状态选项时,该方法是最好的。
如React docs中所述:
当您遇到复杂的情况时,useReducer通常比useState更可取 涉及多个子值或下一个状态的状态逻辑 取决于前一个。 useReducer还可以让您优化 触发深层更新的组件的性能,因为您可以 向下传递调度而不是回调。
它们是功能组件的非常功能强大的补充,允许使用更简单的方法来处理复杂的逻辑或逻辑连接的值。当然,是否使用它完全取决于您,useReducer
做的任何事情都可以用useState
来完成,这些const { useRef, useReducer } = React;
const dataReducer = (state, action) => {
switch (action.type) {
case 'toggle':
return {
...state,
[action.name]: !state[action.name],
};
case 'change':
return {
...state,
[action.name]: action.value,
};
default:
return state;
}
};
function Example() {
const [data, dispatch] = useReducer(dataReducer, {
check1: false,
check2: false,
check3: false,
input1: '',
input2: '',
input3: '',
});
const throwErrorRef = useRef(null);
const handleChange = function (e) {
const { name, value } = e.currentTarget;
dispatch({ type: 'change', name, value });
};
const handleToggle = function (e) {
const { name } = e.currentTarget;
dispatch({ type: 'toggle', name });
};
const checkBoxes = ['check1', 'check2', 'check3'];
const inputs = ['input1', 'input2', 'input3'];
return (
<div>
{checkBoxes.map((name) => (
<label>
{name}
<input
type="checkbox"
name={name}
onChange={handleToggle}
checked={data[name]}
/>
</label>
))}
<br />
{inputs.map((name) => (
<label>
{name}
<input
type="text"
name={name}
onChange={handleChange}
value={data[name]}
/>
</label>
))}
</div>
);
}
ReactDOM.render(<Example />, document.getElementById('root'));
具有不同的样板和逻辑。
使用许多状态属性的通用方法:
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"/>
const { useRef, useReducer } = React;
const dataReducer = (state, action) => {
switch (action.type) {
case 'fetchStart':
return {
loading: true,
data: null,
error: null,
};
case 'fetchError':
if (!state.loading) {
return state;
}
return {
loading: false,
data: null,
error: action.payload.error,
};
case 'fetchSuccess': {
if (!state.loading) {
return state;
}
return {
loading: false,
data: action.payload.data,
error: null,
};
}
default:
return state;
}
};
function Example() {
const [{ loading, data, error }, dispatch] = useReducer(dataReducer, {
loading: false,
data: null,
error: null,
});
const throwErrorRef = useRef(null);
const handleFetch = function () {
if (loading) {
return;
}
dispatch({ type: 'fetchStart' });
const timeoutId = setTimeout(() => {
dispatch({ type: 'fetchSuccess', payload: { data: { test: 'Text' } } });
}, 5000);
throwErrorRef.current = () => {
clearTimeout(timeoutId);
dispatch({ type: 'fetchError', payload: { error: 'Oh noes!' } });
};
};
const handleFetchError = function () {
throwErrorRef.current && throwErrorRef.current();
};
return (
<div>
<button onClick={handleFetch}>Start Loading</button>
<button onClick={handleFetchError}>Throw an error in the fetch!</button>
<div>loading: {`${loading}`}</div>
<div>error: {error}</div>
<div>data: {JSON.stringify(data)}</div>
</div>
);
}
ReactDOM.render(<Example />, document.getElementById('root'));
对于稍微复杂的逻辑,这是数据获取的示例:
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"/>
const [,forceUpdate] = useReducer((state)=>state+1,0);
// Example use: forceUpdate();
我使用的一个简单的方法是强制更新,它只是增加一个值以使组件重新呈现。
setState
我修改了您的示例2,以添加对更新状态的函数方法的支持,以便更接近使用useReducer
进行的this.setState
仿制。我想不出一种使回调工作正常的方法(const { useRef, useReducer } = React;
const stateReducer = (state, action) => {
if (typeof action === 'function') {
action = action(state);
}
return { ...state, ...action };
};
const useMergeState = (initialState) => {
return useReducer(stateReducer, initialState);
};
function Example() {
const [state, setState] = useMergeState({
loading: false,
data: null,
error: null,
count: 0,
});
const throwErrorRef = useRef(null);
const handleFetch = function () {
if (state.loading) {
return;
}
setState({ loading: true });
const timeoutId = setTimeout(() => {
setState({
data: { text: 'A super long text', loading: false, error: null },
});
}, 5000);
throwErrorRef.current = () => {
clearTimeout(timeoutId);
setState({ error: 'Oh noes!', loading: false, data: null });
};
};
const handleFetchError = function () {
throwErrorRef.current && throwErrorRef.current();
};
const incrementCount = function () {
setState((state) => ({ count: state.count + 1 }));
setState((state) => ({ count: state.count + 1 }));
};
return (
<div>
<button onClick={handleFetch}>Start Loading</button>
<button onClick={handleFetchError}>Throw an error in the fetch!</button>
<div>loading: {`${state.loading}`}</div>
<div>error: {state.error}</div>
<div>data: {JSON.stringify(state.data)}</div>
<button onClick={incrementCount}>increase count by 2</button>
<div>count: {state.count}</div>
</div>
);
}
ReactDOM.render(<Example />, document.getElementById('root'));
中的第二个参数)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"/>
--