Andypants
Andypants

Reputation: 3

Redirect React form component on submit. React Router v5.1

The goal - I have a form that is used to update product details. I want to redirect to home on form submit.

The problem is that I'm sending form data up from my Form component to the main App component to update the list of products. The products are in my App state, and passed as a prop to my Form, which causes a re-render of the Form component.

Form has a flag in state that is set to false in the constructor: toHome: false. My plan being to utilise react-router's Redirect to fire when the flag is set to true.

In the EditForm handleSubmit method there's a setState({ toHome: true }). But ALSO in handleSubmit is the method (passed from App as a prop) to update the products in App state.

TLDR -> Form handleSubmit updates products -> Form re-renders -> redirect flag (toHome) set to false in constructor -> redirect never happens.

I have the same problem even if I want to add a short success message. I've been stuck on this for wayyy too long.

EditForm.js

import React, { Component } from 'react'
import { Link, Redirect } from 'react-router-dom'
import PropTypes from 'prop-types'

class EditForm extends Component {
  constructor(props) {
    super(props)
    this.state = {
      product: {
        id: '',
        name: '',
        description: '',
        price: {
          base: '',
          amount: ''
        },
        relatedProducts: []
      },
      productIndex: '',
      toHome: false
    }
  }

  componentDidMount() {
    const { productId, allProducts } = this.props
    const thisProduct = this.findProduct(productId, allProducts)
    const thisIndex = this.findIndex(productId, allProducts)

    this.setState({ product: { ...thisProduct }, productIndex: thisIndex })
  }

  .........

  handleSubmit(e) {
    e.preventDefault()
    const newAllProducts = this.mergeProducts()
// this is the method that's firing the re-render
    this.props.updateProducts(newAllProducts)
// and here is the ill-fated setState
    this.setState({ toHome: true })
  }

  render() {
    const { id, name, description, price, toHome } = this.state.product
    console.log(this.state.toHome)
    if (toHome) {
      return <Redirect to="/" />
    }

    return (
      <div className="edit-form">
       ....form...
        </div>
      </div>
    )
  }
}

EditForm.propTypes = {
  productId: PropTypes.number.isRequired,
  allProducts: PropTypes.array.isRequired,
  userCurrency: PropTypes.string.isRequired,
  updateProducts: PropTypes.func.isRequired
}

export default EditForm

App.js

import React, { Component } from 'react'
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'

import ProductList from './ProductList'
import SingleProduct from './SingleProduct'
import Header from './Header'
import EditForm from './EditForm'
import Footer from './Footer'

import allProducts from '../data/products.json'
import '../scss/index.scss'

class App extends Component {
  constructor() {
    super()

    this.state = {
      selectedCurrency: 'AUD',
      products: []
    }
  }

  componentDidMount() {
    this.setState({
      products: this.getLocalProducts()
    })
  }

  // Checks localStorage for existing product data.
  // If none, initiates localStorage with raw products data
  // and returns raw products to be added to state.
  getLocalProducts() {
    const localData = localStorage.getItem('allProducts')
    if (localData !== 'undefined' && localData !== null) {
      return JSON.parse(localData)
    } else {
      localStorage.setItem('allProducts', JSON.stringify(allProducts))
      return allProducts
    }
  }

  // Updates localStorage and state.
  updateProducts = data => {
    localStorage.setItem('allProducts', JSON.stringify(data))
    this.setState({ products: data })
  }

  clearRedirect = () => {
    this.setState({ redirect: false })
  }

  handleCurrencyChange(e) {
    this.setState({ selectedCurrency: e.value })
  }

  render() {
    const { selectedCurrency, products } = this.state

    return (
      <Router>
        <div className="app">
          <Header
            handleChange={e => this.handleCurrencyChange(e)}
            userCurrency={selectedCurrency}
          />
          <Switch>
            <Route
              path="/products/:id/edit"
              component={({ match }) => (
                <EditForm
                  productId={parseInt(match.params.id)}
                  userCurrency={selectedCurrency}
                  allProducts={products}
                  updateProducts={this.updateProducts}
                />
              )}
            />
            <Route
              path="/products/:id"
              component={({ match }) => (
                <SingleProduct
                  productId={parseInt(match.params.id)}
                  allProducts={this.getLocalProducts()}
                  userCurrency={selectedCurrency}
                />
              )}
            />
            <Route
              path="/"
              exact
              component={() => (
                <ProductList
                  allProducts={products}
                  userCurrency={selectedCurrency}
                />
              )}
            />
          </Switch>
          <Footer />
        </div>
      </Router>
    )
  }
}

export default App

Upvotes: 0

Views: 7006

Answers (2)

hugholousk
hugholousk

Reputation: 169

use this.props.history.push('/') to redirect to your homepage instead

Upvotes: 1

Beowulf
Beowulf

Reputation: 420

I think there is some clarity needed from you regarding the impl, so please provide a fiddle or something.

In the meantime, what I am guessing is, that you need to redirect on your form submit?

If that is the case,

import { useHistory } from "react-router-dom"; 

and do

  let history = useHistory();
  function handleSubmit() {
    props.handleSubmit() // the one that comes from your App
    history.push("/home");
  }

Once you provide more info, I will correspondingly edit the answer.

Upvotes: 3

Related Questions