0

I am trying out a react-redux sample code where I would like to add a course in one form upon clicking 'Add Course', I want to update the store and redirect to a new page with the list of courses.

But for some reason, the redirect happen after calling the redux action creator. It stays in the same page.

Any ideas how to redirect the results to a different page?

import React from "react";
import { connect } from "react-redux";
import * as courseActions from "../../redux/actions/courseActions";
import PropTypes from "prop-types";
import { bindActionCreators } from "redux";
import history from './history'


class CoursesPage extends React.Component {
  state = {
    course: {
      title: "",
    },
  };

  handleSubmit = (event) => {
    event.preventDefault();
    this.props.actions.loadCourses.createCourse(this.state.course).then(() => {
      alert('Inside Promise') 
      history.push('/AllCourses'); //This doesn't get executed.
  };

  render() {
    return (
      <div>
        <form onSubmit={this.handleSubmit}>
          <h2>Courses</h2>
          <h3>Add Course</h3>
          <input type="submit" value="Add Course" />
          {this.props.courses.map((course) => (
            <div key={course.title}>{course.title}</div>
          ))}
        </form>
        <hr />
        
      </div>
    );
  }
}

CoursesPage.propTypes = {
  courses: PropTypes.array.isRequired,
  actions: PropTypes.object.isRequired,
};

function mapStateToProps(state) {
  return {
    courses: state.courses,
  };
}

function mapDispatchToProps(dispatch) {
  return {
    actions: {
      loadCourses: bindActionCreators(courseActions, dispatch),
    },
  };
}

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


Action Code:

import * as types from "./actionTypes";

export function createCourse(course) {
  return { type: types.CREATE_COURSE, course };
}


Reducer:

import * as types from "../actions/actionTypes";

export default function courseReducer(state = [], action) {
  debugger;
  switch (action.type) {
    case types.CREATE_COURSE:
      return [...state, { ...action.course }];
    default:
      return state;
  }
}

history.js

import createHistory from 'history/createHashHistory'

export default createHistory()

bigskull
  • 739
  • 2
  • 13
  • 23

1 Answers1

0

You can create a custom middleware to do so:

const hasAddedData = (state) => {
    // Your checks    
}

const redirectMiddleware = history => storeAPI => next => action => {
    console.log('dispatching', action)
    const result = next(action);
    // If the action is the one you want to trigger the redirect (action.type)
    // and the state pass the checks (storeAPI.getState()),
    // do the redirect.
    //
    // This would be like:
    if (action.type === types.CREATE_COURSE && hasAddedData(storeAPI.getState())) {
        history.push(destinationPath);
    }
    // You must return the result of next(action) to avoid breaking Redux.
    return result;
}

And wherever you create your Redux Store:

// history should already be provided by React Router.
const middlewareEnhancer = applyMiddleware(redirectMiddleware(history))

const store = createStore(yourRootReducer, middlewareEnhancer)

If you need to check the previous state too, just set a const with storeAPI.getState() before running next(action). You can expand your middleware with other redirect checks or scenarios you need for additional actions.

WARNING: I wanted to give you the vanilla code solution, but keep in mind these three things:

  • This is a task that is probably better and opinionatedly made by a library (check connected-react-router).
  • Also, instead of making a custom middleware for action specific tasks, you can use a widely accepted middleware library such as redux-saga.
  • Think about your app workflow. Do you need additional state properties (done flags, selection properties...)? Are all the CREATE_COURSE actions going to redirect or only a fraction of them? Will a specific REDIRECT action make things easier for you? Do you really need an imperative redirect or would it be possible, with the right state structure, a declararive Redirect with React Router component?