I have a parent component which has 1 child. I am updating my child by passing data through props. initially, it works fine but when I click on a button and update the state using setState the child gets rendered with old values by the time setState is finished. I have solved it using componentWillReceiveProps in the child but is this the right way?
In the below code if I do setState in filterResults function it won't update the Emplist component .
import React, { Component } from 'react';
import {Search} from './search-bar'
import Emplist from './emplist'
class App extends Component {
constructor(props){
super(props);
this.emp=[{
name:'pawan',
age:12
},
{
name:'manish',
age : 11
}]
this.state={emp:this.emp};
this.filterResults=this.filterResults.bind(this);
}
filterResults(val)
{
if(this.state)
{
let filt=[];
filt.push(
this.emp.find(e=>{
return e.age==val
})
);
this.setState({emp:filt});
}
}
render() {
return (
<div className="App">
<Search filterResults={this.filterResults}/>
<Emplist emp={this.state.emp}/>
</div>
);
}
}
export default App;
EmpList Componet
import React,{Component} from 'react'
export default class Emp extends Component
{
constructor(props){
super(props);
this.emplist=this.props.emp.map(e=>{return <li>{e.name}</li>});
this.next=this.emplist;
}
componentWillReceiveProps(nextProps,nextState,prevProps,prevState,nextContext,prevContext){
// this.props.updated(this.props.empo);
this.next=nextProps.emp[0];
if(this.next)
this.emplist= nextProps.emp.map(e=>{return <li>{e.name}</li>});
}
render(){
if(!this.next)
return <div>name not found</div>
else
return (
<div>
<br/>
<p>The list is here</p>
<ul>
{this.emplist}
</ul>
</div>
)
}
}
If you want to pass from parent to child you can pass using props and if you wan t to do reverse than you can pass one function from parent to child and than use this passed function to send something back to parent.
child will look something like this
class Reciepe extends Component{
render(){
const { title, img, instructions } = this.props;
const ingredients=this.props.ingredients.map((ing,index)=>(<li key={index} >{ing}</li>));
return (
<div className='recipe-card'>
<div className='recipe-card-img'> <img src={img} alt={title}/> </div>
<div className='recipe-card-content'>
<h3 className='recipe-title'>Reciepe {title}</h3>
<ul> {ingredients} </ul>
<h4>Instructions:</h4>
<p>{instructions}</p>
</div>
</div>
)
}
}
parent will look something like this
class RecipeList extends Component{
render(){
return (
<div style={{'display':'flex'}}>
{this.props.recipes.map((item,index)=>(
<Recipe key={index}
title={item.title}
ingredients={item.ingredients}
instructions={item.instructions}
img={item.img}
/>
))}
</div>
)
}
}
The problem is that you are assigning the values to this which is not a good practice. Check where to declare variable in React here.
If you are not using the props to do any complex operations. This should work.
EmpList Componet
import React, {Component} from 'react'
export default class Emp extends Component {
constructor(props) {
super(props);
}
render() {
if (!this.next)
return <div>name not found</div>;
else
return (
<div>
<br/>
<p>The list is here</p>
<ul>
{this.props.emp && this.props.emp.map(e => <li>{e.name}</li>)}
</ul>
</div>
)
}
}
Your next and emplist class properties are directly derivable from your props and hence you don't actually need them. You could do it in the following way
import React,{Component} from 'react'
export default class Emp extends Component{
render(){
const { emp } = this.props;
if(!emp || emp.length === 1)
return <div>name not found</div>
else {
return (
<div>
<br/> <p>The list is here</p>
<ul>
{emp.map(e=>{return <li>{e.name}</li>});}
</ul>
</div>
)
}
}
}
However in cases when you do what to make really complex decisions based on props, a combination of componentWillReceiveProps and componentDidMount/componentWillMount is the right place to do it.
Related
I created a reset function in App.js and want to call it by an onclick in two other components. the problem is that it works in one component but doesn't in the other.
Here are the codes snippets
App.js
import React from 'react';
import Result from './components/Result';
import GeneralResult from './components/GeneralResult';
class App extends Component {
constructor(props) {
super(props);
this.state = {
result: '',
counter: 0,
}
}
// Reset function
handleReset=()=>{
this.setState({
result: '',
counter: 0,
)}
renderResult() {
return (
<div>
<Result reset={()=>this.handleReset()} />
<GeneralResult back={()=>this.handleReset()} />
</div>
);
}
Result.js
first component making use of reset()
function Result(props) {
return (
<div>
<span>
<button onClick={props.reset}>Replay</button>
</span>
</div>
);
}
export default Result;
GeneralResult.js
second component making use of the reset
import React, { Component } from 'react';
export default class GeneralResult extends Component {
render() {
return (
<React.Fragment>
<h2>Congratulations you won!</h2>
<span>
<button onClick={props.back}> Back to Question</button>
</span>
</React.Fragment>
);
}
}
You can pass the handler as props, and render the component from the parent class.
class Child extends Component {
render(){
return(
<button onClick = {this.props.onClick}></button>
)
}
}
export default Child;
import Child from 'path/to/child';
class Parent extends Component {
onClick = (e) => {
//do something
}
render () {
return(
<Child onClick = {onCLick}/>
)
}
}
Problem is that GeneralResult is class based component. so when you need to access props passed to it. you have to use this.props.
export default class GeneralResult extends Component {
render() {
return (
<React.Fragment>
<h2>Congratulations you won!</h2>
<span>
// you need to change "props.back"
// to "this.props.back"
<button onClick={this.props.back}> Back to Question</button>
</span>
</React.Fragment>
);
}
}
I am trying to load a different React component using a button. It worked when doing it for authentication with GitHub using Firebase, but won't work for this page.
import React from 'react';
import './index.css';
import GamePage from '../Game';
class Home extends React.Component {
constructor(props){
super(props);
this.LoadGamePage = this.LoadGamePage.bind(this);
}
LoadGamePage() {
return(
<div>
<GamePage />
</div>
)
}
render(){
return(
<div className="home">
<h1>Home Page</h1>
<button onClick={this.LoadGamePage}>Play PIT</button>
</div>
)
}
}
export default Home;
Is there something wrong with my LoadGamePage function?
How it is supposed to work? You have an onclick handler, which calls a class method. That class method, called LoadGamePage, returns JSX. Okey, but what now? It is returned, but... not rendered. It won't display anywhere. What would I suggest you? Instead of returning the JSX inside that handler, I would set state and depending on state I would render the Game Page or not.
class Home extends React.Component {
constructor(props){
super(props);
this.state = {
gameVisible: false,
}
this.LoadGamePage = this.LoadGamePage.bind(this);
}
LoadGamePage() {
this.setState({ gameVisible: true });
}
render() {
if (this.state.gameVisible) {
return <GamePage />
}
return (
<div className="home">
<h1>Home Page</h1>
<button onClick={this.LoadGamePage}>Play PIT</button>
</div>
)
}
}
I'm following this article (original implementation Sibling Sibling):
Update state cross component
The example works perfectly. But when I try to separate each class to each .js file, then using import/export to call/bind each other. It (the updating state) doesn't work anymore.
The structure like this:
Sibling1.js
import React, { Component } from 'react';
<-- some declare style -->
export function updateText(text) {
this.setState({text})
}
export class Sibling1 extends Component {
render() {
return (
<div>
<div style={{ ...style.topLabel, color: secondaryColor }}>I am Sibling 1</div>
<input style={style.textBox} type="text"
placeholder="Write text" onChange={(e) => updateText(e.target.value)} />
</div>
)
}
}
Example.js
import React, { Component } from 'react';
import * as sibling1 from './Sibling1'; //is this good?
import {Sibling1} from './Sibling1'; //is this good?
<-- some declare style, variable -->
class Sibling2 extends Component {
constructor(props) {
super(props)
this.state = {
text: "Initial State"
}
sibling1.updateText = sibling1.updateText.bind(this) //is this good binding?
}
render() {
console.log('Sibling2.state : ', this.state);
return (
<div>
<div style={{ ...style.topLabel, color: primaryColor }}>I am Sibling 2</div>
<div style={style.label}>{this.state.text}</div>
</div>
)
}
}
class Example3 extends Component {
render() {
return (
<div>
<Sibling1 />
<Sibling2 />
</div>
)
}
}
export default Example3;
I am simply expecting Sibling1 can change the state of Sibling2 (like the original implementation), but cannot.
I guess that my bind(this) doesn't bind the right context.
Can somebody tell me what are differences between the original implementation (article above) and my approach (separate to multi .js files)?
updateText() should be bound to a component. I'm not sure what you're trying to achieve here but updateText() might not work in Sibling1 if context changes.
You could try binding updateText() in both components (already bound in Sibling2).
import React, { Component } from 'react';
export function updateText(text) {
this.setState({text})
}
export class Sibling1 extends Component {
constructor() {
updateText = updateText.bind(this)
}
render() {
return (
<div>
<div style={{ ...style.topLabel, color: secondaryColor }}>I am Sibling 1</div>
<input style={style.textBox} type="text"
placeholder="Write text" onChange={(e) => updateText(e.target.value)} />
</div>
)
}
}
Usually state is controlled in parent component if two child components need to share the state and only the handler is passed down to the chilren.
React kinda forces you to use a one way data flow. So you can't just update the state of Sibling1 from within Sibling2.
As Dinesh Pandiyan mentions in his example that you normally would have a parent component that controls the state of both the siblings. Your code would then look like this:
Sibling1.js
import React, { Component } from 'react';
<-- some declare style -->
export class Sibling1 extends Component {
function updateText(text) {
// Use updateText function from props.
// Props are like state but not controlled by the component itself
// The value is passed to the component from outside
this.props.updateText(text)
}
render() {
return (
<div>
<div style={{ ...style.topLabel, color: secondaryColor }}>I am Sibling 1</div>
<input style={style.textBox} type="text"
placeholder="Write text"
onChange={(e) => this.updateText(e.target.value).bind(this)} />
</div>
)
}
}
Example.js
import React, { Component } from 'react';
import { Sibling1 } from './Sibling1'; // This is good.
import Sibling1 from './Sibling1'; // This is also possible if you use export default class instead of export class
<-- some declare style, variable -->
class Sibling2 extends Component {
// Use same function as in Sibling1.
function updateText(text) {
this.props.updateText(text)
}
render() {
return (
<div>
<div style={{ ...style.topLabel, color: primaryColor }}>I am Sibling 2</div>
<div style={style.label}>{this.props.text}</div> // changed state to props
</div>
)
}
}
class Example3 extends Component {
constructor(props) {
super(props);
this.state = {
text: "Initial state"
};
}
// Control state from parent component
function updateText(
this.setState({ text: text });
}
render() {
return (
<div>
<Sibling1 updateText={this.updateText.bind(this)}/>
<Sibling2 updateText={this.updateText.bind(this)} text={this.state.text} />
</div>
)
}
}
export default Example3;
Child
import React, {Component} from 'react'
class Card extends Component {
render() {
let props = this.props;
return(
<div className="card-main">
<img src={`http://image.tmdb.org/t/p/w342/${props.path}`} alt="Poster" />
<div className="card-deatils">
<h4 className="card-name">{props.name}</h4>
<h4 className="id card-name">{props.id}</h4>
</div>
</div>
);
}
}
export default Card;
I want to get the id stored in with class "id". The problem is that from previous component the amount cards are at minimum 20 in the page and what I ideally want is to pass id back to its parent component. Most of the methods I tried give value undefined.
Parent
import React, {Component} from 'react'
import Card from './Card'
class CardArray extends Component {
render() {
var props = this.props
return (
<div className="pop-movie-container">
<div className="card">
{
props.popNames.map((val,index) => {
return(
<Card
key ={props.popId[index]}
id={props.popId[index]}
name={props.popNames[index]}
path={props.popPath[index]}
/>
);
})
}
</div>
<div className="load-more">
<button className="btn btn-12" onClick={props.change}>LOAD MORE</button>
</div>
</div>
);
}
}
export default CardArray;
popNames is an array containing 20 names at minimum and increases by 20 on click of load more.
So, ideally what I want is that to get id from Card passed to CardArray.
output
So, when someone clicks on card more information can be fetched from the api using id of movie
parent
import React, { Component } from "react";
import Card from "./Card";
class CardArray extends Component {
fetchDetails = (id) => {
// fetching logic goes here
}
render() {
var props = this.props;
return (
<div className="pop-movie-container">
<div className="card">
{props.popNames.map((val, index) => {
return (
<Card
key={props.popId[index]}
id={props.popId[index]}
name={props.popNames[index]}
path={props.popPath[index]}
handelClick={this.fetchDetails}
/>
);
})}
</div>
<div className="load-more">
<button className="btn btn-12" onClick={props.change}>
LOAD MORE
</button>
</div>
</div>
);
}
}
export default CardArray;
child
import React, { Component } from "react";
class Card extends Component {
render() {
let props = this.props;
return (
<div className="card-main" onClick={() => this.props.handelClick(this.props.id)}>
<img
src={`http://image.tmdb.org/t/p/w342/${props.path}`}
alt="Poster"
/>
<div className="card-deatils">
<h4 className="card-name">{props.name}</h4>
<h4 className="id card-name">{props.id}</h4>
</div>
</div>
);
}
}
export default Card;
u can pass an function as props to child and child can call that function with id.
if you need more explanation let me know.
also once quick suggestion you can directly pass
name={val} instead of name={props.popNames[index]}
Why isn't my component rendering?
import React, { Component } from 'react';
import {Chokers, Bracelets, FRings, MRings} from './AllItems.js'
class App extends Component {
handleClick(e){
<Chokers chokers={this.props.items.Chokers}/>
}
render() {
return (
<div className="App">
<ul style={{display: 'inline'}}>
<li onClick={this.handleClick.bind(this)}>Chokers</li>
<li><Bracelets bracelets={this.props.items.Bracelets}/>Bracelets</li>
<li><FRings frings={this.props.items.FRings}/>Rings for Women</li>
<li><MRings mrings={this.props.items.MRings}/>Rings for Men</li>
</ul>
</div>
);
}
}
export default App;
Basically my this.props.items.Chokers for examples call a on my json file that was passed through.
I just want to create a links to another component at an onClick event.
The problem that I am having is that my Component under handleClick doesnt render.
If you want to render Chockers component on the click of item, then write it like this, create the state variable and set it true onClick of the item:
class App extends React.Component {
constructor(){
super();
this.state = {render:''}
}
handleClick(compName, e){
console.log(compName);
this.setState({render:compName});
}
_renderSubComp(){
switch(this.state.render){
case 'chockers': return <Chokers/>
case 'bracelets' : return <Bracelets/>
case 'rings': return <FRings/>
}
}
render() {
return (
<div className="App">
<ul style={{display: 'inline'}}>
<li onClick={this.handleClick.bind(this, 'chockers')}>Chokers</li>
<li onClick={this.handleClick.bind(this, 'bracelets')}>Bracelets</li>
<li onClick={this.handleClick.bind(this, 'rings')}>Rings for Women</li>
</ul>
{this._renderSubComp()}
</div>
);
}
}
class Chokers extends React.Component {
render(){
return <div>Inside Chockers</div>
}
}
class FRings extends React.Component {
render(){
return <div>Inside FRings</div>
}
}
class Bracelets extends React.Component {
render(){
return <div>Inside Bracelets</div>
}
}
ReactDOM.render(<App />, document.getElementById('container'));
Check the jsfiddle for working example: https://jsfiddle.net/ocg4ebdf/
You are not rendering anything.You just need to return the Chokers component from render function itself.
create some state variable and set that variable to true so that upon this variable check you can render the Chokers component.
Try to something like below.
import React, { Component } from 'react';
import {Chokers, Bracelets, FRings, MRings} from './AllItems.js'
class App extends Component {
var flag;
handleClick(e){
flag = true;
render() {
return (
<Chokers chokers={this.props.items.Chokers}/>
);}
}
if(!flag){
render() {
return (
<div className="App">
<ul style={{display: 'inline'}}>
<li onClick={this.handleClick.bind(this)}>Chokers</li>
<li><Bracelets bracelets={this.props.items.Bracelets}/>Bracelets</li>
<li><FRings frings={this.props.items.FRings}/>Rings for Women</li>
<li><MRings mrings={this.props.items.MRings}/>Rings for Men</li>
</ul>
</div>
);
}
}
}
export default App;