171

我有一个函数组件,我想强制它重新渲染。

我该怎么做?
由于没有实例this,我无法调用this.forceUpdate()

4

13 回答 13

278

你现在可以使用React hooks

使用反应钩子,您现在可以调用useState()您的函数组件。

useState()将返回一个包含两件事的数组:

  1. 一个值,表示当前状态。
  2. 它的二传手。使用它来更新值。

通过它的设置器更新值将强制你的函数组件重新渲染
就像这样forceUpdate做:

import React, { useState } from 'react';

//create your forceUpdate hook
function useForceUpdate(){
    const [value, setValue] = useState(0); // integer state
    return () => setValue(value => value + 1); // update the state to force render
}

function MyComponent() {
    // call your hook here
    const forceUpdate = useForceUpdate();
    
    return (
        <div>
            {/*Clicking on the button will force to re-render like force update does */}
            <button onClick={forceUpdate}>
                Click to re-render
            </button>
        </div>
    );
}

你可以在这里找到一个演示

上面的组件使用了一个自定义的钩子函数 ( useForceUpdate),它使用了反应状态钩子useState。它增加组件的状态值,从而告诉 React 重新渲染组件。

编辑

在此答案的旧版本中,该片段使用了一个布尔值,并将其切换为forceUpdate(). 现在我已经编辑了我的答案,该片段使用数字而不是布尔值。

为什么 ?(你会问我)

因为一旦发生在我身上,我forceUpdate()随后从 2 个不同的事件中被调用了两次,因此它将布尔值重置为其原始状态,并且组件从未呈现。

这是因为在useState's setter ( setValuehere) 中,React将先前的状态与新的状态进行比较,仅当状态不同时才渲染。

于 2018-12-18T16:35:40.870 回答
67

更新 react v16.8(2019 年 2 月 16 日发布)

自从react 16.8 发布了 hooks以来,函数组件现在有能力保持持久化state。有了这个能力, 你现在可以模仿forceUpdate

