0

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
2
  • 1
    can you provide fiddle or code ? Commented Mar 15, 2020 at 10:23
  • Have added code, cheers. Commented Mar 15, 2020 at 10:50

2 Answers 2

3

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for your help. I've updated with the files in question. Since I'm using a class component I can't use hooks :-(
1

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

1 Comment

Oh man. I tried this earlier and I got and error cannot read property 'push' of undefined, but I've since learned about react-router-doms withRouther. That did the trick! Thanks so much for the nudge :rocket:

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.