How to listen for specific property changes in Redux store after an action is dispatched
Asked Answered
S

3

87

I am currently trying to conceptualize how to handle dispatching an action in a component based on a data change after a dispatch in another component.

Take this scenario:

dispatch(someAjax) -> property in state updates.

After this, I need another component dependent on this same property to know that is has updated and dispatch an action based on the new value.

Rather than using some type of value.on(change... solution, what is the preferred way to handle this type of action 'cascading'?

Showboat answered 11/4, 2016 at 19:2 Comment(0)
K
72

There are two basic approaches: either a middleware that diffs the state after an action is done, or using Redux's low-level store.subscribe API.

The Redux FAQ has an answer that covers this. Also, I keep a categorized list of Redux-related addons and utilities, and that includes a group of existing store change subscription libraries that implement various approaches to listening for data changes.

Kantar answered 11/4, 2016 at 20:6 Comment(4)
Thanks for that list, it's really helpful. What I am actually trying to figure out is the best way to inform several components that some data is back from an AJAX call and that X key has changed. Each of these components would then possibly fire their own actions. I can get at the changes in middleware, but the part I am trying to figure out is what to do when the change is detected. I feel like some sort of event bus is needed, but that doesn't feel right with Redux. Any advice there?Showboat
The primary recommended approach for communication in React and Redux is parents passing props to children, and children running callbacks to notify parents. However, other approaches like event buses certainly have their uses, and if an event bus fits your use case, go for it. You may want to look at ctheu.com/2015/02/12/… and andrewhfarmer.com/component-communication for examples.Kantar
Although you didn't specifically answer the question, the first item on your list did work for my case. redux-watch is a simple and creative way to get this functionality. Thanks again for the list, it is helpful.Showboat
Yeah, I recently split the FAQ page into separate pages per category. Just updated the link in my answer to match. Thanks!Kantar
O
107

You may use Redux's mapStateToProps and connect with React's componentDidUpdate(prevProps, prevState, snapshot) hook.

So basically your code could look like this:

const mapStateToProps = (state) => ({ 
   specificProperty: state.specificProperty,
   // any props you need else
});

class YourComponent extends React.Component {
    render() {
      // render your component  
    }

    componentDidUpdate(prevProps, prevState, snapshot) {
        if (prevProps.specificProperty !== this.props.specificProperty) {
            // Do whatever you want
        }
    }
}

YourComponent = connect(mapStateToProps)(YourComponent);
Only answered 25/6, 2017 at 21:40 Comment(8)
Fits the provided tags, thenEmbrocate
Nice. Also, possible typo? Do you mean nextProps.specificProperty !== this.props.specificProperty?Equinoctial
@BrunoCarvalhal, if you don't use react there's probably a better option, depending on your JS Framework, than redux ;)Equilibrist
Minor update to this, componentWillUpdate should no longer be used with React 16. getDerivedStateFromProps is now the recommended approach.Lenten
Depends - could be getDerivedStateFromProps if you want new state, but could be you want to use componentDidUpdate for a bit more versatility.Novanovaculite
I used this approach and worked for me like a charmGerda
"You may use Redux's mapStateToProps" - this is incorrect: mapStateToProps is in Redux-React, not redux.js - remember you can use Redux without using React, and if you're using Redux without React then you don't get mapStateToProps.Egomania
This approach worked great for me too. To use with functional components, I changed componentDidUpdate to useEffect(() => { /* Do whatever you want */ }, [props.specificProperty]) with mapStateToProps defined as an interface and set as my component propsBoucher
K
72

There are two basic approaches: either a middleware that diffs the state after an action is done, or using Redux's low-level store.subscribe API.

The Redux FAQ has an answer that covers this. Also, I keep a categorized list of Redux-related addons and utilities, and that includes a group of existing store change subscription libraries that implement various approaches to listening for data changes.

Kantar answered 11/4, 2016 at 20:6 Comment(4)
Thanks for that list, it's really helpful. What I am actually trying to figure out is the best way to inform several components that some data is back from an AJAX call and that X key has changed. Each of these components would then possibly fire their own actions. I can get at the changes in middleware, but the part I am trying to figure out is what to do when the change is detected. I feel like some sort of event bus is needed, but that doesn't feel right with Redux. Any advice there?Showboat
The primary recommended approach for communication in React and Redux is parents passing props to children, and children running callbacks to notify parents. However, other approaches like event buses certainly have their uses, and if an event bus fits your use case, go for it. You may want to look at ctheu.com/2015/02/12/… and andrewhfarmer.com/component-communication for examples.Kantar
Although you didn't specifically answer the question, the first item on your list did work for my case. redux-watch is a simple and creative way to get this functionality. Thanks again for the list, it is helpful.Showboat
Yeah, I recently split the FAQ page into separate pages per category. Just updated the link in my answer to match. Thanks!Kantar
L
0

Stumbled across this question and noticed the top answer is outdated with componentDidUpdate, etc. I solved this using "@reduxjs/toolkit": "^1.8.4" and "react-redux": "^8.0.2" as follows:

For my redux store:

import { configureStore } from '@reduxjs/toolkit';
import { loadState, saveState, REDUX_KEY } from '../common';
import createReducer from './reducers'; // createReducer from '@reduxjs/toolkit


const store = configureStore({
    reducer: createReducer,
    preloadedState: loadState(REDUX_KEY), // loadState: JSON.parse() of store saved in localStorage
});

store.subscribe(() => saveState(REDUX_KEY, store.getState())); // saveState: JSON.stringify() and saves store to localStorage

export default store;

My index.tsx:

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import Application from './app';
import store from './redux/store';

ReactDOM.render(
    <React.StrictMode>
        <Provider store={store}>
            <Application />
        </Provider>
    </React.StrictMode>,
    document.getElementById('root'),
);

And then in a (usually top-level) application component in which I wanted to watch for changes in the redux store, I would have something like:

import React from 'react';
import { useSelector } from 'react-redux';
import { selectLoginStatus } './redux/selectors';

function LoginWatcher() {
    const loginStatus = useSelector(selectLoginStatus);

    // watch login status
    React.useEffect(() => {
        // perform action here when the state of 'status' updates
    }, [loginStatus]);

    return null;
}
Leastwise answered 5/6, 2024 at 21:22 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.