187

考虑下面的钩子示例

   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>
        );
     }

基本上我们使用 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>
        }
 }

但是我的问题是如何强制上面的功能组件立即用钩子重新渲染?

4

18 回答 18

116

这可以通过useStateor实现useReducer,因为在内部useState使用useReducer

const [, updateState] = React.useState();
const forceUpdate = React.useCallback(() => updateState({}), []);

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

setCount是不正确使用的示例,forceUpdate出于setState性能原因是异步的,不应仅仅因为未正确执行状态更新而强制同步。如果一个状态依赖于先前设置的状态,这应该使用更新函数来完成,

如果您需要根据之前的状态设置状态,请阅读下面的 updater 参数。

<...>

updater 函数接收到的 state 和 props 都保证是最新的。更新器的输出与状态浅合并。

setCount可能不是一个说明性示例,因为它的目的尚不清楚,但更新程序功能就是这种情况:

setCount(){
  this.setState(({count}) => ({ count: count + 1 }));
  this.setState(({count2}) => ({ count2: count + 1 }));
  this.setState(({count}) => ({ count2: count + 1 }));
}

这被 1:1 转换为钩子,但用作回调的函数应该更好地被记忆:

   const [state, setState] = useState({ count: 0, count2: 100 });

   const setCount = useCallback(() => {
     setState(({count}) => ({ count: count + 1 }));
     setState(({count2}) => ({ count2: count + 1 }));
     setState(({count}) => ({ count2: count + 1 }));
   }, []);
于 2018-11-08T20:18:20.567 回答
54
于 2019-10-29T11:26:02.993 回答
51

React Hooks FAQ官方解决方案forceUpdate

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

工作示例

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"));
<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>

于 2020-01-05T17:25:40.280 回答
37

正如其他人所提到的,useState工作 - 这是mobx-react-lite实现更新的方式 - 你可以做类似的事情。

定义一个新的钩子,useForceUpdate-

import { useState, useCallback } from 'react'

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

并在组件中使用它 -

const forceUpdate = useForceUpdate();
if (...) {
  forceUpdate(); // force re-render
}

请参阅https://github.com/mobxjs/mobx-react-lite/blob/master/src/utils.tshttps://github.com/mobxjs/mobx-react-lite/blob/master/src/useObserver .ts

于 2019-04-26T06:38:42.823 回答
28

替代@MinhKha 的答案:

它可以更清洁useReducer

const [, forceUpdate] = useReducer(x => x + 1, 0);

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

于 2019-10-13T04:47:20.353 回答
15

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

const [, forceUpdate] = React.useState(0);

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

希望这有帮助!

于 2019-07-03T04:13:50.260 回答
12

你最好只让你的组件依赖于状态和道具,它会按预期工作,但如果你真的需要一个函数来强制组件重新渲染,你可以使用useState钩子并在需要时调用该函数。

例子

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/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>

于 2018-11-08T20:17:30.240 回答
12

简单的代码

const forceUpdate = React.useReducer(bool => !bool)[1];

采用:

forceUpdate();
于 2020-06-06T18:53:06.573 回答
8

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

例如:

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

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

...
<DataTable
    key={tableKey}
    data={tableData}/>
于 2019-07-14T19:25:20.037 回答
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>
)

于 2019-07-11T11:31:37.323 回答
4

一线解决方案:

const useForceUpdate = () => useState()[1];

useState返回一对值:当前状态和更新它的函数 - statesetter,这里我们只使用 setter 来强制重新渲染。

于 2020-06-22T06:48:35.010 回答
4

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>
  )
}

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

免责声明我是这个图书馆的作者。

于 2020-10-08T13:49:41.543 回答
3

我的变体forceUpdate不是通过 acounter而是通过一个对象:

// Emulates `forceUpdate()`
const [unusedState, setUnusedState] = useState()
const forceUpdate = useCallback(() => setUnusedState({}), [])

因为{} !== {}每次。

于 2019-11-21T13:01:56.890 回答
3

单行解决方案:

const [,forceRender] = useReducer((s) => s+1, 0)

你可以在这里了解 useReducer。 https://reactjs.org/docs/hooks-reference.html#usereducer

于 2020-05-11T06:15:18.163 回答
2

这将渲染依赖组件 3 次(具有相等元素的数组不相等):

const [msg, setMsg] = useState([""])

setMsg(["test"])
setMsg(["test"])
setMsg(["test"])
于 2020-01-23T10:42:30.083 回答
2

在 Hook 中有很多方法可以强制重新渲染。

对我来说,参考对象值的简单方法useState()和提示。

const [, forceRender] = useState({});

// Anywhre
forceRender({});

代码沙盒示例

于 2021-04-16T02:46:40.637 回答
1

对于基于 React 类的常规组件,请参阅URLforceUpdate处的 API 的React 文档。文档提到:

通常你应该尽量避免使用 forceUpdate() 并且只在 render() 中读取 this.props 和 this.state

但是,文档中也提到:

如果你的 render() 方法依赖于其他数据,你可以通过调用 forceUpdate() 告诉 React 组件需要重新渲染。

因此,尽管使用的用例forceUpdate可能很少见,而且我从未使用过它,但是我已经看到其他开发人员在我从事的一些遗留企业项目中使用它。

因此,对于功能组件的等效功能,请参阅URL 上 HOOKS 的 React 文档。根据上面的 URL,可以使用“useReducer”钩子forceUpdate为功能组件提供功能。

下面提供了一个工作代码示例,URLthat does not use state or props也可在 CodeSandbox 上找到

import React, { useReducer, useRef } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  // Use the useRef hook to store a mutable value inside a functional component for the counter
  let countref = useRef(0);

  const [, forceUpdate] = useReducer(x => x + 1, 0);

  function handleClick() {
    countref.current++;
    console.log("Count = ", countref.current);
    forceUpdate(); // If you comment this out, the date and count in the screen will not be updated
  }

  return (
    <div className="App">
      <h1> {new Date().toLocaleString()} </h1>
      <h2>You clicked {countref.current} times</h2>
      <button
        onClick={() => {
          handleClick();
        }}
      >
        ClickToUpdateDateAndCount
      </button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

注意:此URL还提供了使用 useState 挂钩(而不是 useReducer)的替代方法。

于 2019-10-01T18:04:19.427 回答
1
const useForceRender = () => {
  const [, forceRender] = useReducer(x => !x, true)
  return forceRender
}

用法

function Component () {
  const forceRender = useForceRender() 
  useEffect(() => {
    // ...
    forceRender()
  }, [])
于 2021-10-15T11:12:52.073 回答