我有一个函数组件,想要强制它重新渲染。
我该如何做?
由于没有实例 this
,我不能调用 this.forceUpdate()
。
我有一个函数组件,想要强制它重新渲染。
我该如何做?
由于没有实例 this
,我不能调用 this.forceUpdate()
。
const [, forceUpdate] = useReducer(x => x + 1, 0);
使用方法:
function MyComponent(){
const [, forceUpdate] = useReducer(x => x + 1, 0);
return (
<div onClick={forceUpdate}>
Click me to refresh
</div>
);
}
useState
(更明确的答案)使用 React hooks,你现在可以在函数组件中调用 useState()
。
useState()
将返回一个包含两个元素的数组:
通过 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
// A function that increment the previous state like here
// is better than directly setting `setValue(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
),它使用了 React 的状态钩子 useState
。它增加了组件状态的值,从而告诉 React 重新渲染组件。
在这个答案的旧版本中,代码片段使用了一个布尔值,并在forceUpdate()
中进行了切换。现在我已经编辑了我的答案,代码片段使用了一个数字而不是布尔值。
为什么?(你会问我)
因为曾经有一次,我的forceUpdate()
被连续两次调用,来自两个不同的事件,因此它将布尔值重置为原始状态,导致组件从未渲染。
这是因为在useState
的setter函数(这里是setValue
)中,React
会将前一个状态与新状态进行比较,只有状态不同才会进行渲染。
forceUpdate
的信息。 - jdelmanuseState
钩子,而不是类中的setState
,除非你实现了shouldUpdate方法,否则它确实不会进行比较。请查看我发布的相同演示,但使用静态值作为setState
,它不会再次渲染:https://codesandbox.io/s/determined-rubin-8598l - Yairopro官方FAQ 现在建议这样做,如果你真的需要这样做:
const [ignored, forceUpdate] = useReducer(x => x + 1, 0);
function handleClick() {
forceUpdate();
}
const [, forceUpdate] = useReducer(x => x + 1, 0);
。请注意,这不会改变原始意思。 - Konstantin Smolyaninconst forceUpdate = useReducer(x => x + 1, 0)[1]
- vsync更新 React v16.8(2019年2月16日发布)
自从React 16.8发布了hooks以来,函数组件具备了持久状态的能力。有了这个能力,你现在可以模拟一个forceUpdate:
function App() {
const [, forceUpdate] = React.useReducer(o => !o);
console.log("render");
return (
<div>
<button onClick={forceUpdate}>Force Render</button>
</div>
);
}
const rootElement = document.getElementById("root");
const root = ReactDOM.createRoot(rootElement);
root.render(<App />);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<div id="root"/>
在 React 16.8.0 之前
不行,无状态函数组件只是普通的函数,返回 JSX,你无法访问 React 的生命周期方法,因为你没有继承自 React.Component。
把函数组件看作类组件中的 render 方法的一部分。
props
或 state
变化时运行渲染方法的情况下,例如。你不能强制执行无状态组件上的渲染函数,因为无状态组件上没有 render
函数。无状态组件不会 extends
React.Component
,它们只是返回 jsx
的普通函数。 - Sagiv b.g如果你想强制重新渲染,可以添加一个虚拟状态来触发重新渲染。
const [rerender, setRerender] = useState(false);
...
setRerender(!rerender); //whenever you want to re-render
这将确保重新渲染,您可以在任何地方调用setRerender(!rerender)
,以便您随时进行重新渲染 :)
我使用了一个名为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} />;
};
useForceUpdate
使用了其他答案中提到的useCallback
。这个库只是一个实用工具库,可以帮您省去一些打字。 - asyncwait最佳实践-每次渲染时不要创建多余的变量:
const forceUpdateReducer = (i) => i + 1
export const useForceUpdate = () => {
const [, forceUpdate] = useReducer(forceUpdateReducer, 0)
return forceUpdate
}
使用方法:
const forceUpdate = useForceUpdate()
forceUpdate()
如果您已经在函数组件中有一个状态,并且不想更改它,但需要重新渲染组件,您可以模拟一个状态更新,这将重新渲染组件。
const [items,setItems] = useState({
name:'Your Name',
status: 'Idle'
})
const reRender = () =>{
setItems((state) => [...state])
}
这将保持状态不变,并让React认为状态已经更新。
import React from 'react';
function MyComponent() {
const forceUpdate = MyComponent();
return (
<div>
<button onClick={forceUpdate}>
Click to re-render
</button>
</div>
);
}
但是如果您将某些属性传递给它,这将会破坏它。在我的情况下,我只是将接收到的相同属性传递给了rerender函数。
这些都没有给我一个令人满意的答案,所以最终我使用了 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)
}
}
如果您在组件中添加一个属性(prop)和一个状态(state),则可以不使用提供的钩子(hooks)来完成此操作:
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>
)
}