如何在react.js中监听状态更改?

215
在React.js中,与Angular的$watch函数相当的是什么?
我想监听状态变化并调用像getSearchResults()这样的函数。
componentDidMount: function() {
    this.getSearchResults();
}
9个回答

376

当状态改变时,以下生命周期方法将被调用。您可以使用提供的参数和当前状态来确定是否发生了有意义的更改。

componentWillUpdate(object nextProps, object nextState)
componentDidUpdate(object prevProps, object prevState)

7
当另一个组件中的属性被更新时(通过回调上升一个属性,通过prop下降一个属性),需要触发一个方法,将componentDidUpdate添加到拥有该属性的组件中是正确的解决方案。感谢您提供这个方法。 - Keith DC
49
componentWillUpdate 正在被废弃:https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html - Dmitry Minkovsky
2
当收到新的props时,componentDidUpdate是否也会触发,而不仅仅是在状态改变时? - andy mccullough
7
componentWillUpdate已被弃用。 - sean
我认为使用钩子应该可以解决所有问题。 - Aravind Reddy
显示剩余2条评论

91
在2020年,您可以使用useEffect钩子来监听状态变化,就像这样。
export function MyComponent(props) {
    const [myState, setMyState] = useState('initialState')

    useEffect(() => {
        console.log(myState, '- Has changed')
    },[myState]) // <-- here put the parameter to listen, react will re-render component when your state will be changed
}

9
但我如何将它用于类组件? - Yurii Space
@YuriiSpace 使用相应的componentDidUpdate()、componentDidMount等钩子。 - MMMM
3
useEffect 的这种用法本身总是会导致额外的渲染 - 有没有什么方法可以避免这种情况? - Philzen
2
@Philzen 如果你将依赖数组作为 useEffect 的第二个参数传递进去(例如上面的例子中的 [myState]),那么只有在依赖数组中的变量发生改变时,组件才会重新渲染。 - Ryan Schafer

52

我没有使用过Angular,但是阅读上面的链接,似乎你正在尝试编写不需要处理的代码。您可以在React组件层次结构中更改状态(通过this.setState())并且React将导致重新渲染您的组件(有效地“监听”更改)。

  1. 通过父组件传递处理程序(通过props)并让它们更新父组件的状态,从而导致父组件以下的组件层次结构被重新渲染。
  2. 或者,为了避免处理程序在层次结构中级联扩大,应该查看flux模式,它将状态移动到数据存储中,并允许组件观察其更改。 Fluxxor插件非常有用。

6
当您需要获取远程数据并将状态的一部分用作URL /参数时,该怎么办? - RnMss
@RnMss - 看看redux和reducers(http://redux.js.org/docs/basics/Reducers.html),还有reselect(https://github.com/reactjs/reselect)。 - edoloughlin
很好的发现。 使用redux来解决它是正确的方法,特别是对于大型项目和多团队开发。 - Slavik Meltser
1
问题不在于React如何“监听”并重新渲染DOM。问题在于用户如何监听状态的变化(例如,如果状态count20变为21),并在其发生变化时运行一些代码。 useEffect在React hooks中实现了这一点。不确定在React hooks之前机制是什么,或者是否存在这样的机制。 - Marius

38

自2019年React 16.8推出 useStateuseEffect Hooks后,在简单情况下,以下几个框架现在是等价的:

AngularJS:

$scope.name = 'misko'
$scope.$watch('name', getSearchResults)

<input ng-model="name" />

React:

const [name, setName] = useState('misko')
useEffect(getSearchResults, [name])

<input value={name} onChange={e => setName(e.target.value)} />

31

我认为你应该使用下面的组件生命周期。如果你有一个输入属性,需要在更新时触发组件更新,那么这是最好的位置,因为它将在渲染之前被调用,你甚至可以更新组件状态以反映在视图中。

componentWillReceiveProps: function(nextProps) {
  this.setState({
    likesIncreasing: nextProps.likeCount > this.props.likeCount
  });
}

8
componentWillReceiveProps已被弃用:https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops - John Skoumbourdis

14

如果你使用钩子(hooks)像是const [ name , setName ] = useState (''),你可以尝试以下内容:

 useEffect(() => {
      console.log('Listening: ', name);
    }, [name]);

2
在 useEffect 中是否有获取旧值的方法? 假设一开始 name 为空,然后我输入了 I。 那么在 useEffect 中,我将得到 i,但是如果我想要获取先前的状态,有什么办法吗? - Bhumit 070

5

使用上述描述的useEffect和useState是绝对正确的方式。但是,如果getSearchResults函数返回订阅,则useEffect应该返回一个函数,负责取消订阅。从useEffect返回的函数将在每次更改依赖项(在上面的例子中是name)之前运行,并在组件销毁时运行。


2

有一段时间没有更新了,但是作为以后的参考:可以使用方法shouldComponentUpdate()。

更改props或state会导致更新。当组件重新渲染时,这些方法按以下顺序调用:

static getDerivedStateFromProps() 
shouldComponentUpdate() 
render()
getSnapshotBeforeUpdate() 
componentDidUpdate()

参考文献:https://reactjs.org/docs/react-component.html

React组件是构建React应用程序的基本单元。每个组件都有自己的状态和生命周期方法,可以在组件内部进行处理。当组件的props或state发生变化时,React会自动重新渲染组件。您可以使用React.Component类来定义组件,并覆盖其生命周期方法以执行特定操作。


你需要从shouldComponentUpdate返回一个布尔值,因此它可能不适合这种情况。 - sean

0

我使用这段代码来查看依赖项中的哪个发生了变化。在许多情况下,这比纯粹的useEffect更好。

// useWatch.js
import { useEffect, useMemo, useRef } from 'react';

export function useWatchStateChange(callback, dependencies) {
    const initialRefVal = useMemo(() => dependencies.map(() => null), []);
    const refs = useRef(initialRefVal);
    useEffect(() => {
        for(let [index, dep] of dependencies.entries()) {
            dep = typeof(dep) === 'object' ? JSON.stringify(dep) : dep;
            const ref = refs.current[index];
            if(ref !== dep) {
                callback(index, ref, dep);
                refs.current[index] = dep;
            }
        }
    // eslint-disable-next-line react-hooks/exhaustive-deps
    }, dependencies);
}

在我的 React 组件中

// App.js
import { useWatchStateChange } from 'useWatch';
...
useWatchStateChange((depIndex, prevVal, currentVal) => {
  if(depIndex !== 1) { return } // only focus on dep2 changes
  doSomething("dep2 now changes", dep1+dep2+dep3);
}, [ dep1, dep2, dep3 ]);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接