自从我们从 React 16 更新到 React 18 以来,我们有一个奇怪的行为。 我们有通过钩子确定的数据。
这适用于 React 16,现在在 React 18 中似乎组件仍然首先使用旧数据值进行渲染,然后才更新数据。不幸的是我只有伪代码,但也许有人可以提供帮助。
import * as _ from "lodash";
import * as React from "react";
const useData = (id: string): string[] => {
const [data, setData] = React.useState(DataManager.selectCurrentDataById(id));
React.useEffect(() => {
const handleDataChange = () => {
const newData = DataManager.selectCurrentDataById(id);
if (_.isEqual(newData, data)) return;
console.log("set new data ", newData);
setData(newData);
};
handleDataChange();
return DataManager.subscribe(handleDataChange);
}, [data, planId]);
console.log("return data ", data);
return data;
};
const MatcherComponent = ({
id,
}: { id: string }): React.ReactElement<{ id: string }> => {
const data = useData(id);
console.log("data ", data);
return (
<TestType type={data[0]} />
)
}
const TestType = ({
type,
}: { type: string }): React.ReactElement<{ type: string }> => {
console.log("type ", type);
return (
<div>{type}</div>
)
}
以及日志,如果数据发生更改:
// the data was `["type1"]`
// in React 18 it is called twice
set new data "type2"
set new data "type2"
// but then first the child component is re-rendered with the old data and this didn't
// happen in React 16
type "type1"
// then the hook seem to return the new value
return data "type2"
data "type2"
// now the value for the child component is correct
type "type2"
它适用于
flushSync
我知道这不是一个好方法,我想了解 React 18 中发生了什么变化。因为 useState 一直是异步的。但在我们更新之前它已经工作了 5 年 :D
import * as _ from "lodash";
import * as React from "react";
import { flushSync } from "react-dom";
const useData = (id: string): string[] => {
const [data, setData] = React.useState(DataManager.selectCurrentDataById(id));
React.useEffect(() => {
const handleDataChange = () => {
const newData = DataManager.selectCurrentDataById(id);
if (_.isEqual(newData, data)) return;
console.log("set new data ", newData);
flushSync(() => {
setData(newData);
});
};
handleDataChange();
return DataManager.subscribe(handleDataChange);
}, [data, planId]);
console.log("return data ", data);
return data;
};