我该如何在React中强制使用钩子重新渲染组件?

Hem*_*ari 38 javascript reactjs react-native react-hooks

考虑下面的钩子示例

   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>
        );
     }
Run Code Online (Sandbox Code Playgroud)

基本上,我们使用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>
        }
 }
Run Code Online (Sandbox Code Playgroud)

但是我的查询是如何强制上述功能组件立即使用钩子重新渲染?

Qwe*_*rty 45

通常,您可以使用任何想要触发更新的状态处理方法。

使用打字稿

代码沙盒示例

使用状态

const forceUpdate: () => void = React.useState()[1].bind(null, {})  // see NOTE below
Run Code Online (Sandbox Code Playgroud)

使用减速器

const forceUpdate = React.useReducer(() => ({}), {})[1] as () => void
Run Code Online (Sandbox Code Playgroud)

作为自定义钩子

只需像这样包装您喜欢的任何方法

function useForceUpdate(): () => void {
  return React.useReducer(() => ({}), {})[1] as () => void // <- paste here
}
Run Code Online (Sandbox Code Playgroud)

这是如何工作的?

触发更新”的意思是告诉 React 引擎某些值已经改变,它应该重新渲染你的组件。

[, setState]fromuseState()需要一个参数。我们通过绑定一个新对象来摆脱它{}
() => ({})inuseReducer是一个虚拟的reducer,每次分派动作时都会返回一个新对象。
{} (fresh object)是必需的,以便它通过更改状态中的引用来触发更新。

PS:useState只是在useReducer内部包装。来源

注意:将 .bind 与 useState 一起使用会导致渲染之间的函数引用发生变化。可以将它包装在 useCallback 中,正如这里已经解释的那样,但它不会是一个性感的 one-liner™。Reducer 版本已经在渲染之间保持引用相等。如果你想在 props 中传递 forceUpdate 函数,这很重要。

纯JS

const forceUpdate = React.useState()[1].bind(null, {})  // see NOTE above
const forceUpdate = React.useReducer(() => ({}))[1]
Run Code Online (Sandbox Code Playgroud)

  • 我必须对打字稿 useState 解决方案使用以下内容 `const forceUpdate: () =&gt; void = React.useState({})[1].bind(null, {});` 否则我会收到 useState 的类型错误类型错误 (4认同)

for*_*d04 34

React Hooks FAQ官方解决方案forceUpdate

const [_, forceUpdate] = useReducer((x) => x + 1, 0);
// usage
<button onClick={forceUpdate}>Force update</button>
Run Code Online (Sandbox Code Playgroud)

工作示例

const [_, forceUpdate] = useReducer((x) => x + 1, 0);
// usage
<button onClick={forceUpdate}>Force update</button>
Run Code Online (Sandbox Code Playgroud)
const App = () => {
  const [_, forceUpdate] = useReducer((x) => x + 1, 0);

  return (
    <div>
      <button onClick={forceUpdate}>Force update</button>
      <p>Forced update {_} times</p>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));
Run Code Online (Sandbox Code Playgroud)


nul*_*ook 27

替代@MinhKha 的回答:

它可以更清洁useReducer

const [, forceUpdate] = useReducer(x => x + 1, 0);
Run Code Online (Sandbox Code Playgroud)

用法: forceUpdate()- 没有参数的清洁器


Est*_*ask 24

可以使用useState或进行useReducer,因为在内部useState使用useReducer

const [, updateState] = React.useState();
const forceUpdate = useCallback(() => updateState({}), []);
Run Code Online (Sandbox Code Playgroud)

forceUpdate不打算在正常情况下使用,仅在测试或其他出色情况下使用。可以以更常规的方式解决这种情况。

setCount是不当使用的一个例子forceUpdatesetState出于性能原因是异步的,不应仅由于状态更新未正确执行而被迫同步。如果状态依赖于先前设置的状态,则应使用updater函数来完成,

如果需要基于先前的状态来设置状态,请阅读以下有关updater参数的信息。

<...>

更新器功能接收到的状态和道具都保证是最新的。更新器的输出与state合并在一起。

setCount 可能不是说明性的示例,因为其用途尚不清楚,但updater函数就是这种情况:

setCount(){
  this.setState(({count}) => ({ count: count + 1 }));
  this.setState(({count2}) => ({ count2: count + 1 }));
  this.setState(({count}) => ({ count2: count + 1 }));
}
Run Code Online (Sandbox Code Playgroud)

  • 因此,“useCallback”部分并不是真正必要的。没有它它应该可以正常工作。 (4认同)
  • @DávidMolnár `useCallback` 会记住 `forceUpdate`,因此它在组件生命周期内保持不变,并且可以作为 prop 安全地传递。`updateState({})` 在每次 `forceUpdate` 调用时使用新对象更新状态,这会导致重新渲染。所以是的,它在被调用时强制更新。 (3认同)

Gil*_*Dev 10

简单的代码

const forceUpdate = React.useReducer(bool => !bool)[1];
Run Code Online (Sandbox Code Playgroud)

用:

forceUpdate();
Run Code Online (Sandbox Code Playgroud)


Bri*_*rns 9

As the others have mentioned, useState works - here is how mobx-react-lite implements updates - you could do something similar.

Define a new hook, useForceUpdate -

import { useState, useCallback } from 'react'

export function useForceUpdate() {
  const [, setTick] = useState(0);
  const update = useCallback(() => {
    setTick(tick => tick + 1);
  }, [])
  return update;
}
Run Code Online (Sandbox Code Playgroud)

and use it in a component -

const forceUpdate = useForceUpdate();
if (...) {
  forceUpdate(); // force re-render
}
Run Code Online (Sandbox Code Playgroud)

See https://github.com/mobxjs/mobx-react-lite/blob/master/src/utils.ts and https://github.com/mobxjs/mobx-react-lite/blob/master/src/useObserver.ts

  • 根据我对钩子的理解,这可能不起作用,因为每次函数重新渲染时“useForceUpdate”都会返回一个新函数。为了让 `forceUpdate` 在 `useEffect` 中使用时起作用,它应该返回 `useCallback(update)` 请参阅 https://kentcdodds.com/blog/usememo-and-usecallback (2认同)

Ida*_*dan 7

潜在的选择是使用key. 更新密钥会触发组件的渲染(之前未能更新)

例如:

const [tableKey, setTableKey] = useState(1);
...

useEffect(() => {
    ...
    setTableKey(tableKey + 1);
}, [tableData]);

...
<DataTable
    key={tableKey}
    data={tableData}/>
Run Code Online (Sandbox Code Playgroud)


Tho*_*lle 6

您最好只让您的组件依赖状态和道具,并且它将按预期工作,但是如果您确实需要一个函数来强制重新渲染组件,则可以使用useStatehook并在需要时调用该函数。

const { useState, useEffect } = React;

function Foo() {
  const [, forceUpdate] = useState();

  useEffect(() => {
    setTimeout(forceUpdate, 2000);
  }, []);

  return <div>{Date.now()}</div>;
}

ReactDOM.render(<Foo />, document.getElementById("root"));
Run Code Online (Sandbox Code Playgroud)
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.production.min.js"></script>

<div id="root"></div>
Run Code Online (Sandbox Code Playgroud)


Fer*_*gie 6

您可以(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>
)

Run Code Online (Sandbox Code Playgroud)


Min*_*Kha 5

您可以像这样简单地定义useState:

const [, forceUpdate] = React.useState(0);
Run Code Online (Sandbox Code Playgroud)

和用法: forceUpdate(n => !n)

希望对您有所帮助!

  • 如果每次渲染调用forceUpdate偶数次,将会失败。 (9认同)
  • 这很容易出错,应该删除或编辑。 (3认同)
  • 只要不断增加该值即可。 (2认同)

web*_*eat 5

react-tidy有一个专门用于执行此操作的自定义挂钩,称为useRefresh

import React from 'react'
import {useRefresh} from 'react-tidy'

function App() {
  const refresh = useRefresh()
  return (
    <p>
      The time is {new Date()} <button onClick={refresh}>Refresh</button>
    </p>
  )
}
Run Code Online (Sandbox Code Playgroud)

了解有关此挂钩的更多信息

免责声明我是这个库的作者。