如何强制一个函数组件渲染?

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

我该怎么做呢?< br > 因为没有this实例,所以不能调用this.forceUpdate().

336255 次浏览

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

React 16.8发布了钩子开始,函数组件就有能力保存持久的state。有了这个能力,你现在可以模仿 a 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"/>

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


react 16.8.0之前

不,你不能,无状态函数组件只是普通的functions,返回jsx,你没有任何访问React生命周期方法的权限,因为你没有从React.Component扩展。

可以把function-component看作类组件的render方法部分。

🎉现在可以使用反应钩子

使用react钩子,你现在可以在你的函数组件中调用useState()

useState()将返回一个包含2个东西的数组:

  1. 值,表示当前状态。
  2. setter。使用它来更新值。
< p > 通过setter更新值将迫使函数组件重新呈现,
就像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 state to force render
// An function that increment 👆🏻 the previous state like here
// is better than directly setting `value + 1`
}


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的setter(这里是setValue)中,React将以前的状态与新的状态并且只在状态不同时才呈现。进行比较

我使用了一个名为 use-force-update 强制渲染我的react功能组件。工作很有魅力。 只要在你的项目中使用导入包,并像这样使用
import useForceUpdate from 'use-force-update';


const MyButton = () => {


const forceUpdate = useForceUpdate();


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


return <button onClick={handleClick} />;
};

这可以在不显式使用钩子的情况下完成,只要你给你的组件添加一个道具,给无状态组件的父组件添加一个状态:

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

官方FAQ现在推荐这种方式,如果你真的需要这样做:

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


function handleClick() {
forceUpdate();
}

接受的答案是好的。 为了便于理解

示例组件:

export default function MyComponent(props) {


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


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

强制重新渲染调用下面的代码:

setUpdateView((updateView) => ++updateView);

这些都没有给我一个满意的答案,所以最后我得到了我想要的key道具,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)
}
}

最佳方法-没有多余的变量重新创建在每次渲染:

const forceUpdateReducer = (i) => i + 1


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

用法:

const forceUpdate = useForceUpdate()


forceUpdate()

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

import React from 'react';


function MyComponent() {
const forceUpdate = MyComponent();
    

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

但如果你给它一些支撑,它就会断裂。在我的情况下,我只是通过相同的道具,我收到了渲染功能。

最简单的方法👌

如果你想强制重新渲染,添加一个虚拟状态,你可以改变它来启动重新渲染。

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


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

并且你可以随时随地调用setRerender(!rerender):)

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

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

这将保持状态不变,并将做出反应,认为状态已更新

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

我的方法是用条件渲染扩展上面的方法。在我的例子中,我想在值改变时调整组件的大小。

//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被触发,update执行真正的,这使得组件再次可见,并导致更新(在我的例子中是调整大小)。