React-Router: Programmatic Navigation

Contents

GitHub Repos

๐Ÿ’พ Ncoughlin: React-Streams-Client

๐Ÿ’พ Ncoughlin: React-Streams-API

Intro

If we consider navigation when a user clicks on links to be Intentional Navigation, we are now going to look at Programmatic Navigation, where we force the application to navigate to a new location based on a set of triggers, like form submission.

We will be learning this within the context of our Twitch Clone application called โ€œGlitchโ€

The question of when to programmatically navigate the user is an important one, and will be context specific. In this example we will be re-routing the user after they have successfully created a stream in our Twitch Clone. Therefore we want to wait until we have received a successful status code from the server before we re-route the user. Some flash messages would be helpful also, but we will be getting into those at a later time.

In this case, in my current opinion, the best way to accomplish this would be to use the React Router Redirect component, which is built for just this purpose.

๐Ÿ”— React-Router: Redirect

If we combine that with all of the props that are available to us thanks to Redux-Form, we can take advantage of the submitSucceeded prop and implement this re-direct quite easily.

components/streams/CreateStream.js
import React, { Component } from "react";
import { Field, reduxForm } from "redux-form";
import { connect } from "react-redux";
import { Redirect } from "react-router-dom";

import { createStream } from "../../actions";
class StreamCreate extends Component {
  renderError({ error, touched }) {
    if (touched && error) {
      return <div className="ui pointing label">{error}</div>;
    }
  }

  // must be arrow function so that context of this is bound for this.renderError
  renderInput = ({ input, label, meta }) => {
    return (
      <div className="field">
        <label>{label}</label>
        <input {...input} />
        {this.renderError(meta)}
      </div>
    );
  };

  // send post request to api server on submit
  onSubmit = (formValues) => {
    this.props.createStream(formValues);
  };

  render() {
    // check for successful form submission
    // redirect to index if true
    if (this.props.submitSucceeded === true) {      return <Redirect to="/" />;    }
    return (
      <form
        className="ui form"
        onSubmit={this.props.handleSubmit(this.onSubmit)}
      >
        <Field
          name="title"
          component={this.renderInput}
          label="Enter Title: "
        />
        <Field
          name="description"
          component={this.renderInput}
          label="Enter Description: "
        />
        <button className="ui button primary">Submit</button>
      </form>
    );
  }
}

const validate = (formValues) => {
  const errors = {};

  if (!formValues.title) {
    errors.title = "You must enter a title";
  }

  if (!formValues.description) {
    errors.description = "You must enter a description";
  }

  return errors;
};

// form wrapper
const formWrapped = reduxForm({
  form: "createStream",
  validate: validate,
})(StreamCreate);


// redux connect
export default connect(null, { createStream })(formWrapped);

It is worth noting however that there is disagreement on this issue. There is one school of thought that you should accomplish this by messing with the React-Router History.

There is a good article that discusses the difference between the two methods here.

๐Ÿ”— ui.dev:React Router v4 Programmatically navigate

It seems to me that manipulating the history is a โ€œhackyโ€ way of accomplishing this, and is not preferable in newer versions.