React) "this.props" while sharing the "state" using router - reactjs

Somebody help me :(
I couldn't find "this.props.Epoint" on the result page.
It's just like "Epoint : " "IPoint : ". Empty, Empty.
I do have to receive "Epoint : 0", "IPoint : ", don't I?
Here is the code. Please save me.
<App.js>
class App extends Component {
state = {
EPoint: 0,
IPoint: 0,
};
upEPoint = async () => {
this.setState({
Epoint: this.state.EPoint ++
})
};
upIPoint = async () => {
this.setState({
Ipoint: this.state.IPoint ++
})
};
render() {
return (
<>
<Router>
<Route exact path="/" component={Home} />
<Route path="/question1" component={() => <Question1 EPoint={this.state.EPoint} IPoint={this.state.IPoint} upEPoint={this.upEPoint} upIPoint={this.upIPoint}/>} />
<Route path="/question2" component={() => <Question2 EPoint={this.state.EPoint} IPoint={this.state.IPoint} upEPoint={this.upEPoint} upIPoint={this.upIPoint}/>} />
<Route path="/question3" component={() => <Question3 EPoint={this.state.EPoint} IPoint={this.state.IPoint} upEPoint={this.upEPoint} upIPoint={this.upIPoint}/>} />
<Route path="/result" component={() => <Result EPoint={this.state.EPoint} IPoint={this.state.IPoint}/>} />
<Router/>
</>
export default App;
<Result.js>
class Result extends Component {
render() {
return (
<div>
<header>
<h1> Result </h1>
<h5> Epoint : {this.props.Epoint}</h5>
<h5> Ipoint : {this.props.Ipoint}</h5>
</header>
</div>)
}
}
export default Result;

I think the first issue here is that you are trying to access Epoint from props, but the variable in state that you are passing down in props is actually EPoint (notice the capital P there). Same goes for IPoint.
Your Result.js should look like this:
import React from "react";
class Result extends React.Component {
render() {
return (
<div>
<header>
<h1> Result </h1>
<h5> Epoint : {this.props.EPoint}</h5>
<h5> Ipoint : {this.props.IPoint}</h5>
</header>
</div>
);
}
}
export default Result;
As the other answers have also mentioned, you cannot set your state as you have.
I am not so good with class components, but I believe you must set it something like the following:
constructor(props) {
super(props);
this.state = { EPoint: 0, IPoint: 0 };
}

u cant use this.state inside setState just get prev state from arrow function then assign it to new object and return it into setState
upIPoint = async () => {
this.setState(prev => ({
Ipoint: prev.IPoint + 1
})
};

Related

React updating component when params change with old state

i'm rendering some router links with params that contain the the same url but different id params. The router view updates but the state is always behind 1.
here's my Router setup:
<Router>
<div>
<h1>HEY THERE</h1>
<Link to={'/'}>Home</Link>
<Link to={'/detail/13721'}>Show Number 1 </Link>
<Link to={'/detail/1228'}>Show Number 2</Link>
</div>
<Switch>
<Route exact path="/" component={HomePage} />
<Route path="/detail/:id" component={DetailPage} />
</Switch>
</Router>
Here's my detail page setup:
import React, { Component } from 'react'
class DetailPage extends React.Component {
constructor(props){
super(props);
this.state ={
showid: '30318',
showurl: 'http://localhost/podcast/podcastsbyid/?id=',
shows: []
}
}
render() {
return ( <div>
<h1>Detail Page</h1><p>{this.state.showid}</p>
{this.state.shows.map((show, i) => {
return <div key={i}>{show.title}</div>
})}
</div>
)
}
getShow(){
fetch(this.state.showurl + this.state.showid).then(res => res.json()).then(data => {
this.setState({shows: []})
this.setState({shows: this.state.shows.concat(data.items)})
})
}
componentWillReceiveProps(newProps){
if(this.state.showid == newProps.match.params.id){
console.log('they are the same')
}
else{
console.log('they are different')
this.setState({showid: newProps.match.params.id})
this.getShow()
}
}
}
export default DetailPage;
any help would be appreciated!!
The problem seems to be state update being asynchronous. Try updating the code as shown below:
componentWillReceiveProps(newProps){
if(this.state.showid == newProps.match.params.id){
console.log('they are the same')
}
else{
console.log('they are different')
this.setState({showid: newProps.match.params.id}, this.getShow)
// use it as callback ------------------------------^
}
}

How to send strings to another page in react.js

I am trying to build a website where the user must choose a size and I want the size to go into the form but the size is undefined
this is my code so far
StartPage.js
class StartPage extends React.Component{
render() {
return (
<div>
<h3>Start Page</h3>
<dir>
<Size size="small" /><Size size="medium" /><Size size="large" />
</dir>
</div>
);
}
}
Size.js
const Size = ({size}) => {
console.log(size);
return(
<Link to="/contact" size={size}>
Choose this if you want a
<br/> {size}
<br/>package
</Link>
);
}
Form.js
const Form = ({ size }) => {
console.log(size);
return (
<div>
<h3>Form</h3>
<input value={size}/>
<button>submit</button>
</div>
);
}
In Size.js it works fine, but as soon as i click on one of then I get an undefined to the form, how do I fix this?
I have tried to make a constructor(props) but that gives me the same result..
UPDATE
Startpage.js
class StartPage extends React.Component{
constructor(){
super();
this.state = {PRODUCTS}
}
render() {
return (
<div>
<h3>Start Page</h3>
<dir>
{PRODUCTS.map((sizes, i) => {
return <Size key={i} size={PRODUCTS[i].size} />
})}
</dir>
</div>
);
}
}
export default StartPage;
Size.js
class Size extends React.Component {
constructor(props){
super(props);
this.state = {
size: this.props.size,
id: this.props.id
}
}
selectedSize(size, id){
this.setState = size.size;
}
render(){
const {size, id} = this.props;
return(
<Link to="/contact"
size={size}
onClick={() => this.selectedSize({size})}
>
Choose this if you want a
<br/> {size}
<br/>package
</Link>
);
}
}
export default Size;
Form.js
class Form extends React.Component {
constructor(props){
super(props);
this.state = {size: this.props.size};
this.setState = this.props.size;
}
render(){
return (
<div>
<h3>Form</h3>
<input value={this.props.size}/>
<button>submit</button>
</div>
);
}
}
export default Mail;
I have added an array and a constructor and I now as before get 3 buttons, but I still don't get the value to the form, I know that selectedSize gets the size clicked on, but it does not get printed on the form.
I would recommend making the size part of the Link's URL:
<Link to={`/contact/${size}`}>
Then in your React Router (we add two routes in order to handle the route without a size, too):
<Route path="/contact/:size" exact render={(props) => contactRoute(props)} />
<Route path="/contact" exact render={(props) => contactRoute(props)} />
Then make contactRoute in the same file as this Route:
const contactRoute = ({match}, props) => {
if(match && match.params && match.params.size) {
const size = match.params.size;
return <Form size={size}/>
}else{
return <Form/>
}
}
This will then make the size available as a prop in the Form component (as long as it is in the URL, e.g. /contact/small).
Here is an example of this:
https://github.com/revolution0x/society0x/blob/master/client/src/components/PageContainer.js
You need to pass "size" to Form.js but you did not do that. So its expected behaviour that its undefined.
Use state management like (redux,mobx etc.)
You could pass props to compnent using "Route"
example:
<Route exact path="/contact" component={() => <Form size="any String you want" />} />

Updated state not rendering in child component

I am trying to pull a picture from NASA's API to get their astronomy picture of the day. When I update the state in my API call and console.log this.state.picture, I can see that picture has been set to the data object. But when I try to log picture in render, it logs an empty array. I used a similar approach in previous assignments and never had this issue, so I really can't figure out what I'm doing wrong. I'm new to React, so sorry if the answer is really obvious.
Parent component:
class App extends Component {
state = {
picture: [],
asteroids: [],
}
render() {
return (
<div className="App">
<Route exact path="/" component={Homepage}/>
<Route path="/apod" render={() =>
<APOD picture={this.state.picture}/>}/>
<Route path="/asteroids" render={() =>
<Asteroids asteroids={this.state.asteroids} />} />
</div>
);
}
}
export default App;
Child component:
class Apod extends React.Component{
getApodPicture = e => {
e.preventDefault();
let url = `https://api.nasa.gov/planetary/apod?api_key=v1sEi9chxFYzrf1uXei0J1GvXaemhnQXiDjEcnK2`;
fetch(url)
.then(res => {
if (!res.ok){
throw new Error (res.status)
}
return res.json();
})
.then(data => {
this.setState({picture: data})
console.log(this.state.picture) // logs the data object from NASA
})
.catch(err => console.log(err))
}
render(){
console.log(this.props.picture) // logs []
// console.log(this.state.picture) // returns "Cannot read property 'picture' of null"
return(
<div>
<h1>Astronomy picture of the day!</h1>
<Link to="/">Back to homepage</Link>
<section>
<button onClick={e => this.getApodPicture(e)}>Click to see picture</button>
</section>
</div>
)
}
}
export default Apod;
This.setState will set the state of the component, not the parent component.
you will need a changeHandler to get the desired result.
something along the lines of
class App extends Component {
state = {
picture: '',
asteroids: [],
}
pictureChangeHandler = value => {
this.setState(value);
}
render() {
return (
<div className="App">
<Route exact path="/" component={Homepage}/>
<Route path="/apod" render={() =>
<APOD pictureChangeHandler={this.pictureChangeHandler}
picture={this.state.picture}/>}/>
<Route path="/asteroids" render={() =>
<Asteroids asteroids={this.state.asteroids} />} />
</div>
);
}
}
export default App;
class Apod extends React.Component{
getApodPicture = e => {
e.preventDefault();
let url = `https://api.nasa.gov/planetary/apod?api_key=v1sEi9chxFYzrf1uXei0J1GvXaemhnQXiDjEcnK2`;
fetch(url)
.then(res => {
if (!res.ok){
throw new Error (res.status)
}
return res.json();
})
.then(data => {
this.props.pictureChangeHandler(data)
})
.catch(err => console.log(err))
}
render(){
console.log(this.props.picture) // logs []
// console.log(this.state.picture) // returns "Cannot read property 'picture' of null"
return(
<div>
<h1>Astronomy picture of the day!</h1>
<Link to="/">Back to homepage</Link>
<section>
<button onClick={e => this.getApodPicture(e)}>Click to see picture</button>
</section>
</div>
)
}
}

Undefined prop value in child component

I'm attempting to read an array item in a child component via props. Logging the array in the child component works. But if I try to access a property of one of the array items by indexing it with the :id from match.params, it tells me that I can't access a property of 'undefined'.
Any guidance would be greatly appreciated.
tours.js
import React, { Component } from "react";
import { Route, Switch } from "react-router-dom";
// Page Imports
import Summary from "../pages/summary";
import Details from "../pages/details";
// Component Imports
import Homebutton from "../components/homebutton";
class Tours extends Component {
state = {
tours: []
};
componentDidMount() {
window.scrollTo(0, 0);
fetch("/tours")
.then(res => res.json())
.then(res => this.setState({ tours: res }));
}
render() {
const tours = this.state.tours;
return (
<section className="tours-page">
<div className="center-box">
<h2>Tours</h2>
</div>
<Switch>
<Route
exact
path={this.props.match.url}
render={props => <Summary {...props} tours={tours} />}
/>
<Route
path={this.props.match.url + "/:id"}
render={props => <Details {...props} tours={tours} />}
/>
</Switch>
<Homebutton />
</section>
);
}
}
export default Tours;
details.js
import React from "react";
const Details = ({
tours,
match: {
params: { id }
}
}) => (
<section className="details">
<h2>{tours[id]["name"]}</h2>
</section>
);
export default Details;
To be sure that tours[id] is not undefined you should check it first
<section className="details">
<h2>{tours[id] && tours[id]["name"]}</h2>
</section>
As componentDidMountalways gets called after first render, you must validate your props to avoid app crashes:
const Details = ({
tours,
match: {
params: { id }
}
}) => (
<section className="details">
<h2>{tours.length && tours[id]["name"]}</h2>
</section>
);

React - send function to child via stateless component

I can send a function down to a class component with no problem. But when I want to pass down a function to a stateless component, and from there to a class component, something goes wrong.
It says "this.props.getProduct is not a function" , so I guess some binding thing has to be added, but I already did that in the top component.
The top component is like this:
class App extends Component {
constructor(){
super()
this.state = {
product: {}
}
this.getProduct = this.getProduct.bind(this)
}
getProduct(productId){
const result = productlist.products.filter(obj => {
return obj.id === productId;
})
this.setState({
product: result[0]
})
}
render() {
const {product} = this.state
return (
<div className="App">
<BrowserRouter>
<div>
<Route path="/" render={props =>
<Main
getProduct = {this.getProduct}
product = {this.state.product}
{...props}
/>}
/>
</div>
</BrowserRouter>
</div>
)
}
}
Here is "Main", the intermediate stateless component
const Main = (props) =>
<Route path="/products/:product" render={ props =>
<Product
getProduct={props.getProduct}
product={props.product}
{...props}
/>}
/>
"Product" component where the error occurs
class Product extends Component {
constructor(props) {
super(props)
this.state = {
}
}
componentDidMount() {
this.props.getProduct(this.props.match.params.product) // "not a function"
}
I suspect there is some kind of extra binding that needs to be done, but how?
Here is a sandbox where you can see the problem:
codesandbox
You have a collision here:
const Main = (props) =>
<Route path="/products/:product" render={ props =>
<Product
getProduct={props.getProduct}
product={props.product}
{...props}
/>}
/>
props from Main and props in render, so when you are passing in props from it takes props from the render function you passed instead of props from Main. You have to rename one, or replace const Main = (props) with const Main = ({ getProduct, product}) and then pass it to
The route render function props argument is shadowing the Main component's props variable. Better change variable name of render argument
const Main = (props) =>
<Route path="/products/:product" render={ p =>
<Product
getProduct={props.getProduct}
product={props.product}
{...props}
/>}
/>
This should do the job:
class App extends Component {
constructor() {
super()
this.state = {
product: {},
}
this.getProduct = this.getProduct.bind(this)
}
getProduct(productId) {
const result = productlist.products.filter(obj => {
return obj.id === productId
})
this.setState({
product: result[0],
})
}
render() {
const { product } = this.state
return (
<div className="App">
<BrowserRouter>
<div>
<Route
path="/products/:product" render={props =>
<Product
getProduct={props.getProduct}
product={props.product}
{...props}
/>}
/>
</div>
</BrowserRouter>
</div>
)
}
}

Resources