React - 如何强制功能组件呈现?
React - How to force a function component to render?
我有一个函数组件,我想强制它重新渲染。
我该怎么做?
由于没有实例 this
,我无法调用 this.forceUpdate()
.
更新 react v16.8(2019 年 2 月 16 日发布)
自 react 16.8 released with hooks 起,函数组件具有持久化 state
的能力。有了这个能力,你现在可以 mimic 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"/>
请注意,应重新考虑这种方法,在大多数情况下,当您需要强制更新时,您可能做错了什么。
反应 16.8.0 之前
不,你不能,State-Less 函数组件只是正常的 functions
returns jsx
,你无权访问 React 生命周期方法,因为您没有从 React.Component
.
延伸
将功能组件视为 class 组件的 render
方法部分。
您现在可以使用 React hooks
使用 React Hooks,您现在可以在函数组件中调用 useState()
。
useState()
将 return 包含 2 个元素的数组:
- 一个值,代表当前状态。
- 是setter。用它来更新值。
通过 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 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
的setter(这里是setValue
)中,React
比较之前的状态和新的状态,并仅在状态不同时渲染。
免责声明:不是问题的答案。
在此留下重要提示:
如果您尝试强制更新无状态组件,则可能是您的设计出了问题。
考虑以下情况:
- 将 setter (setState) 传递给可以更改状态并导致父组件变为 re-render 的子组件。
- 考虑提升状态
- 考虑将该状态放入您的 Redux 存储中,这样可以自动在连接的组件上强制执行 re-render。
我使用了一个名为
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} />;
};
如果您向组件添加 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>
)
}
如果您确实需要这样做,官方常见问题解答 (https://reactjs.org/docs/hooks-faq.html#is-there-something-like-forceupdate) 现在推荐这种方式:
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);
其中 None 给了我一个满意的答案所以最后我得到了我想要的 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>
);
};
您需要传递要跟踪更改的值。应该用于条件渲染的钩子 returns 布尔值。
当 change 值触发 useEffect 更新时,false 会隐藏组件。之后第二个 useEffect 被触发并且更新变为 true 这使得组件再次可见并且这导致更新(在我的情况下调整大小)。
我有一个函数组件,我想强制它重新渲染。
我该怎么做?
由于没有实例 this
,我无法调用 this.forceUpdate()
.
更新 react v16.8(2019 年 2 月 16 日发布)
自 react 16.8 released with hooks 起,函数组件具有持久化 state
的能力。有了这个能力,你现在可以 mimic 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"/>
请注意,应重新考虑这种方法,在大多数情况下,当您需要强制更新时,您可能做错了什么。
反应 16.8.0 之前
不,你不能,State-Less 函数组件只是正常的 functions
returns jsx
,你无权访问 React 生命周期方法,因为您没有从 React.Component
.
将功能组件视为 class 组件的 render
方法部分。
您现在可以使用 React hooks
使用 React Hooks,您现在可以在函数组件中调用 useState()
。
useState()
将 return 包含 2 个元素的数组:
- 一个值,代表当前状态。
- 是setter。用它来更新值。
通过 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 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
的setter(这里是setValue
)中,React
比较之前的状态和新的状态,并仅在状态不同时渲染。
免责声明:不是问题的答案。
在此留下重要提示:
如果您尝试强制更新无状态组件,则可能是您的设计出了问题。
考虑以下情况:
- 将 setter (setState) 传递给可以更改状态并导致父组件变为 re-render 的子组件。
- 考虑提升状态
- 考虑将该状态放入您的 Redux 存储中,这样可以自动在连接的组件上强制执行 re-render。
我使用了一个名为 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} />;
};
如果您向组件添加 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>
)
}
如果您确实需要这样做,官方常见问题解答 (https://reactjs.org/docs/hooks-faq.html#is-there-something-like-forceupdate) 现在推荐这种方式:
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);
None 给了我一个满意的答案所以最后我得到了我想要的 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>
);
};
您需要传递要跟踪更改的值。应该用于条件渲染的钩子 returns 布尔值。
当 change 值触发 useEffect 更新时,false 会隐藏组件。之后第二个 useEffect 被触发并且更新变为 true 这使得组件再次可见并且这导致更新(在我的情况下调整大小)。