Dynamic routing is not working while adding a new post

Viewed 183

While adding a post to my blog, and doing the dynamic route for that post, to show the details, I am not able to do that. Dynamic route only works for data which I kept on my state previously. Not worked for newly added data. this.props only show the data which I have kept in my state. Give me some suggestions. While adding a new vechile dynamic route showing loading. It is not fetching the new data from state.

App.js:

import React, { Component } from 'react'
import './App.css';
import Addview from './components/Addview'
import {BrowserRouter,Route, Switch} from 'react-router-dom'
import Add from './components/Add'
import Vechile from './components/Vechile';
import View from './components/View'


class App extends Component {
  render() {
    return (
      <BrowserRouter>
      <div className='App container'>
        <h2 className='text-primary text-decoration-underline'>Vehicle Information</h2>
        {/* <Addview /> */}
          <Route exact Path='/' component ={Addview} />
        <Switch>
          <Route path = '/add' component={Add} />
          <Route path = '/view' component={View} />
          <Route path = '/:id' component ={Vechile} />
        </Switch>
      </div>
      </BrowserRouter>
    )
  }
}

export default App

rootReducer.js:

const initState={
    vechileInfo:[
        {id:'1' , model_name:'AUDI' , vehchile_type: 'SUV', mileage:"5km/lt", top_speed:'250km/hr', cost:'30lakh', sales_units: 4},
        {id:'2' , model_name:'BMW' , vehchile_type: 'Hatchback', mileage:"7km/lt",top_speed:'320km/hr', cost:'40lakh', sales_units: 2},
        {id:'3' , model_name:'Mercidez' , vehchile_type: 'SUV', mileage:"6km/lt", top_speed:'220km/hr', cost:'25lakh', sales_units: 6}
    ]
   
}
const rootReducer = (state = initState,action) =>{
    console.log(action) 
    switch(action.type){
    case "DELETE_VECHILE":
        let newVechileInfo= state.vechileInfo.filter(vechile=>{
            return action.id!==vechile.id
        })
        return{
            ...state,
            vechileInfo : newVechileInfo
        }
        case "ADD_VECHILE":
            action.vechile.id=Math.random();
            let vechileInfoAdd=[...state.vechileInfo,action.vechile]
            console.log(action,vechileInfoAdd)
        return{
            ...state,
            vechileInfo: vechileInfoAdd
        }
        case "SORT_SALES":
            let sortvechileInfo = action.vechile.sort((a,b)=>{
                // console.log(action)
                return (a.sales_units - b.sales_units)
            })
            return{
                ...state,
                vechileInfo : sortvechileInfo
            }
    }
    
    return state
}

export default rootReducer

Addview.js:

import React from 'react'
import {Link,NavLink} from 'react-router-dom'

const Addview = () => {
    return (
        <div className='my-5'>
            <button type="button" class="btn btn-warning btn-lg"><Link to="/" className="text-decoration-none text-dark fw-bold">Go to Home</Link></button>
            <button type="button" class="btn btn-success btn-lg"><Link to="/add" className="text-decoration-none text-dark fw-bold">Add a vechile</Link></button>
            <button type="button" class="btn btn-danger btn-lg"><Link to="/view" className="text-decoration-none text-dark fw-bold">View Vechiles</Link> </button>
        </div>
    )
}

export default Addview

View.js:

import React, { Component } from 'react'
import { connect } from "react-redux"
import {Link} from 'react-router-dom'

class View extends Component {

    handleClick=(id)=>{
         this.props.deleteVechile(id)
        // console.log(id)
    }

    handleClickSort=()=>{
        this.props.sortSales(this.props.vechileInfo)
    }
    

    render() {
        // console.log(this.props)
        const { vechileInfo } = this.props;
        const vechileList = vechileInfo.length ? (
            vechileInfo.map(vechile => {
                return(      
                    <tbody key={vechile.id}>    
                        <tr> 
                            <td><Link to ={'/' + vechile.id} className='text-decoration-none'>{vechile.model_name}</Link> </td>  
                            <td>{vechile.vehchile_type}</td>
                            <td>{vechile.mileage}</td>
                            <td>{vechile.top_speed}</td>
                            <td>{vechile.cost}</td>
                            <td>{vechile.sales_units}</td>
                            <td onClick={()=>{this.handleClick(vechile.id)}} ><a className='text-danger' href='#'>X</a></td>    
                        </tr>
                    </tbody>
                       
                )
            })
        ) : (
            
            <div className="text-dark">No vechile to display. Please add a vechile</div>
        )
        return (
            <div className='container'>
                <table className="table table-dark table-hover ">
                    <thead>
                        <tr>
                            <th scope="col">Model Name</th>
                            <th scope="col">Vechile Type</th>
                            <th scope="col">Top Speed</th>
                            <th scope="col">Mileage</th>
                            <th scope="col">Cost</th>
                            <th onClick={this.handleClickSort} scope="col">Sales Units</th>
                            <th scope="col"></th>
                        </tr>
                    </thead>
                {vechileList}
                </table>
            </div>
        )
    }
}

const mapStateToProps = (state,ownProps) => {
    console.log(ownProps)
    return {
        vechileInfo: state.vechileInfo
    }
}

