A child ponent called Edit is receiving props.match from the route ('/:id/edit') but I cannot access other props in <Edit />
sent from the parent ponent <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 ponent)
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);
}
ponentDidMount() {
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 ponent)
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="/" ponent={Home} />
<Route path="/:id/edit" ponent={Edit} />
<Route path="/:id/delete" ponent={Delete} />
<Route path="/:id" ponent={Chirp} />
</Switch>
</Fragment>
</Router>
)
}
}
export default Navigation;
A child ponent called Edit is receiving props.match from the route ('/:id/edit') but I cannot access other props in <Edit />
sent from the parent ponent <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 ponent)
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);
}
ponentDidMount() {
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 ponent)
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="/" ponent={Home} />
<Route path="/:id/edit" ponent={Edit} />
<Route path="/:id/delete" ponent={Delete} />
<Route path="/:id" ponent={Chirp} />
</Switch>
</Fragment>
</Router>
)
}
}
export default Navigation;
Share
Improve this question
edited Sep 25, 2018 at 21:31
cDub
asked Sep 25, 2018 at 20:27
cDubcDub
5181 gold badge6 silver badges21 bronze badges
1
-
editClick
is a no-op here; you create and discard a ponent and returnundefined
. I think what you meant to do is set a flag saying you're editing, and check that flag to decide whether or not you should render the edit ponent. – Dave Newton Commented Sep 25, 2018 at 20:35
3 Answers
Reset to default 3You cannot render
a ponent onClick
like that.
Plus, editClick
is not returning anything.
My suggestion is to store a Boolean in state like isEditMode
and conditional render the ponent:
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./ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare./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" ponent={Edit} />
You can use the render prop.
The issue here is that your data for this ponent is not at the root ponent, but that's ok as you can render a <Route />
at any level down the ponent tree.
You can see a running example in this snippet
The original problem is that props were not getting passed to the <Edit />
ponent in the router Link. The most efficient solution es 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) ponent, 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 ponent. 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 ponent.
The problem with the child ponent is that you have defined the constructor method inside it without passing props to the super and constructor method for parent ponent.This way you will never get the props inside child ponent .
There are two solution of it.
1) Remove constructor method from the child ponent 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