Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React components lifecycle, state and redux

I would like to use redux to store the state of my whole react application, however I am stuck with a particular case:

  • what to do with redux when the component needs a local state, modified by lifecycle methods like componentDidUpdate or componentDidMount ?

Example of a react component to contain "cards" arranged by isotope layout library:

componentDidMount() {
    let container = ReactDOM.findDOMNode(this);
    if (! this.state.isotope) {
        this.setState({ isotope: new Isotope(container, {itemSelector: '.grid-item', layoutMode: 'masonry'})});
    }
}

componentDidUpdate(new_props, new_state) {
    if (new_state.items_list != this.state.items_list) {
        if (this.state.isotope) {
            this.state.isotope.reloadItems();
            this.state.isotope.layout();
            this.state.isotope.arrange();
        }
    }
}

Is there a way to remove the local state in this component and to use redux instead ? I can't see how to do it

like image 967
mguijarr Avatar asked Nov 24 '15 22:11

mguijarr


1 Answers

Put your items_list in your redux state. Your reducer might look like this:

const initialState = {
    items: []
};

export function myReducer(state = initialState, action) {
    switch (action.type) {
        case 'SET_ITEMS':
            return Object.assign({}, state, {
                items: action.items
            });
    }
    return state;
}

Or for something a little more complex:

const initialState = {
    items: []
};

export function myReducer(state = initialState, action) {
    switch (action.type) {
        case 'ADD_ITEM':
            return Object.assign({}, state, {
                items: [ ...state.items, action.item ]
            });
        case 'REMOVE_ITEM':
            return Object.assign({}, state, {
                items: [
                    ...state.items.slice(0, action.index),
                    ...state.items.slice(action.index + 1)
                ]
            });
    }
    return state;
}

Once you've configured your store and Provider (see the Redux docs), set up your "smart component" like so:

function mapStateToProps(state) {
    return {
        items: state.items
    }
}

function mapDispatchToProps(dispatch) {
    const actions = bindActionCreators(actionCreators, dispatch);
    return {
        addItem: actions.addItem,
        removeItem: actions.removeItem
    };
}

export default connect(mapStateToProps, mapDispatchToProps)(Root);

Now your items and actions are props to your Root component. If your items live in a lower order component, simply pass them down the tree as props.

I hope that gives you the general idea. With Redux what you'll find that your React components will use state a lot less and props a lot more.

One more thing...

This might be a minor matter, but I urge you NOT to store your isotope object on the component state. (Regardless of whether or not you use Redux.) The isotope object isn't really a piece of state, it's your view. In React, a component updates in response to a change in state. But your componentDidUpdate does the reverse: it changes the state in response to a component update.

As an alternative, simply store it on the object itself. i.e.

componentDidMount() {
    const container = ReactDOM.findDOMNode(this);
    this.isotope = new Isotope(container, {
        itemSelector: '.grid-item',
        layoutMode: 'masonry'
    });
}

componentDidUpdate(prevProps, prevState) {
    if (prevProps.items !== this.props.items) {
        this.isotope.reloadItems();
        this.isotope.layout();
        this.isotope.arrange();
    }
}

(Whilst normally I would recommend against against using these sort of instance variables in React, DOM manipulation libraries like Isotope are a worthy exception.)

like image 172
David L. Walsh Avatar answered Sep 22 '22 06:09

David L. Walsh