const mapDispatchToProps = (dispatch) =>{   
    return{   
        deleteVechile: (id) =>{ dispatch({type: 'DELETE_VECHILE', id:id})},
        sortSales: (vechile) =>{dispatch({type:'SORT_SALES', vechile})}
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(View)

Add.js:

import React, { Component } from 'react'
import { connect } from "react-redux"

class Add extends Component {
    state={   
        model_name:'',
        vehchile_type:'',
        mileage:'',
        top_speed:'',
        cost:'',
        sales_units:''
    }
    handleChange=(e)=>{
        this.setState({
            [e.target.id]: e.target.value
        })
    }
    handleSubmit=(e)=>{
        e.preventDefault();
        // console.log(this.state)
        this.props.addVechile(this.state)
        this.props.history.push('/View')
    }
    render() {
        return (
            <div>
                <form className="row g-3" onSubmit={this.handleSubmit}>
                    <div className="col-md-6">
                        <label for="model_name" className="form-label">Model Name</label>
                        <input type="text" className="form-control" id="model_name" required onChange={this.handleChange}/>
                    </div>
                    <div className="col-md-6">
                        <label for="vehchile_type" className="form-label">Vechile Type</label>
                        <input type="text" className="form-control" id="vehchile_type" required onChange={this.handleChange}/>
                    </div>
                    <div className="col-md-6">
                        <label for="mileage" className="form-label">Mileage</label>
                        <input type="text" className="form-control" id="mileage" required onChange={this.handleChange}/>
                    </div>
                    <div className="col-md-6">
                        <label for="top_speed" className="form-label">Top Speed</label>
                        <input type="text" className="form-control" id="top_speed" required onChange={this.handleChange}/>
                    </div>
                    <div className="col-md-6">
                        <label for="cost" className="form-label">Cost</label>
                        <input type="text" className="form-control" id="cost" required onChange={this.handleChange}/>
                    </div>
                    <div className="col-md-6">
                        <label for="sales_units" className="form-label">Sales unit in FY20-21</label>
                        <input type="text" className="form-control" id="sales_units" required onChange={this.handleChange}/>
                    </div>   
                    <button type="submit" className="btn btn-info col-md-4 offset-md-4 justify-content-center">Login</button>
                </form>
            </div>
        )
    }
}

const mapDispatchToProps = (dispatch) =>{   
    return{   
        addVechile: (vechile) =>{ dispatch({type: 'ADD_VECHILE', vechile})}
    }
}

 export default connect(null,mapDispatchToProps)(Add)

Vechile.js:

import React, { Component } from 'react'
import {connect} from 'react-redux'

class Vechile extends Component {

    render() {
        console.log(this.props.vechile)
        const vechile =this.props.vechile ? (
            <div>
                <h4>Model Name: {this.props.vechile.model_name}</h4>
                <h4>Vechile Type: {this.props.vechile.vehchile_type}</h4>
                <h4>Mileage: {this.props.vechile.mileage}</h4>
                <h4>Top Speed: {this.props.vechile.top_speed}</h4>
                <h4>Cost: {this.props.vechile.cost}</h4>
                <h4>Sales in unit for FY20-21: {this.props.vechile.sales_units}</h4>
            </div>
        ):(
            <div>Loading Data....</div>
        )
        return (
            <div>
                <span>{vechile}</span>
            </div>
        )
    }
}

const mapStateToProps=(state,ownProps)=>{
    console.log(state,ownProps)
    let id= ownProps.match.params.id;
    console.log(id)
    return{
        vechile: state.vechileInfo.find(vechile=>{
            return vechile.id===id
        })
    }
}
export default connect(mapStateToProps)(Vechile)
1 Answers

Issue

When you add a new vehicle you navigate to "/View", not a path that includes a a vehicle id, like "/1234". What I suspect is occurring here is that your router has only a "/view" path, not "/View" and so it is not matched until the switch matches and renders your dynamic "/:id" and id is assigned the value "View" which likely won't match any saved vehicle id properties.

In Vechile component the injected vechile is simply undefined and so "Loading Data...." is rendered.

const mapStateToProps=(state,ownProps)=>{
    let id= ownProps.match.params.id;
    return{
        vechile: state.vechileInfo.find(vechile=>{
            return vechile.id === id // <-- vechile.id === "View" is false
        })
    }
}

Solution

If you are wanting to navigate to the vehicle details page then fix the navigation in Add component. You first need to add or generate an id property, and then navigate to the correct path.

import React, { Component } from 'react';
import { connect } from "react-redux";
import { v4 as uuidV4 } from 'uuid';

class Add extends Component {
  state={   
    model_name:'',
    vehchile_type:'',
    mileage:'',
    top_speed:'',
    cost:'',
    sales_units:''
  }

  handleChange = (e) => {
    this.setState({
      [e.target.id]: e.target.value
    })
  }

  handleSubmit=(e)=>{
    e.preventDefault();
    const newId = uuidV4(); // <-- generate new id
    this.props.addVechile({
      ...this.state,
      id: newId, // <-- add id to new vehicle object
    });
    this.props.history.push(`/${newId}`); // <-- navigate to generic path with id
  }

If you are want to navigate to the "/view" path then you will still need to create a new vehicle object with id property, but just point to the correct path.

...

handleSubmit=(e)=>{
  e.preventDefault();
  const newId = uuidV4();
  this.props.addVechile({
    ...this.state,
    id: newId,
  });
  this.props.history.push(`/view`); // <-- navigate to "/view" instead of "/View"
}
Related