Source:  Twitter logo

A child component called Edit is receiving props.match from the route ('/:id/edit') but I cannot access other props in <Edit /> sent from the parent component <Chirp />.

Is it not possible to send props with both methods?

My initial problem was that I wanted to access props from <Edit /> without rendering everything in Edit on the Chirp page. So I sent the props on Edit's button click. <Edit /> is rendering all its html fine otherwise.

console.log(this.props) shows Match, Location, and History. I've tried this.props.match.params and this.props.user, etc. but only get undefined.

Chirp.jsx (parent - also a child component)

import React, { Component, Fragment } from 'react';
import { BrowserRouter as Router, Link } from 'react-router-dom';
import 'isomorphic-fetch';
import Edit from './edit';

class Chirp extends Component {

    constructor() {
        super();
        this.state = {
            user: "",
            text: ""
        }
        this.editClick = this.editClick.bind(this);
    }

    componentDidMount() {
        fetch(`http://127.0.0.1:3000/api/chirps/${this.props.match.params.id}`)
            .then(response => response.json())
            .then(data => {
                this.setState({
                    user: data.user,
                    text: data.text
                })
            })
            .catch(err => console.log(err))

    }

    editClick() {
        <Edit user={this.state.user} text={this.state.text} />
        console.log("props passed")
    }

    render() {
        return (
            <div>
                <Fragment>
                    <Link to="/" className="homelink" style={{ textDecoration: "none" }}>Home</Link>
                </Fragment>
                <div className="current">
                    <div className="flex-column">
                        <div className='chirps'>
                            <p>{this.state.user}: {this.state.text}</p>
                            <Fragment >
                                <Link to={`/${this.props.match.params.id}/edit`}><button onClick={this.editClick}>Edit</button></Link>
                            </Fragment>
                            <Fragment >
                                <Link to={`/${this.props.match.params.id}/delete`}><button className="delete">x</button></Link>
                            </Fragment>
                        </div>
                    </div>
                </div>
            </div>
        )
    }
}

export default Chirp;

edit.jsx (child component)

import React, { Component, Fragment } from 'react';
import { BrowserRouter as Router, Link } from 'react-router-dom';
import 'isomorphic-fetch';

class Edit extends Component {

    constructor() {
        super();
        this.state = {
            newUser: "",
            newText: ""
        }
    }

    render() {
        console.log(this.props)
        return (
            <div>
                <Fragment>
                    <Link to="/" className="homelink" style={{ textDecoration: "none" }}>Home</Link>
                </Fragment>
                <h2>Edit Your Chirp</h2>
                <div className="input">
                    <form action="">
                        <input
                            type="text"
                            placeholder={this.props.user}
                            size="10"
                            id="user"
                            name="user"
                        // onChange={this.inputHandler}
                        // defaultValue={this.props.user}
                        />
                        <input
                            type="text"
                            placeholder={this.props.text}
                            size="60"
                            id="text"
                            name="text"
                        // onChange={this.inputHandler}
                        // defaultValue={this.state.text}
                        />
                        <button
                            onClick={this.editChirps}
                            id="submit">
                            Submit
                    </button>
                    </form>
                </div>
            </div>

        )

    }
}

export default Edit;

app.jsx

import React, { Component, Fragment } from 'react';
import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom';
import Home from './home'
import Chirp from './chirp'
import Edit from './edit'
import Delete from './delete'

class Navigation extends Component {

    render() {
        return (
            <Router>
                <Fragment>
                    <Switch>
                        <Route exact path="/" component={Home} />
                        <Route path="/:id/edit" component={Edit} />
                        <Route path="/:id/delete" component={Delete} />
                        <Route path="/:id" component={Chirp} />
                    </Switch>
                </Fragment>
            </Router>
        )
    }
}

export default Navigation;

You cannot render a component onClick like that.
Plus, editClick is not returning anything.

My suggestion is to store a Boolean in state like isEditMode and conditional render the component:

editClick(){
  this.setState(state => ({isEditMode: !state.isEditMode}));
}

and in render:

{this.state.isEditMode && <Edit user={this.state.user} text={this.state.text} />}

Here is a small running example:

const Edit = ({ someProp }) => <input placeholder={someProp} />;

class App extends React.Component {
  state = { isEditMode: false };

  toggleEdit = () =>
    this.setState(state => ({ isEditMode: !state.isEditMode }));

  render() {
    const { isEditMode } = this.state;
    return (
      <div>
        <button onClick={this.toggleEdit}>Toggle Edit</button>
        {isEditMode && <Edit someProp="Edit me" />}
      </div>
    );
  }
}

const root = document.getElementById("root");
ReactDOM.render(<App />, root);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"/>

Edit
After you update, i now see that your problem is the way you render the Route for <Edit />.

You are not passing any props:

 <Route path="/:id/edit" component={Edit} />

You can use the render prop.

The issue here is that your data for this component is not at the root component, but that's ok as you can render a <Route /> at any level down the component tree.

You can see a running example in this snippet

3 users liked answer #0dislike answer #03
Sagiv b.g profile pic
Sagiv b.g

The original problem is that props were not getting passed to the <Edit /> component in the router Link. The most efficient solution comes from this YouTube: Pass Props To React Router Link Component

Simply add properties to "to" in the Link with the syntax below. Pass props in 'state'.

<Link to={{
     pathname: `/${this.props.match.params.id}/edit`,
     state: {
          user: this.state.user,
          text: this.state.text
     }
     }}>
     <button onClick={this.editClick}>Edit</button>
</Link>

Then on the Link (child) component, access the props with

this.props.location.state.user
this.props.location.state.text

Another solution presented on this page will also work; Instead of defining the route for this Link in app.jsx, define it in the parent component. This would require a few more steps:

import Route, Switch, & Edit rearrange the Route structure in render; i.e. wrap return in <Router >, move home Link, etc add this inline function to the route: render={route => <Edit match={route.match} user={this.state.user} text={this.state.text}

But an issue this method presents is that the home link and chirp 'user' and 'text' show in the Edit component.

1 users liked answer #1dislike answer #11
cDub profile pic
cDub

The problem with the child component is that you have defined the constructor method inside it without passing props to the super and constructor method for parent component.This way you will never get the props inside child component .

There are two solution of it.

1) Remove constructor method from the child component 2) If you don't want to remove the constructor for some reason then simply pass props to the super and constructor method in it .

Example :

class Edit extends Component {
    constructor(props) {
    super(props);
    this.state = {
        newUser: "",
        newText: ""
    }
 }

 render() {
    console.log(this.props)
 }
}

Hope this will solve your problem

0 users liked answer #2dislike answer #20
Alok Rai profile pic
Alok Rai

Copyright © 2022 QueryThreads

All content on Query Threads is licensed under the Creative Commons Attribution-ShareAlike 3.0 license (CC BY-SA 3.0).