How to send strings to another page in react.js - reactjs

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" />} />

Related

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

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
})
};

Why is this.props.history undefined despite having used withRouter?

I'm trying to do this.props.history.push... in my component, but even after making sure that I'm exporting it using withRouter I still get this error:
Uncaught TypeError: Cannot read property 'push' of undefined
I also made sure that the parent component that's using this is wrapped inside of a ProtectedRoute as well:
// my component:
import React from 'react';
import { withRouter } from 'react-router-dom';
import { Link } from 'react-router-dom';
class UserIndexItem extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.play = this.play.bind(this);
}
handleClick(e) {
if (!e.target.classList.contains("triangle")) {
this.props.history.push(`/playlist/${this.props.playlist.id}`);
}
}
handleTrack(playlist) {
// still going forward one, then back one, and then it plays normally...
if (!playlist.payload.tracks) return;
let tracks = Object.values(playlist.payload.tracks);
let currentTrack = tracks[0];
let nextTrack = tracks[1];
this.props.receiveCurrentTrack(currentTrack);
this.props.receiveNextTrack(nextTrack);
this.props.receiveTitle(currentTrack.title);
this.props.receiveArtist(currentTrack.artist);
this.props.receiveAlbumId(currentTrack.album_id);
}
play() {
const { playlist } = this.props;
this.props.requestSinglePlaylist(this.props.playlist.id).then(playlist => this.handleTrack(playlist));
this.props.receivePlaylistId(playlist.id);
}
render() {
const { playlist } = this.props;
return (
<li>
<div className="playlist-image" onClick={ this.handleClick }>
<div className="play-button" onClick={ this.play }>
<div className="triangle right"></div>
<div className="circle"></div>
</div>
<div className="overlay"></div>
<img src={playlist.photo_url} alt="Playlist thumbnail" onClick={ this.handleClick }/>
</div>
<div className="playlist-name">
<Link to={`/playlist/${playlist.id}`}>{ playlist.title}</Link>
</div>
</li>
);
}
}
export default withRouter(UserIndexItem);
// my parent component:
import React from 'react';
import UserIndexItem from './user_index_item';
import { selectTracksFromPlaylist } from '../../reducers/selectors';
class UserIndex extends React.Component {
constructor(props) {
super(props);
}
render() {
const { user, playlists } = this.props;
return(
<div className="user-index-container">
<div className="header">
<h1>{ user.username }</h1>
<h2>Public Playlists</h2>
</div>
<div className="playlists">
<ul>
{ playlists.map(playlist =>
<UserIndexItem
key={ playlist.id }
playlist={ playlist }
requestSinglePlaylist={ this.props.requestSinglePlaylist }
receiveCurrentTrack={ this.props.receiveCurrentTrack }
receiveNextTrack = { this.props.receiveNextTrack }
receiveTitle={ this.props.receiveTitle }
receiveArtist={ this.props.receiveArtist }
receivePlaylistId={ this.props.receivePlaylistId }
receiveAlbumId={ this.props.receiveAlbumId }
/>)
}
</ul>
</div>
</div>
);
}
}
export default UserIndex;
// my route that's using the parent component:
<ProtectedRoute path="/users/:userId" component={UserIndex} />
// my ProtectedRoute implementation:
const Protected = ({ component: Component, path, loggedIn, exact }) => (
<Route path={ path } exact={ exact } render={ (props) => (
loggedIn ? (
<Component {...props} />
) : (
<Redirect to="/welcome" />
)
) }/>
);
You can try like this:
<ProtectedRoute path="/users/:userId" component={props => <UserIndex {...props} />} />
Please let me know if this is working.
Thanks.
I think that {...props} need to call inside UserIndexItem as well.
According to my understand inside the App.js you need to pass {...props} to child component otherwise it don't have parent properties
// this ProtectedRoute should change according to your requirement. I just put sample code
const ProtectedRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={(props) => (
? <Component {...props} />
: <Redirect to="/Login"/>
)} />
)
<ProtectedRoute path="/users/:userId" component={UserIndex} />
// my parent component:
<UserIndexItem
key={ playlist.id }
playlist={ playlist }
requestSinglePlaylist={ this.props.requestSinglePlaylist }
receiveCurrentTrack={ this.props.receiveCurrentTrack }
receiveNextTrack = { this.props.receiveNextTrack }
receiveTitle={ this.props.receiveTitle }
receiveArtist={ this.props.receiveArtist }
receivePlaylistId={ this.props.receivePlaylistId }
receiveAlbumId={ this.props.receiveAlbumId }
{...this.props}
/>

ReactJS - Props and classes

