我有一个外部(对组件),我想要监听变化的可观察对象。当对象更新时,它会发出更改事件,然后我想在检测到任何更改时重新呈现组件。
使用顶级React.render
这是可能的,但在一个组件中它不起作用(这是有道理的,因为render
方法只返回一个对象)。
这是一个代码示例:
export default class MyComponent extends React.Component {
handleButtonClick() {
this.render();
}
render() {
return (
<div>
{Math.random()}
<button onClick={this.handleButtonClick.bind(this)}>
Click me
</button>
</div>
)
}
}
单击该按钮会在内部调用this.render()
,但这并不是实际导致渲染发生的原因(您可以看到此操作因为{Math.random()}
创建的文本不会更改)。但是,如果我只是拨打this.setState()
而不是this.render()
,则可以正常使用。
所以我想我的问题是:做反应组件需要有状态才能重新渲染?有没有办法强制组件按需更新而不改变状态?
答案 0 :(得分:616)
在您的组件中,您可以致电this.forceUpdate()
强制重新投降。
答案 1 :(得分:268)
forceUpdate
,因为它偏离了React的心态。 React文档引用了可能使用forceUpdate
的示例:
默认情况下,当组件的状态或道具发生更改时,组件将重新呈现。但是,如果这些隐式更改(例如:对象内部的数据更改而不更改对象本身)或者如果render()方法依赖于其他一些数据,则可以告诉React需要通过调用重新运行render() forceUpdate()。
但是,我想提出这样的想法,即使使用深层嵌套的对象,forceUpdate
也是不必要的。通过使用不可变数据源,跟踪变化变得便宜;更改将始终生成一个新对象,因此我们只需要检查对象的引用是否已更改。您可以使用库Immutable JS在应用程序中实现不可变数据对象。
通常你应该尽量避免使用forceUpdate(),只能在render()中读取this.props和this.state。这使您的组件“纯粹”,您的应用程序更简单,更高效。 https://facebook.github.io/react/docs/component-api.html#forceupdate
更改要重新渲染的元素的键将起作用。通过状态在元素上设置关键支柱,然后在您想要更新设置状态以获得新密钥时。
<Element key={this.state.key} />
然后发生更改并重置密钥
this.setState({ key: Math.random() });
我想要注意,这将替换密钥正在更改的元素。这可能有用的一个示例是当您有一个文件输入字段,您希望在图像上载后重置。
虽然OP问题的真正答案是forceUpdate()
我发现这个解决方案在不同的情况下很有用。我还要注意,如果您发现自己使用forceUpdate
,可能需要查看代码并查看是否有其他方法可以执行操作。
请注意1-9-2019:
以上(更改密钥)将完全替换元素。如果您发现自己更新密钥以进行更改,则可能在代码中的其他位置存在问题。在键中使用Math.random()
将使用每个渲染重新创建元素。我不建议像这样更新密钥,因为反应使用密钥来确定重新渲染事物的最佳方法。
答案 2 :(得分:53)
实际上,forceUpdate()
是唯一正确的解决方案,因为setState()
可能不触发重新渲染,如果在shouldComponentUpdate()
中实现了其他逻辑,或者只是返回false
。
调用
forceUpdate()
将导致在组件上调用render()
,跳过shouldComponentUpdate()
。 more...
除非在setState()
中实现条件呈现逻辑,否则
shouldComponentUpdate()
将始终触发重新渲染。 more...
<小时/> 可以通过
forceUpdate()
从您的组件中调用
this.forceUpdate()
答案 3 :(得分:30)
当您希望两个React组件进行通信时,它们不受关系(父子)的约束,建议使用Flux或类似的体系结构。
您要做的是监听 observable component 商店的更改,该商店保存模型及其界面,并保存导致呈现更改为state
的数据在MyComponent
。当商店推送新数据时,您可以更改组件的状态,从而自动触发渲染。
通常您应该尽量避免使用forceUpdate()
。来自文档:
通常你应该尽量避免使用forceUpdate(),只能在render()中读取this.props和this.state。这使您的应用程序更简单,更高效
答案 4 :(得分:27)
forceUpdate
错误的方式:不要使用索引作为键
this.state.rows.map((item, index) =>
<MyComponent cell={item} key={index} />
)
正确的方式:使用数据ID作为密钥,它可以是一些guid等
this.state.rows.map((item) =>
<MyComponent item={item} key={item.id} />
)
通过这样的代码改进,您的组件将 UNIQUE 并自然呈现
答案 5 :(得分:14)
在 2021 年,这是 the official way 强制更新 React 功能组件。
const [, forceUpdate] = useReducer(x => x + 1, 0);
function handleClick() {
forceUpdate();
}
我知道 OP 是针对类组件的。但是这个问题是在 2015 年提出的,现在钩子可用,许多人可能会在功能组件中搜索 forceUpdate
。这一点是给他们的。
答案 6 :(得分:12)
所以我想我的问题是:React组件需要有状态 为了重新奉献?有没有办法强制组件更新 要求不改变国家?
其他答案试图说明你的方法,但重点是你不应该。即使是改变关键的黑客解决方案也忽略了这一点。 React的力量放弃了控制手动管理什么东西应该渲染,而只是关于你自己应该如何映射输入。然后提供输入流。
如果你需要手动强制重新渲染,你几乎肯定不会做正确的事情。
答案 7 :(得分:10)
使用 HOC(高阶组件)模式,您可以包装React组件并在商店更改时自动更新。这是一种非常轻量级的方法,没有框架。
withStores HOC处理商店更新
10
11
11
11
如何使用
import React, { Component } from 'react'
export default function(/* store1, store2, store3... */) {
const storeArgs = Array.prototype.slice.call(arguments)
return function(WrappedComponent) {
return class WithStore extends Component {
constructor(props) {
super(props)
this.state = {
lastUpdated: Date.now()
}
this.stores = storeArgs
}
_onChange = () => {
this.setState({ lastUpdated: Date.now() })
}
componentWillMount = () => {
this.stores && this.stores.forEach(store => {
// each store has a common change event to subscribe to
store.on('change', this._onChange)
})
}
componentWillUnmount = () => {
this.stores && this.stores.forEach(store => {
store.off('change', this._onChange)
})
}
render() {
return <WrappedComponent
lastUpdated={this.state.lastUpdated}
{...this.props} />
}
}
}
}
简单商店类示例
import React, { Component } from 'react'
import { View, Text, Button } from 'react-native'
import withStores from './withStores'
import userStore from './stores/users'
class MyView {
_increaseUserCount = () => {
userStore.setState({ userCount: userStore.state.count + 1 })
}
render() {
return (
<View>
<Text>User count: {userStore.state.count}</Text>
<Button
title="Increase User Count"
onPress={this._increaseUserCount}
/>
</View>
)
}
}
return withStores(userStore)(MyView)
将实例存储为单身
var ee = require('event-emitter')
export default class Store {
constructor() {
this._state = {}
this._eventEmitter = ee({})
}
get state() {
return this._state
}
setState(newState) {
this._state = {...this._state, ...newState}
this._eventEmitter.emit('change')
}
on(ev, fn) {
this._eventEmitter.on(ev, fn)
}
off(ev, fn) {
this._eventEmitter.off(ev, fn)
}
}
真正意味着小型项目,组件树深度较低。原因是如果你将它用于更大的组件树,这对于为商店更新的部分没有选择性,只是商店本身会触发更新。
如果您以更精细的方式拆分商店,那就没问题了,这就是 redux很好的原因,因为它可以根据需要进行细化,只需要一个商店但是在较小的项目上也有不成比例的样板。
答案 8 :(得分:4)
你可以通过以下两种方式实现:
<强> 1。使用forceUpdate()
方法:
使用forceUpdate()
方法时可能会出现一些故障。一个例子是它忽略了shouldComponentUpdate()
方法,并且无论shouldComponentUpdate()
是否返回false,都将重新呈现视图。因此,在可能的情况下应尽量避免使用forceUpdate()。
<强> 2。将this.state传递给setState()
方法
以下代码行克服了上一个示例的问题:
this.setState(this.state);
真的这一切都是用触发重新渲染的当前状态覆盖当前状态。这仍然不是最好的做事方式,但它确实克服了使用forceUpdate()方法可能遇到的一些故障。
答案 9 :(得分:2)
我们可以如下使用this.forceUpdate()。
class MyComponent extends React.Component {
handleButtonClick = ()=>{
this.forceUpdate();
}
render() {
return (
<div>
{Math.random()}
<button onClick={this.handleButtonClick}>
Click me
</button>
</div>
)
}
}
ReactDOM.render(<MyComponent /> , mountNode);
即使使用setState重新呈现组件,DOM中的元素'Math.random'部分也只会更新。
这里的所有答案都是对问题理解的正确补充。.众所周知,不使用setState({})即可重新渲染组件是通过使用forceUpdate()。
上面的代码与setState一起运行,如下所示。
class MyComponent extends React.Component {
handleButtonClick = ()=>{
this.setState({ });
}
render() {
return (
<div>
{Math.random()}
<button onClick={this.handleButtonClick}>
Click me
</button>
</div>
)
}
}
ReactDOM.render(<MyComponent /> , mountNode);
答案 10 :(得分:1)
只需再回复一次即可备份已接受的答案:-)
React不鼓励使用forceUpdate()
,因为它们通常在函数式编程中使用非常“这是唯一的方法”。在许多情况下这很好,但是许多React开发人员都带有面向对象的背景,并且采用这种方法,完全可以监听可观察对象。
如果这样做,您可能知道您必须在可观察到的“开火”时重新渲染,因此,您应该使用forceUpdate()
,而这里不涉及shouldComponentUpdate()
实际上是一个加法。
MobX之类的工具需要采用面向对象的方法,实际上是在表面之下进行此操作(实际上MobX直接调用render()
)
答案 11 :(得分:1)
有几种方法可以重新渲染组件:
最简单的解决方案是使用forceUpdate()方法:
this.forceUpdate()
另一种解决方案是在状态(nonUsedKey)中创建未使用的密钥 并通过更新此nonUsedKey来调用setState函数:
this.setState({ nonUsedKey: Date.now() } );
或重写所有当前状态:
this.setState(this.state);
更改道具还可以重新渲染组件。
答案 12 :(得分:1)
为完整起见,您还可以在功能组件中实现此目的:
const [, updateState] = useState();
const forceUpdate = useCallback(() => updateState({}), []);
// ...
forceUpdate();
或者,作为可重复使用的钩子:
const useForceUpdate = () => {
const [, updateState] = useState();
return useCallback(() => updateState({}), []);
}
// const forceUpdate = useForceUpdate();
请参阅:https://stackoverflow.com/a/53215514/2692307
请注意,使用强制更新机制仍然是不正确的做法,因为它违背了反应的心态,因此,如果可能的话,仍应避免使用它。
答案 13 :(得分:0)
我发现最好避免使用forceUpdate()。强制重新渲染的一种方法是在临时外部变量上添加render()的依赖关系,并在需要时更改该变量的值。
这是一个代码示例:
class Example extends Component{
constructor(props){
this.state = {temp:0};
this.forceChange = this.forceChange.bind(this);
}
forceChange(){
this.setState(prevState => ({
temp: prevState.temp++
}));
}
render(){
return(
<div>{this.state.temp &&
<div>
... add code here ...
</div>}
</div>
)
}
}
当您需要强制重新渲染时调用this.forceChange()。
答案 14 :(得分:0)
ES6 - 我举了一个例子,这对我有帮助:
在“短if语句”中,您可以传递如下的空函数:
isReady ? ()=>{} : onClick
这似乎是最短的方法。
()=>{}
答案 15 :(得分:0)
另一种方法是调用setState
, AND 保存状态:
this.setState(prevState=>({...prevState}));
答案 16 :(得分:0)
forceUpdate();
方法可以使用,但建议使用setState();
答案 17 :(得分:0)
为了完成您要描述的内容,请尝试this.forceUpdate()。
答案 18 :(得分:0)
您可以使用forceUpdate()获得更多详细信息,请检查https://reactjs.org/docs/react-component.html#forceupdate
答案 19 :(得分:0)
forceUpdate(),但是每次我听到有人谈论它时,都应该跟进它,永远不要使用它。