function App() {
  const [, updateState] = React.useState();
  const forceUpdate = React.useCallback(() => updateState({}), []);
  console.log("render");
  return (
    <div>
      <button onClick={forceUpdate}>Force Render</button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.1/umd/react-dom.production.min.js"></script>
<div id="root"/>

请注意,应该重新考虑这种方法,并且在大多数情况下,当您需要强制更新时,您可能做错了什么。


在反应 16.8.0 之前

不,你不能,State-Less 函数组件只是正常functions返回jsx,你没有任何访问 React 生命周期方法的权限,因为你没有从React.Component.

将函数组件视为render类组件的方法部分。

于 2017-09-15T13:30:24.587 回答
52

官方常见问题解答(https://reactjs.org/docs/hooks-faq.html#is-there-something-like-forceupdate)现在推荐这种方式,如果你真的需要这样做:

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

  function handleClick() {
    forceUpdate();
  }
于 2020-03-23T19:22:01.367 回答
11

我使用了一个名为use-force-update的第三方库 来强制渲染我的反应功能组件。像魅力一样工作。只需在您的项目中使用导入包并像这样使用。

import useForceUpdate from 'use-force-update';

const MyButton = () => {

  const forceUpdate = useForceUpdate();

  const handleClick = () => {
    alert('I will re-render now.');
    forceUpdate();
  };

  return <button onClick={handleClick} />;
};
于 2020-01-21T18:35:55.543 回答
8

免责声明:不是问题的答案。

在这里留下重要说明:

如果您尝试强制更新无状态组件,则很可能您的设计有问题。

考虑以下情况:

  1. 将 setter (setState) 传递给可以更改状态并导致父组件重新渲染的子组件。
  2. 考虑提升状态
  3. 考虑将该状态放在您的 Redux 存储中,这可以自动强制重新渲染连接的组件。
于 2019-05-16T13:14:35.967 回答
6

最简单的方法

如果要强制重新渲染,请添加一个虚拟状态,您可以更改以启动重新渲染。

const [rerender, setRerender] = useState(false);

...
setRerender(!rerender);     //whenever you want to re-render

这将确保重新渲染,并且您可以setRerender(!rerender)随时随地调用:)

于 2021-07-31T14:09:28.647 回答
3

最佳方法 - 每次渲染都不会重新创建多余的变量:

const forceUpdateReducer = (i) => i + 1

export const useForceUpdate = () => {
  const [, forceUpdate] = useReducer(forceUpdateReducer, 0)
  return forceUpdate
}

用法:

const forceUpdate = useForceUpdate()

forceUpdate()
于 2021-03-11T15:56:06.680 回答
1

这可以在不显式使用钩子的情况下完成,只要您向组件添加一个 prop 并向无状态组件的父组件添加一个状态:

const ParentComponent = props => {
  const [updateNow, setUpdateNow] = useState(true)

  const updateFunc = () => {
    setUpdateNow(!updateNow)
  }

  const MyComponent = props => {
    return (<div> .... </div>)
  }

  const MyButtonComponent = props => {
    return (<div> <input type="button" onClick={props.updateFunc} />.... </div>)
  }

  return (
    <div> 
      <MyComponent updateMe={updateNow} />
      <MyButtonComponent updateFunc={updateFunc}/>
    </div>
  )
}
于 2020-03-23T16:42:17.237 回答
1

接受的答案很好。只是为了更容易理解。

示例组件:

export default function MyComponent(props) {

    const [updateView, setUpdateView] = useState(0);

    return (
        <>
            <span style={{ display: "none" }}>{updateView}</span>
        </>
    );
}

要强制重新渲染,请调用以下代码:

setUpdateView((updateView) => ++updateView);
于 2020-05-02T16:05:08.793 回答
1

这些都没有给我一个满意的答案,所以最后我得到了我想要的keyprop、useRef 和一些随机 id 生成器,比如shortid.

基本上,我希望一些聊天应用程序在有人第一次打开应用程序时自动播放。因此,我需要通过轻松的异步等待来完全控制何时以及更新什么答案。

示例代码:

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

// ... your JSX functional component, import shortid somewhere

const [render, rerender] = useState(shortid.generate())

const messageList = useRef([
    new Message({id: 1, message: "Hi, let's get started!"})
])

useEffect(()=>{
    async function _ () {
      await sleep(500)
      messageList.current.push(new Message({id: 1, message: "What's your name?"}))
      // ... more stuff
      // now trigger the update
      rerender(shortid.generate())
   } 
   _()
}, [])

// only the component with the right render key will update itself, the others will stay as is and won't rerender.
return <div key={render}>{messageList.current}</div> 

事实上,这也让我可以滚动聊天消息之类的内容。

const waitChat = async (ms) => {
    let text = "."
    for (let i = 0; i < ms; i += 200) {
        if (messageList.current[messageList.current.length - 1].id === 100) {
            messageList.current = messageList.current.filter(({id}) => id !== 100)
        }
        messageList.current.push(new Message({
            id: 100,
            message: text
        }))
        if (text.length === 3) {
            text = "."
        } else {
            text += "."
        }
        rerender(shortid.generate())
        await sleep(200)
    }
    if (messageList.current[messageList.current.length - 1].id === 100) {
        messageList.current = messageList.current.filter(({id}) => id !== 100)
    }
}
于 2020-12-25T10:22:12.560 回答
1

如果您使用版本 < 16.8 的功能组件。一种解决方法是直接调用相同的函数,例如

import React from 'react';

function MyComponent() {
    const forceUpdate = MyComponent();
    
    return (
        <div>
            <button onClick={forceUpdate}>
                Click to re-render
            </button>
        </div>
    );
}

但是,如果您向它传递一些道具,这将中断。就我而言,我只是将收到的相同道具传递给重新渲染功能。

于 2021-06-03T17:28:51.043 回答
1

如果你已经在函数组件中有一个状态并且你不想改变它并且需要重新渲染你可以伪造一个状态更新,这反过来又会重新渲染组件

const [items,setItems] = useState({
   name:'Your Name',
   status: 'Idle'
})
const reRender = () =>{
setItems((state) => [...state])
}

这将使状态保持原样,并将做出反应,认为状态已更新

于 2021-09-08T12:54:12.930 回答
0

对我来说,仅仅更新状态是行不通的。我正在使用带有组件的库,看起来我无法强制更新组件。

我的方法是使用条件渲染扩展上述方法。就我而言,我想在值更改时调整组件的大小。

//hook to force updating the component on specific change
const useUpdateOnChange = (change: unknown): boolean => {
  const [update, setUpdate] = useState(false);

  useEffect(() => {
    setUpdate(!update);
  }, [change]);

  useEffect(() => {
    if (!update) setUpdate(true);
  }, [update]);

  return update;
};

const MyComponent = () => {
  const [myState, setMyState] = useState();
  const update = useUpdateOnChange(myState);

  ...

  return (
    <div>
      ... ...
      {update && <LibraryComponent />}
    </div>
  );
};

您需要传递要跟踪更改的值。钩子返回布尔值,它应该用于条件渲染。

更改值触发 useEffect 更新时,隐藏组件。之后,第二个 useEffect 被触发并且更新变为,这使组件再次可见,这导致更新(在我的情况下调整大小)。

于 2021-12-13T08:27:39.637 回答