I am really new to React, so I am trying to build a Pokemon app. My main goal is to build the list of 20 pokemon, and detail box which when clicked on Pokemon from the list should display chosen pokemon details, pictures etc.
import React, { Component } from 'react';
import './styles/App.css';
class App extends Component{
render(){
return <div className="App">
<h1> Pokedex! </h1>
<PokemonList/>
<PokemonDetail/>
</div>;
}
}
class Pokemon extends Component {
render(){
const {pokemon,id} = this.props;
return <div className="pokemon--species">
<button className="pokemon--species--container">
<div className="pokemon--species--sprite">
<img src={`https://pokeapi.co/media/sprites/pokemon/${id}.png`} />
</div>
<div className="pokemon--species--name">{id} {pokemon.name} {pokemon.url} </div>
</button>
</div>;
}
}
class PokemonDetail extends Component {
render(){
const {pokemon, id} = this.props;
return <div className="pokemon--species">
<button className="pokemon--species--container">
<div className="pokemon--species--sprite">
<img src={`https://pokeapi.co/media/sprites/pokemon/${id}.png`} />
</div>
<div className="pokemon--species--name">{id}</div>
<p>Attack:72</p>
<p>Defense:23</p>
<p>Health:99</p>
</button>
</div>;
}
}
class PokemonList extends Component{
constructor(props){
super(props);
this.state = {
species : [],
fetched : false,
loading : false,
};
}
componentWillMount(){
this.setState({
loading : true
});
fetch('https://pokeapi.co/api/v2/pokemon?limit=20').then(res=>res.json())
.then(response=>{
this.setState({
species : response.results,
loading : true,
fetched : true
});
});
}
render(){
const {fetched, loading, species} = this.state;
let content ;
if(fetched){
content = <div className="pokemon--species--list">{
species.map((pokemon,index) => <Pokemon key={pokemon.name} id={index+1} pokemon={pokemon}/>)
}
</div>;
}else if(loading && !fetched){
content = <p> Loading ...</p>;
}
else{
content = <div/>;
}
return <div className="container">
{content}
</div>;
}
}
export default App;
I know, there is much to do there, but first I want to understand how to pass ID to pokemondetails class.
Have a look at react-router and how to pass parameters to components associated with routes. Basically, you could have two routes that would render following components PokemonList and PokemonDetail. Redirect user from the PokemonList to PokemonDetail and append pokemonId to the url ( e.g "/details/23").
After redirection 'PokemonDetail' component would be rendered and pokemonId would be available in the component.
const App = () => (
<Router>
<div>
...
<Route exact path="/" component={PokemonList}/>
<Route path="/details/:pokemonId" component={PokemonDetail}/>
</div>
</Router>
)
// access pokemonId
class PokemonDetail extends Component{
render() {
return (
<div>
<h2>{this.props.params.pokemonId}</h2>
</div>
)
}
}

Is it possible to realize communication between independent components in ReactJS?

