考虑下面的钩子示例

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

但我的问题是,我如何才能强制上述功能组件重新渲染立即与挂钩?


当前回答

@MinhKha的回答:

使用useReducer可以更简洁:

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

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

其他回答

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

const useForceRender = () => {
  const [, forceRender] = useReducer(x => !x, true)
  return forceRender
}

使用

function Component () {
  const forceRender = useForceRender() 
  useEffect(() => {
    // ...
    forceRender()
  }, [])

对于常规的基于React类的组件,请参考React Docs中的forceUpdate api。文件中提到:

通常你应该尽量避免使用forceUpdate() 读一读。道具和这个。render()中的状态

然而,在文档中也提到:

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

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

因此,对于功能组件的等效功能,请参考这个URL中的React Docs for HOOKS。根据上面的URL,可以使用“useReducer”钩子为功能组件提供forceUpdate功能。

下面提供了一个不使用状态或道具的工作代码示例,也可以在这个URL的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);

注意:使用useState钩子(而不是useReducer)的另一种方法也可以在这个URL中使用。

正如其他人提到的,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.ts和https://github.com/mobxjs/mobx-react-lite/blob/master/src/useObserver.ts

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

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

forceUpdate不打算在正常情况下使用,只能在测试或其他突出的情况下使用。这种情况可以用更常规的方法来解决。

setCount是一个使用不当的forceUpdate的例子,setState是异步的,因为性能原因,不应该仅仅因为状态更新没有正确执行而被强制为同步的。如果一个状态依赖于之前设置的状态,这应该通过updater函数来完成,

如果需要基于前面的状态设置状态,请阅读下面的updater参数。 <…> updater函数接收的状态和道具都是有保证的 与时俱进。与更新程序的输出进行浅合并 状态。

setCount可能不是一个说明性的例子,因为它的目的不清楚,但这是updater函数的情况:

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 }));
   }, []);