页面在history.push()之后不重新渲染



我正在使用React Router和我的React dictionary项目,由于某种原因,我的页面在我运行history.push()(使用来自React - Router的useHistory钩子)后没有重新渲染。我有一个搜索栏,我使用这个功能去到一个新的链接。

const KeyPressHandler: KeyboardEventHandler<HTMLInputElement> = (event) => {
const { value } = event.currentTarget;
if ((event.code === "Enter" || event.code === "NumpadEnter") && value.length)
history.push(`/dictionary/${value}`);
};
};

我的App组件看起来像这样:

const App = (): JSX.Element => {
...
return (
...
<Route path="/dictionary" component={DictionaryEntryPage} />
...
);
};

DictionaryEntryPage组分是:

const DictionaryEntryPage = (): JSX.Element => {
const [wordData, setWordData] = useState<WordData[] | undefined | null>(undefined);
// useRouteMatch is imported from react-router
const match = useRouteMatch<{ requestedWord: string }>("/dictionary/:requestedWord");
useEffect(() => {
const { requestedWord } = match?.params ?? {};
if (requestedWord) {
(async () => {
const data = await parseWordData(requestedWord);
setWordData(data || null);
})();
} else setWordData(null);
}, []);

const wordDataEls = wordData ? wordData.map((data, i) => <Word {...data} key={i} />) : <Loading />;
...
}

让我知道我应该在问题中添加/删除什么,如果有人想看的话,这里有一个演示链接。

我认为您实际上会发现您的组件正在正确地重新呈现。问题是你的useEffect,而不是渲染的问题。

在你的useEffect中,你在[]的末尾有一个空的依赖数组。通过使用一个空的依赖项数组,你本质上是在告诉React没有任何会改变,并导致这个副作用重新运行(事实并非如此)。如果这确实是您的愿望,并且您只希望在组件第一次挂载时运行副作用,则应该完全删除依赖项数组,这将导致您的代码看起来像这样:

useEffect(() => {
const { requestedWord } = match?.params ?? {};
console.log('ran effect');
if (requestedWord) {
(async () => {
const data = await parseWordData(requestedWord);
setWordData(data || null);
})();
} else setWordData(null);
});
但是,您真正想要的实际上是在每次搜索词更改时重新运行该效果。让你的useEffect的其他一切保持不变,这会给你这个:
useEffect(() => {
const { requestedWord } = match?.params ?? {};
console.log('ran effect');
if (requestedWord) {
(async () => {
const data = await parseWordData(requestedWord);
setWordData(data || null);
})();
} else setWordData(null);
}, [
match
]);

以上仍然存在一些问题。也就是说,match是一个对象,这意味着它是一个引用类型。在决定是否重新运行副作用时,React只进行浅层比较,而不是深入检查引用类型以查看它们是否相等(查看这篇文章以获得关于引用类型与值类型的更好解释)。基本上,如果你让useEffect像那样,你将运行效果,并且可能从你的API中得到很多速率限制错误。所以,你想要的最后一个例子是:

const DictionaryEntryPage = (): JSX.Element => {
const [wordData, setWordData] = useState<WordData[] | undefined | null>(undefined);
const match = useRouteMatch<{ requestedWord: string }>("/dictionary/:requestedWord");
const { requestedWord } = match?.params ?? {};
useEffect(() => {
console.log('ran effect');
if (requestedWord) {
(async () => {
const data = await parseWordData(requestedWord);
setWordData(data || null);
})();
} else setWordData(null);
}, [
requestedWord
]);
//...
}

现在你在渲染中提取requestedWord(这是一个字符串,因此是一个值类型),你的useEffect只是依赖于这个字符串。现在您的效果将是向API发出正确数量的请求,它不会有关于所请求单词的陈旧数据。

作为题外话,我强烈建议你安装eslint-plugin-react-hooks,并将其添加到你的.eslintrc.json中并进行扩展。它有一个叫做exhaustive-deps的规则,它会发现这个问题并提前警告你。

关于详尽依赖的附加阅读:https://reactjs.org/docs/hooks-faq.html#why-am-i-seeing-stale-props-or-state-inside-my-function

相关内容

  • 没有找到相关文章

最新更新