I have two components. These components are located on different routes. 'CreateItem' component gives me possibility to create new items. I store new items to array. Array will include new created items. I want send this modified array to component 'Main' where I will iterate those items and display them as list.
Here is my code:
1) index.js file:
import React, { Component } from 'react';
import { render } from 'react-dom';
import { BrowserRouter, Route } from 'react-router-dom'
import {Main} from "./components/Main"
import {CreateItem} from "./components/CreateItem"
import {CurrentItem} from "./components/CurrentItem"
render(
<BrowserRouter>
<div>
<Route exact path="/" component={Main}/>
<Route path="/create_item" component={CreateItem}/>
<Route path="/item" component={CurrentItem}/>
</div>
</BrowserRouter>,
document.getElementById('app')
);
2) Main.js
import React from 'react';
import { withRouter } from 'react-router-dom';
import { Route, browserHistory } from 'react-router-dom';
export class Main extends React.Component {
render(){
const ToCreateItemPageButton = () => (
<Route render={({ history}) => (
<button type='button' onClick={() => { history.push('/create_item') }}>Move to create item page!</button>
)}
/>
)
return (
<div>
<h1>Main Page</h1>
<ToCreateItemPageButton/>
</div>
);
}
}
3) CreateItem.js
import React from 'react';
import { Route, browserHistory } from 'react-router-dom';
export class CreateItem extends React.Component {
constructor(props) {
super(props);
this.state = {
mainArray: [],
item: {},
item_id: 0,
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({item: {item_id: this.state.item_id,
name:event.target.value}});
}
handleSubmit(event) {
if (this.state.item.name.length > 0) {
this.state.mainArray.push(this.state.item);
this.state.item_id = this.state.item_id + 1;
let data = JSON.stringify(this.state.mainArray);
localStorage.setItem('mainObject', data);
this.setState(
{mainArray : this.state.mainArray,
item_id : this.state.item_id,}
);
event.preventDefault();
}
}
render(){
const ToMainPageButton = () => (
<Route render={({ history}) => (
<button type='button' onClick={() => { history.push('/') }}>Move to main page!</button>
)}
/>
)
return (
<div>
<h1>Create new item</h1>
<ToMainPageButton/>
<form onSubmit={this.handleSubmit}>
<label>
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
</div>
);
}
}
So all I want is to have possibility to transfer my mainArray from 'CreateItem' component to 'Main' component.
You could redirect and send data like that:
this.props.history.push({
pathname: '/target-path',
data: [/*your data*/]
});
and receive it on the target component so:
const { data } = this.props.location;
Short answer - Yes it's possible using container component like in fiddle example.
So the idea is to keep you array of items in a container state and pass it to "iterated" component as well as a callback for handling incoming item.
// container component
class Container extends React.Component {
constructor(props){
super(props);
this.state = {
array: ['Hello', 'Stack', 'Overflow']
}
this.handleOnAdd = this.handleOnAdd.bind(this)
}
handleOnAdd(item){
this.setState({
array: [...this.state.array, item]
})
}
render() {
return (
<div>
// pass shared props to "display" component
<ChildOneDisplay items={this.state.array} />
// pass a callback to CreateItem component
<ChildTwoAdd onAdd={this.handleOnAdd} />
</div>
);
}
}
// display component
class ChildTwoAdd extends React.Component{
constructor(props){
...
this.handleAdd = this.handleAdd.bind(this)
}
handleAdd(){
this.props.onAdd(this.state.item);
...
}
render(){
return(
<div>
<input
name="item"
type="text"
onChange={this.handleChange}
value={this.state.item}
/>
<button onClick={this.handleAdd}>Add Me</button>
</div>
)
}
}
So all you need is to wrap your two routes with a container component and pass props to both of them as i did in this example.
// So your container should look like the following one
render(){
return (
<div>
<Route exact path="/" render={() => <Main items={this.state.array}}/>
<Route path="/create_item" render={() => <CreateItem onAdd={this.handleAdd}/>}/>
</div>
)
}
// And render it as the following
<BrowserRouter>
<Container />
<Route path="/item" component={CurrentItem}/>
</BrowserRouter>
Moreover i suggest looking at redux - this is the library for managing your app state.
Thanks!

can't update component correctly with any of life cycles (react)

I have three components Routes, Menu and a Page, and i'm trying to update Page (scroll it down to the selected part), when clicked on the corresponding menu item.
This is Routes code, it has a state scrollDestination which is passed to Page as a prop and a setScrollDestination method which passed to Menu also as a prop.
export default class Routes extends React.Component {
constructor(props) {
super(props);
this.state = {
scrollDestination: '#part_1'
};
this.setScrollDestination = this.setScrollDestination.bind(this);
}
setScrollDestination(destination) {
this.setState({
scrollDestination : destination
})
}
render() {
return (
<Router onUpdate = {() => {document.body.scrollTop = 0}}>
<div className="router">
<Menu text = {this.props.text.header}
changeLang = {this.props.changeLang}
setScrollDestination = {this.setScrollDestination} />
<Switch>
<Route exact path = "/"
render = {(props) => (
<HomePage text = {this.props.text}
changeLang = {this.props.changeLang}
{...props} />
)} />
<Route path = "/page"
render = {(props) => (
<Page text = {this.props.text}
scrollDestination = {this.state.scrollDestination}
{...props} />
)} />
</Switch>
</div>
</Router>
);
}
}
Menu component pass on destination as a parameter to setScrollDestination function and it is updating Routes' state and also Page's prop.
export default class Menu extends React.Component {
constructor(props) {
super(props);
this.handleCloseClick = this.handleCloseClick.bind(this);
}
handlePageScroll(destination) { this.props.setScrollDestination(destination); }
render() {
return (
<div>
<ul>
<li onClick = {this.handlePageScroll.bind(this, '#part_1')}><Link to="/">Home</Link></li>
<li><Link to="/page">Page
<ul>
<li onClick = {this.handlePageScroll.bind(this, '#part_1')}>part_1</li>
<li onClick = {this.handlePageScroll.bind(this, '#part_2')}>part_2</li>
<li onClick = {this.handlePageScroll.bind(this, '#part_3')}>part_3</li>
</ul></Link>
</li>
</ul>
</div>
);
}
}
Page component must only scroll the page. The state is updating, but it ignors some clicks. It does not always work.
export default class Page extends React.Component {
constructor(props) {
super(props);
}
componentWillReceiveProps(nextProps) {
let scrollDestination = nextProps.scrollDestination;
setTimeout(function() {
TweenMax.to(window, 1, {scrollTo: {y:scrollDestination, offsetY:100}});
}, 400);
}
}
render() {
return (
<div id="part_1"></div>
<div id="part_2"></div>
<div id="part_3"></div>
);
}
}
I've tried all of the updating methods and all of them are work the same. Where can be the issue?
The second thing is that all methods updates the component when ANY props have changed. Is there any way to run methods if one specific prop changed?
Thanks a lot for any answers.

Resources