Javascript 如何强制组件在 React 中使用钩子重新渲染?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/53215285/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I force component to re-render with hooks in React?
提问by Hemadri Dasari
Considering below hooks example
考虑下面的钩子示例
import { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
Basically we use this.forceUpdate() method to force the component to re-render immediately in React class components like below example
基本上我们使用 this.forceUpdate() 方法强制组件在 React 类组件中立即重新渲染,如下例所示
class Test extends Component{
constructor(props){
super(props);
this.state = {
count:0,
count2: 100
}
this.setCount = this.setCount.bind(this);//how can I do this with hooks in functional component
}
setCount(){
let count = this.state.count;
count = count+1;
let count2 = this.state.count2;
count2 = count2+1;
this.setState({count});
this.forceUpdate();
//before below setState the component will re-render immediately when this.forceUpdate() is called
this.setState({count2: count
}
render(){
return (<div>
<span>Count: {this.state.count}></span>.
<button onClick={this.setCount}></button>
</div>
}
}
But my query is How can I force above functional component to re-render immediately with hooks?
但我的查询是如何强制上述功能组件立即使用钩子重新渲染?
采纳答案by Estus Flask
This is possible with useStateor useReducer, since useStateuses useReducerinternally:
这可以使用useState或useReducer,因为在内部useState使用useReducer:
const [, updateState] = React.useState();
const forceUpdate = React.useCallback(() => updateState({}), []);
forceUpdateisn't intended to be used under normal circumstances, only in testing or other outstanding cases. This situation may be addressed in a more conventional way.
forceUpdate不打算在正常情况下使用,仅在测试或其他未解决的情况下使用。这种情况可以以更传统的方式解决。
setCountis an example of improperly used forceUpdate, setStateis asynchronous for performance reasons and shouldn't be forced to be synchronous just because state updates weren't performed correctly. If a state relies on previously set state, this should be done with updater function,
setCount是使用不当的例子forceUpdate,setState是异步的性能,而且不应该被强迫只是因为状态更新没有正确执行是同步的。如果状态依赖于先前设置的状态,则应使用更新程序功能完成,
If you need to set the state based on the previous state, read about the updater argument below.
<...>
Both state and props received by the updater function are guaranteed to be up-to-date. The output of the updater is shallowly merged with state.
如果您需要根据之前的状态设置状态,请阅读下面的更新程序参数。
<...>
updater 函数接收到的 state 和 props 都保证是最新的。更新器的输出与状态浅合并。
setCountmay not be an illustrative example because its purpose is unclear but this is the case for updater function:
setCount可能不是一个说明性的例子,因为它的目的不明确,但更新程序功能就是这种情况:
setCount(){
this.setState(({count}) => ({ count: count + 1 }));
this.setState(({count2}) => ({ count2: count + 1 }));
this.setState(({count}) => ({ count2: count + 1 }));
}
回答by Brian Burns
As the others have mentioned, useStateworks - here is how mobx-react-liteimplements updates - you could do something similar.
正如其他人提到的,useState有效 - 这是mobx-react-lite实现更新的方式 - 你可以做类似的事情。
Define a new hook, useForceUpdate-
定义一个新的钩子,useForceUpdate-
import { useState, useCallback } from 'react'
export function useForceUpdate() {
const [, setTick] = useState(0);
const update = useCallback(() => {
setTick(tick => tick + 1);
}, [])
return update;
}
and use it in a component -
并在组件中使用它 -
const forceUpdate = useForceUpdate();
if (...) {
forceUpdate(); // force re-render
}
See https://github.com/mobxjs/mobx-react-lite/blob/master/src/utils.tsand https://github.com/mobxjs/mobx-react-lite/blob/master/src/useObserver.ts
见https://github.com/mobxjs/mobx-react-lite/blob/master/src/utils.ts和https://github.com/mobxjs/mobx-react-lite/blob/master/src/useObserver .ts
回答by Qwerty
Generally, you can use any state handling approach you want to trigger an update.
通常,您可以使用任何想要触发更新的状态处理方法。
With TypeScript
使用打字稿
useState
使用状态
const forceUpdate: () => void = React.useState()[1].bind(null, {}) // see NOTE below
useReducer
使用减速器
const forceUpdate = React.useReducer(() => ({}), {})[1] as () => void
as custom hook
作为自定义钩子
Just wrap whatever approach you prefer like this
只需像这样包装您喜欢的任何方法
function useForceUpdate(): () => void {
return React.useReducer(() => ({}), {})[1] as () => void // <- paste here
}
How this works?
这是如何工作的?
"To trigger an update"means to tell React engine that some value has changed and that it should rerender your component.
“触发更新”的意思是告诉 React 引擎某些值已经改变,它应该重新渲染你的组件。
[, setState]from useState()requires a parameter. We get rid of it by binding a fresh object {}.() => ({})in useReduceris a dummy reducer that returns a fresh object each time an action is dispatched.{}(fresh object)is required so that it triggers an update by changing a reference in the state.
[, setState]fromuseState()需要一个参数。我们通过绑定一个新对象来摆脱它{}。() => ({})inuseReducer是一个虚拟的reducer,每次分派动作时都会返回一个新对象。{}(fresh object)是必需的,以便它通过更改状态中的引用来触发更新。
PS: useStatejust wraps useReducerinternally. source
PS:useState只是在useReducer内部包装。来源
NOTE:Using .bind with useState causes a change in function reference between renders. It is possible to wrap it inside useCallback as already explained here, but then it wouldn't be a sexy one-liner?. The Reducer version already keepsreference equality between renders. This is important if you want to pass the forceUpdate function in props.
注意:将 .bind 与 useState 一起使用会导致渲染之间的函数引用发生变化。可以将它包装在 useCallback 中,正如这里已经解释的那样,但是它不会是一个性感的单线吗?. Reducer 版本已经在渲染之间保持引用相等。如果你想在 props 中传递 forceUpdate 函数,这很重要。
plain JS
纯JS
const forceUpdate = React.useState()[1].bind(null, {}) // see NOTE above
const forceUpdate = React.useReducer(() => ({}))[1]
回答by Tholle
You should preferably only have your component depend on state and props and it will work as expected, but if you really need a function to force the component to re-render, you could use the useStatehook and call the function when needed.
你最好只让你的组件依赖 state 和 props,它会按预期工作,但如果你真的需要一个函数来强制组件重新渲染,你可以使用useState钩子并在需要时调用该函数。
Example
例子
const { useState, useEffect } = React;
function Foo() {
const [, forceUpdate] = useState();
useEffect(() => {
setTimeout(forceUpdate, 2000);
}, []);
return <div>{Date.now()}</div>;
}
ReactDOM.render(<Foo />, document.getElementById("root"));
<script src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script>
<div id="root"></div>
回答by Minh Kha
You can simply define the useState like that:
您可以像这样简单地定义 useState:
const [, forceUpdate] = React.useState(0);
And usage: forceUpdate(n => !n)
和用法: forceUpdate(n => !n)
Hope this help !
希望这有帮助!
回答by Taher
Alternative to @MinhKha's answer:
替代@MinhKha 的回答:
It can be much cleaner with useReducer:
它可以更清洁useReducer:
const [, forceUpdate] = useReducer(x => x + 1, 0);
Usage:
forceUpdate()- cleaner without params
用法:
forceUpdate()- 没有参数的清洁器
回答by Fergie
You can (ab)use normal hooks to force a rerender by taking advantage of the fact that React doesn't print booleansin JSX code
您可以 (ab) 使用普通钩子通过利用React 不会在 JSX 代码中打印布尔值这一事实来强制重新渲染
// create a hook
const [forceRerender, setForceRerender] = React.useState(true);
// ...put this line where you want to force a rerender
setForceRerender(!forceRerender);
// ...make sure that {forceRerender} is "visible" in your js code
// ({forceRerender} will not actually be visible since booleans are
// not printed, but updating its value will nonetheless force a
// rerender)
return (
<div>{forceRerender}</div>
)
回答by Idan
Potential option is to force update only on specific component using key. Updating the key trigger a rendering of the component (which failed to update before)
潜在的选择是使用key. 更新密钥会触发组件的渲染(之前未能更新)
For example:
例如:
const [tableKey, setTableKey] = useState(1);
...
useEffect(() => {
...
setTableKey(tableKey + 1);
}, [tableData]);
...
<DataTable
key={tableKey}
data={tableData}/>
回答by think-serious
Solution in one single line:
单行解决方案:
const [,forceRender] = useReducer((s) => s+1, 0)
You can learn about useReducer here. https://reactjs.org/docs/hooks-reference.html#usereducer
您可以在此处了解 useReducer。 https://reactjs.org/docs/hooks-reference.html#usereducer
回答by ford04
Here is the official solution (forceUpdate+ functional component) from the React Hooks FAQ:
这是React Hooks FAQ 中的官方解决方案(forceUpdate+ 功能组件):
const Test = () => {
const [_count, forceUpdate] = useReducer(x => x + 1, 0);
return (
<div>
<h3 onClick={forceUpdate}>Click me!</h3>
<p>Rendered {_count} times</p>
</div>
);
};
ReactDOM.render(<Test />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.1/umd/react.production.min.js" integrity="sha256-vMEjoeSlzpWvres5mDlxmSKxx6jAmDNY4zCt712YCI0=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.1/umd/react-dom.production.min.js" integrity="sha256-QQt6MpTdAD0DiPLhqhzVyPs1flIdstR4/R7x4GqCvZ4=" crossorigin="anonymous"></script>
<script>var useReducer = React.useReducer</script>
<div id="root"></div>

