Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redux & React Router: Combing dispatch and navigation (history.push)

I'm a bit clueless as to how to use React Router's history.push('/route') with Redux.

In other words, if you connect a component with Redux's mapDispatchToProps, how do you put history.push('/route') that is supposed to be fired after dispatching a certain action?

To clarify the issue, here is a snippet of my experimentation...

class PostContainer extends React.Component {

    handleSubmit(data) {
        return this.props.addPost(data);
    }

    render() {
        return (<Post onSubmit={data=> this.handleSubmit(data)}/>);
    }
}

const mapDispatchToProps = (dispatch) => {
    return {    
        addPost: (data) => {
            dispatch(addPost(data)).data.then((result) => {
                dispatch(addPostFulfilled(result.data));

                //How do you call the following function?
                this.props.history.push('/posts')
            }).catch((error) => {
                dispatch(addPostRejected());
            });
        },      
    }
}

export default connect(null, mapDispatchToProps)(PostContainer);

As you can see, as soon as a post is added into Redux's state through addPostFulfilled, the page needs to move to /posts via history.push.

According to Redux's doc, containers are made for updating states (as well as fetching data). So, dumb components are not supposed to put anything like history.push.

What would be a better approach than the code above?

Any advice will be appreciated.

like image 434
Hiroki Avatar asked Dec 24 '17 03:12

Hiroki


People also ask

What is a Redux?

Redux is a post-positive adjective meaning 'brought back, restored' (from Latin reducere, 'to bring back') used in literature, film and video game titles.

What is Redux and how it works?

Redux is a pattern and library for managing and updating application state, using events called "actions". It serves as a centralized store for state that needs to be used across your entire application, with rules ensuring that the state can only be updated in a predictable fashion.

Is Redux frontend or backend?

It should be clear that Redux can be used for the client side (frontend) with user interfaces. However, since Redux is just JavaScript, it can also be used on the server side (backend).

What is React VS Redux?

Redux manages state and state transformations and is often used with React, but React has its own concept of state. When using these libraries, it's good to know which to use when. Even if you choose to use Redux in your project, you will still need to make decisions on how much of your data is stored in Redux.


2 Answers

You can either user react-router-redux (require an initial set up) and dispatch your action from action creators

import { push } from 'react-router-redux'
class PostContainer extends React.Component {

  handleSubmit(data) {
    return this.props.addPost(data);
  }

  render() {
    return (
      <div>
        <Post onSubmit={data=> this.handleSubmit(data)}/>
      </div>
    );
  }
}

const mapDispatchToProps = (dispatch) => {
  return {    
    addPost: (data) => {
      dispatch(addPost(data)).data.then((result) => {
        dispatch(addPostFulfilled(result.data));

        //How do you call the following function?
        dispatch(push('/posts'));
      }).catch((error) => {
        dispatch(addPostRejected());
      });
    },      
  }
}
export default connect(null, mapDispatchToProps)(PostContainer);

or just pass push as a "done callback".

class PostContainer extends React.Component {

  handleSubmit(data, done) {
    return this.props.addPost(data, done);
  }

  render() {
    const { push } = this.props.history;
    return (
      <div>
        <Post onSubmit={data=> this.handleSubmit(data, push)}/>
      </div>
    );
  }
}

const mapDispatchToProps = (dispatch) => {
  return {    
    addPost: (data, done) => {
      dispatch(addPost(data)).data.then((result) => {
        dispatch(addPostFulfilled(result.data));

        //How do you call the following function?
        done('/posts')
      }).catch((error) => {
        dispatch(addPostRejected());
      });
    },      
  }
}

export default connect(null, mapDispatchToProps)(PostContainer);
like image 59
Denis Rybalka Avatar answered Oct 18 '22 09:10

Denis Rybalka


A cleaner approach might be to use React Router's <Redirect>:

class PostContainer extends React.Component {
  render() {
    if (userJustAddedAPostRightNow === true) {
      return <Redirect to='/posts' />
    }
    return (
      <div>
        <Post onSubmit={addPost}/>
      </div>
    );
  }
}
export default connect(null, { addPost })(PostContainer);

addPost would trigger a change of state if successful. It should changeuserJustAddedAPostRightNow to true, so that you'll be redirected to the posts' page.

To avoid all those nested callbacks you can follow Nir Kaufman's approach and handle logic in middlewares:

//src/middlewares/post.js
export const newPost = ({dispatch}) => next => action => {
  next(action);

  if (action.type === ADD_POST) {
    dispatch(apiRequest('POST', action.URL, null, ADD_POST_SUCCESS, ADD_POST_ERROR));
    dispatch(showSpinner());
  }
};

Then, on success, ADD_POST_SUCCESS action would change, via your reducer, the value of userJustAddedAPostRightNow.

like image 35
Federico Avatar answered Oct 18 '22 10:10

Federico