222

What is the angular's $watch function equivalent in React.js?

I want to listen state changes and call a function like getSearchResults().

componentDidMount: function() { this.getSearchResults(); } 

9 Answers 9

378

The following lifecycle methods will be called when state changes. You can use the provided arguments and the current state to determine if something meaningful changed.

componentWillUpdate(object nextProps, object nextState) componentDidUpdate(object prevProps, object prevState) 
Sign up to request clarification or add additional context in comments.

7 Comments

Was needing to trigger a method when a property in another component was updated (up one via callback, down one via prop), and adding componentDidUpdate in the component with the prop was the right prescription. Thanks for this.
I think this is the best way to guarantee notification of state changes, which is what the OP asked. But note that neither of these methods will fire after a state change if you veto the update in shouldComponentUpdate.
componentWillUpdate is being deprecated: reactjs.org/blog/2018/03/27/update-on-async-rendering.html
will componentDidUpdate not also fire when receiving new props, not necessarily just when state changes?
componentWillUpdate is deprecated.
|
106

In 2020 you can listen to state changes with the useEffect hook like this

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 } 

5 Comments

but how I can use it for the class components?
@YuriiSpace with the corresponding componentDidUpdate(), componentDidMount, etc. hooks..
This usage of useEffect itself always causes an additional render – is there a way to avoid that?
@Philzen if you pass a dependency array as the second parameter of useEffect (i.e [myState] in the example above) the component will only rerender if one of the variables in the dependency array has changed.
52

I haven't used Angular, but reading the link above, it seems that you're trying to code for something that you don't need to handle. You make changes to state in your React component hierarchy (via this.setState()) and React will cause your component to be re-rendered (effectively 'listening' for changes). If you want to 'listen' from another component in your hierarchy then you have two options:

  1. Pass handlers down (via props) from a common parent and have them update the parent's state, causing the hierarchy below the parent to be re-rendered.
  2. Alternatively, to avoid an explosion of handlers cascading down the hierarchy, you should look at the flux pattern, which moves your state into data stores and allows components to watch them for changes. The Fluxxor plugin is very useful for managing this.

4 Comments

But what about when you need to fetch remote data that use (part of) the state as an url / a parameter?
@RnMss - look at redux and reducers (redux.js.org/docs/basics/Reducers.html) and also reselect (github.com/reactjs/reselect).
Good catch. Solving it with redux is the right and correct approach, especially for large scale projects and multi-team development.
The question is not about React 'listening' and re-rendering the dom. The question is about how a user can listen for a change in state (for example if the state count changes from 20 to 21) and run some code when it changes. useEffect does this in React hooks. Not sure what the mechanism was before React hooks, or if there was one.
38

Since React 16.8 in 2019 with useState and useEffect Hooks, following are now equivalent (in simple cases):

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

Comments

31

I think you should be using below Component Lifecycle as if you have an input property which on update needs to trigger your component update then this is the best place to do it as its will be called before render you even can do update component state to be reflected on the view.

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

1 Comment

componentWillReceiveProps has been deprecated: reactjs.org/docs/…
14

If you use hooks like const [ name , setName ] = useState (' '), you can try the following:

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

1 Comment

is there any way to get old value in useEffect ? suppose name was empty at first then i type I in it then in useEffect I will get i but what if I want to get previous state any Idea
5

Using useState with useEffect as described above is absolutely correct way. But if getSearchResults function returns subscription then useEffect should return a function which will be responsible for unsubscribing the subscription . Returned function from useEffect will run before each change to dependency(name in above case) and on component destroy

Comments

2

It's been a while but for future reference: the method shouldComponentUpdate() can be used.

An update can be caused by changes to props or state. These methods are called in the following order when a component is being re-rendered:

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

ref: https://reactjs.org/docs/react-component.html

1 Comment

You'll have to return a boolean from shouldComponentUpdate so it might not be suitable for this use case.
0

I use this code to see which one in the dependencies changes. This is better than the pure useEffect in many cases.

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

And in my React component

// 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 ]); 

2 Comments

Why is this better than the pure useEffect in many cases, and which cases are these?
@ggorlen the pure useEffect hook doesn't tell you which dependencies are updated/changed, but my custom useWatchStateChange hook gives more details over which dependencies are changes so we have more control over the logic in the code.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.