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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 05:05:06  来源:igfitidea点击:

How can I force component to re-render with hooks in React?

javascriptreactjsreact-nativereact-hooks

提问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:

这可以使用useStateuseReducer,因为在内部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是使用不当的例子forceUpdatesetState是异步的性能,而且不应该被强迫只是因为状态更新没有正确执行是同步的。如果状态依赖于先前设置的状态,则应使用更新程序功能完成,

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.tshttps://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

使用打字稿

codesandbox example

代码沙盒示例

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>