我的状态更新顺序有这个问题。有一个数据源,其中包含一些由键存储的值,这些值通过WebSocket不断更新。我已经做了一个自定义钩子,从数据源设置初始值并订阅套接字更新-并在任何相关更新时调用setState。
然后我有一个数字输入的表单,它使用当前数据源值作为初始表单输入值。例如,在组件呈现时,数据源提供值"345",然后用户可以编辑它(增加/减少)。
现在,当用户选择不同的数据源时,输入值应该再次重置为当前数据源值—但仅作为显式用户操作的结果—按下数据源切换按钮。在每次更改套接字时重置它将使表单输入不可用。
问题是当我改变数据源键时,从自定义钩子返回的值是旧的(它只在useEffect中改变)。
下面是代码示例:
import React, { useEffect, useState } from "react";
// some data source, which has initial values and is constantly updated from socket
const data = {
one: 12345,
two: 23456,
};
// a simplified version of hook, which gets current value
// and subscribes to socket updates
const useMyHook = (key) => {
const [state, setState] = useState(data[key]);
useEffect(() => {
// set current state
setState(data[key]);
// subscribes/unsubscribes to socket updates, omitted for simplicity
// ...
}, [key]);
return state;
};
export default function App() {
const [dataSource, setDataSource] = useState("one");
const dataSourceValue = useMyHook(dataSource);
const [fieldValue, setFieldValue] = useState(dataSourceValue);
const handleDataSourceToggle = (e) => {
e.preventDefault();
setDataSource(dataSource === "one" ? "two" : "one");
};
const handleUserInput = (e) => {
setFieldValue(e.target.value);
};
// input value reset - please note, that I cannot use dataSourceValue as a dependency here
// because I don't want to lose the value entered by user - only when he explicitly changes the data source
useEffect(() => {
setFieldValue(dataSourceValue);
}, [dataSource]);
return (
<div className="App">
<h1>Custom Hook Example</h1>
<form>
<p>
<input type="number" value={fieldValue} onChange={handleUserInput} />
</p>
<p>
Data source: <strong>{dataSource}</strong>
</p>
<p>
Data source value: <strong>{dataSourceValue}</strong>
</p>
<button onClick={handleDataSourceToggle}>Change data source</button>
</form>
</div>
);
}
这个例子是有意简化的,实际上数据源是在一些外部组件中切换的,当前组件从React context prop接收它。
如何解决这个问题?
这是沙盒:https://codesandbox.io/s/elated-brook-r9iubv?file=/src/App.js
现在,当用户选择不同的数据源时,输入值应该再次重置为当前数据源值-但仅作为显式用户操作的结果-按下数据源切换按钮。在每次更改套接字时重置它将使表单输入不可用。
这不是React可以猜测的东西,你必须明确地说(程序)哪些事件应该改变fieldValue
,例如通过在handleDataSourceToggle
回调中添加另一个setFieldValue
。
你也应该避免这样:
// input value reset - please note, that I cannot use dataSourceValue as a dependency here
// because I don't want to lose the value entered by user - only when he explicitly changes the data source
useEffect(() => {
setFieldValue(dataSourceValue);
}, [dataSource]);
你将得到eslint警告,因为这不是应该使用的效果。如果您觉得需要省略一些依赖项,那么很可能意味着您应该以不同的方式编写代码。在这种情况下,我认为你根本不需要这种效果。