React - Ajax data not being passed into Child Component - javascript

I have two components, one parent one child. I am using the fetch method in componentDidMount() callback. Once I do this, I set the state with key items to that data that is pulled from the api. Once I do this it should be able to be console logged in the child component as a prop. However this is not working. What am I doing wrong here?
Parent Component:
import React, {Component} from 'react';
import Map from './maps/Map';
class Main extends Component {
constructor(props){
super(props);
this.state = {
name: "John",
items: []
}
}
componentDidMount() {
fetch('https://hn.algolia.com/api/v1/search?query=')
.then(dat => dat.json())
.then(dat => {
this.setState({
items: dat.hits
})
})
}
render() {
return (
<div>
<Map list={this.state.name} items={this.state.items}></Map>
</div>
)
}
}
export default Main;
Child Component:
import React, {Component} from 'react';
class Map extends Component {
constructor(props) {
super(props);
console.log(props.items)
}
render () {
return (
<h1>{this.props.name}</h1>
)
}
}
export default Map;

First, fetch is asynchronous. So, the fetch statement might be pending by the time you try to console.log the result inside the child constructor.
Putting the console.log inside the render method would work, because the component will be rerendered, if the state items changes.

The constructor for a component only runs one time during a lifecycle. When it does, props.items is undefined because your ajax request is in-flight, so console.log(props.items) doesn't show anything.
If you change your constructor to console.log("constructed");, you'll see one-time output (stack snippets may not show this--look in your browser console). Henceforth, componentDidUpdate() can be used to see the new props that were set when your ajax request finishes.
You could also log the props inside the render method, which will run once before the ajax request resolves and again afterwards when props.items changes.
As a side point, you have <Map list=... but the component tries to render this.props.name, which is undefined.
Also, if you aren't doing anything in the constructor (initializing state or binding functions) as here, you don't need it.
class Map_ /* _ added to avoid name clash */ extends React.Component {
constructor(props) {
super(props);
console.log("constructed");
}
componentDidUpdate(prevProps, prevState) {
const props = JSON.stringify(this.props, null, 2);
console.log("I got new props", props);
}
render() {
return (
<div>
<h1>{this.props.name}</h1>
<pre>
<ul>
{this.props.items.map((e, i) =>
<li key={i}>{JSON.stringify(e, null, 2)}</li>)}
</ul>
</pre>
</div>
);
}
}
class Main extends React.Component {
constructor(props) {
super(props);
this.state = {name: "John", items: []};
}
componentDidMount() {
fetch('https://hn.algolia.com/api/v1/search?query=')
.then(dat => dat.json())
.then(dat => {
this.setState({items: dat.hits})
});
}
render() {
return (
<div>
<Map_
name={this.state.name}
items={this.state.items}
/>
</div>
);
}
}
ReactDOM.createRoot(document.querySelector("#app"))
.render(<Main />);
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div id="app"></div>

The only problem you have is that you are trying to use this.props.name and your Map component props are called list and items, so it will return undefined.
If you log your props in the constructor you will get the initial state of Main because the fetch hasn't returned anything yet. Remember that the constructor only runs once. So you are probably getting an empty array when you log props.items in the constructor because that's what you have in your initial state.
{
name: "John",
items: []
}
If you log the props in your render method you will see your array filled with the data you fetched, as you can see here:
https://codesandbox.io/s/stoic-cache-m7d43
If you don't want to show the component until the data is fetched you can include a boolean property in your state that you set to true once you the fetch returns a response and pass it as a prop to your component. Your component can you use that variable to show, for example, a spinner while you are fetching the data. Here's an example:
https://codesandbox.io/s/reverent-edison-in9w4
import CircularProgress from "#material-ui/core/CircularProgress"
class Main extends Component {
constructor(props) {
super(props);
this.state = {
name: "John",
items: [],
fecthed: false
};
}
componentDidMount() {
fetch("https://hn.algolia.com/api/v1/search?query=")
.then(dat => dat.json())
.then(dat => {
this.setState({
items: dat.hits,
fecthed: true
});
});
}
render() {
return (
<Map
fetched={this.state.fecthed}
list={this.state.name}
items={this.state.items}
/>
);
}
}
class Map extends Component {
render() {
return (
<div>
{this.props.fetched ? (
<div>
<h1>{this.props.list}</h1>
{this.props.items.map((item, indx) => (
<div key={indx}>Author: {item.author}</div>
))}
</div>
) : (
<CircularProgress />
)}
</div>
);
}
}
Hope this helps. Cheers!

Related

React child components don't re-render when mapped from an array

I'm loading some react components on demand (among with other information) depending on user input.
The components to render are kept in an array and the render method uses array.map to include the components.
The problem is, that if I trigger a forceUpdate() of the main app component, the mapped components won't update.
Code example: https://codesandbox.io/s/react-components-map-from-array-ekfb7
The dates are not updating because you are creating the instance of the component in your add function, and from then on you are referencing that instance without letting react manage the updates.
This is why storing component instances in state or in other variables is an anti-pattern.
Demonstration of the problem
Below I've created a working example still using forceUpdate just to prove what the issue is.
Notice instead of putting the component in state, I'm just pushing to the array to increase it's length. Then React can manage the updates correctly.
class TestComponent extends React.Component {
render() {
return <p>{Date.now()}</p>;
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.comps = [1];
}
add() {
this.comps.push(1);
this.forceUpdate();
}
render() {
return (
<div className="App">
<h1>Components map example</h1>
<p></p>
<h2>Static TestComponent (ok):</h2>
<TestComponent />
<h2>TestComponents mapped from an array (not ok):</h2>
{this.comps.map((comp, id) => {
return <div key={id}><TestComponent /></div>;
})}
<h2>All should update when the App component renders</h2>
<p>
<button onClick={() => this.add()}>Add TestComponent</button>
<button onClick={() => this.forceUpdate()}>forceUpdate App</button>
</p>
</div>
);
}
}
ReactDOM.render(<App/>,document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
This is still a less than ideal solution. But it does show where the issue lies.
A better solution
If you need to know more about each component instance up front, you can make the array more complex.
I would also suggest using state to store the comps array, and removing forceUpdate completely.
class TestComponent extends React.Component {
render() {
return <p>{Date.now()} {this.props.a} {this.props.b}</p>;
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
comps: [{ a: 'a', b: 'b' }]
}
}
add = () => {
// add your custom props here
this.setState(prev => ({comps: [ ...prev.comps, { a: 'c', b: 'd' } ]}));
}
render() {
return (
<div className="App">
<h1>Components map example</h1>
<p></p>
<h2>Static TestComponent (ok):</h2>
<TestComponent />
<h2>TestComponents mapped from an array (not ok):</h2>
{this.state.comps.map((compProps, id) => {
return <div key={id}><TestComponent {...compProps} /></div>;
})}
<h2>All should update when the App component renders</h2>
<p>
<button onClick={() => this.add()}>Add TestComponent</button>
</p>
</div>
);
}
}
ReactDOM.render(<App/>,document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Now notice that each component in the map callback can have it's own unique set of props based on whatever logic you what. But the parts that should re-render will do so correctly.
In order to update in React, you have to put your data in the state and then setState.
setState() schedules an update to a component’s state object. When state changes, the component responds by re-rendering which means updating the screen with the new state.
import React from "react";
import "./styles.css";
class TestComponent extends React.Component {
render() {
return <p>{Date.now()}</p>;
}
}
export class App extends React.Component {
constructor(props) {
super(props);
this.state = {
comps: [<TestComponent />],
}
}
add = () => {
this.setState({ comps: this.state.comps.concat(<TestComponent />) })
}
render() {
return (
<div className="App">
<h1>Components map example</h1>
<p></p>
<h2>Static TestComponent (ok):</h2>
<TestComponent />
<h2>TestComponents mapped from an array (not ok):</h2>
{
this.state.comps.map((comp, id) => {
return <div key={id}>{comp}</div>;
})
}
<h2>All should update when the App component renders</h2>
<p>
<button onClick={this.add}>Add TestComponent</button>
</p>
</div>
);
}
}

ReactJs: Rendering on state change getting into infinite loop

Here I am trying to pass data between sibling components A and B, where I want to re-render component B upon completion of an asynchronous event in component A.
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
valueKey: '',
};
}
parentFunction(dataFromChild) {
this.setState({ valueKey: dataFromChild });
}
render() {
const { valueKey } = this.state;
return (
<div>
<componentA
url={url}
...
functionCallFromParent={() => this.parentFunction()}
/>
<componentB valueFromParent={valueKey} />
</div>
);
}
}
class componentA extends React.Component {
constructor(props) {
super(props);
this.state = {
items: null,
};
// asynchronous request made to server which updates the state when response received.
this._getTiles();
}
render() {
const { items } = this.state;
if (items && items.asArray().length > 0) {
functionCallFromParent('Hello From Child1');
return (
<div>
{tiles}
</div>
);
}
return null;
}
}
Issue is, every time componentA calls functionCallFromParent() ParentComponent's render is executed, and it re-renders componentA and componentB and this loop continues. Whereas, I want only componentB to be re-rendered when componentA ask it to.
functionCallFromParent() is put inside render() in ComponentA which means it will get called every time the component is rendered/re-rendered which happens quite a lot! When that happens, the ParentComponent.parentFunction() will be called to set its own state which in turn causes a re-render to itself and its own children and this is how the loop is created. You should move this to the part of the code where you receive the response.

React trying to assign props to state variable doesn't work

I'm trying to assign props that I get from parent component and assign it to state in child component(because I want to manipulate the props data I assign it to state first).
When I log the state variable it comes out as an empty array but when I make a new variable in render and assign props to it and log it. It does show the data I need. Also, when I just log this.props I can definitely see that props holds the data I need.
I've assigned props to state a couple of times before, so I'm not sure what is so different this time for it not to work.
Parent component where I pass props to child:
<ShowAvailableTimeslots onClick={this.getSelectedTimeslot} allTimeSlots={this.state.AvailabletimeSlots} />
Child component where I try to assign props to state:
class ShowAvailableTimeslots extends Component {
constructor(props) {
super(props)
this.state = {
sliceEnd: 5,
sliceStart:0,
selectedSlotValue: "",
timeSlotArr: this.props.allTimeSlots,
// timeSlotSlice: timeSlotArr.slice(this.state.sliceStart, this.state.sliceEnd)
}
}
handleTimeSlotClick = (timeSlot) => {
this.setState({ selectedSlotValue: timeSlot }, () => {
this.props.onClick(this.state.selectedSlotValue)
console.log('time slot value', timeSlot)
});
}
previousSlots =()=>{
var test;
}
forwordSlots =()=>{
var test;
}
render() {
var timeSlotArrRender = this.props.allTimeSlots;
return (
<React.Fragment>
{console.log("state", this.state.timeSlotArr)} // --> doesn't show data
{console.log("props", this.props)} // --> does show data
{console.log("render variable", timeSlotArrRender )} // --> does show data
<button className="button btn" onClick={() => this.previousSlots()} disabled={this.state.sliceStart === 0}>left</button>
{/* {this.state.timeSlotArr.map(timeSlot => <a className="timeslot btn " key={timeSlot} value={timeSlot} onClick={() => this.handleTimeSlotClick(timeSlot)}>{timeSlot}</a>)
} */}
<button className="button btn">right</button>
</React.Fragment>
)
}
}
export default ShowAvailableTimeslots
the constructor is called when the component life cycle begins.
You are passing the this.state.AvailabletimeSlots from the parent and by then the constructor have already been called and the assignment for timeSlotArr is already done, so
timeSlotArr: this.props.allTimeSlots // will not work
You have to get help of life cycle methods or hooks
componentWillReceiveProps(nextProps){
this.setState({timeSlotArr: nextProps.allTimeSlots })
}
According to new changes you have to use
static getDerivedStateFromProps(nextProps, prevState){
return {
timeSlotArr: nextProps.allTimeSlots
};
}
I have it working just fine. https://jsfiddle.net/85zc4Lxb/
class App extends React.Component {
render() {
return (<Child passing="I am being passed to child" />);
}
}
class Child extends React.Component {
constructor(props) {
super(props)
this.state = {
passedProp: this.props.passing,
}
}
render() {
return (
<React.Fragment>
<button>{this.state.passedProp}</button>
</React.Fragment>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('container')
);
I try to recreate the scenario and it work try saving all your files again and then check
parents component
import React, { Component } from "react";
import TestOne from "./Components/TestOne/TestOne";
export class App extends Component {
state = {
x: "x data",
y: "y data",
};
render() {
return (
<div>
<TestOne x={this.state.x} allTimeSlots={this.state.y}/>
</div>
);
}
}
export default App;
Child component
import React, { Component } from "react";
export class TestOne extends Component {
constructor(props) {
super(props);
this.state = {
sliceEnd: 5,
sliceStart: 0,
selectedSlotValue: "",
timeSlotArr: this.props.x,
};
}
render() {
var timeSlotArrRender = this.props.allTimeSlots;
return (
<React.Fragment>
{console.log("state", this.state.timeSlotArr)}
{console.log("props", this.props)}
{console.log("render variable", timeSlotArrRender)}
<button className="button btn">right</button>
</React.Fragment>
);
}
}
export default TestOne;
Result:
I think you are missing this.setState({})

React component not rendering props coming from parent

class CyInfo extends Component {
foo(){
console.log(this.props.id);
return getAttributes(this.props.id)
}
render() {
return ( <Info data = {this.foo()}> </Info>)
}
}
this parent receive "props.id" and pass data value to children which is returned by getAttributes().
export default class Info extends Component {
constructor(props){
super(props)
}
/*componentWillReceiveProps(nextProps){
console.log(nextProps);
*/
render() {
console.log(this.props.data);
return (
<div id="info">{this.props.data}</div>
)
}
}
On child i can see props value on the console and in componentWillReceiveProps also.But array not rendering.
I try the use react-devtool. In react-devtool props seems passes the children but not rendering. Interestingly in react-devtool when i change the some of array's element array is rendering.
What did i do wrong.
EDIT:
[React-Devtool Screenshot][1]
I edited the react-devtool screenshot. Props are seems but component only renders initial value. In screenshot console error is favicon just ignore this
EDIT2:Console prints props array
EDIT 3:
JSON.stringify(this.props.data)
The props are coming from function(getattributes) which is call a method asynchronous and when this props passed the child there are not rendering.
I call async method directly in parent child and set state with callback in componentWillReceiveProps:
componentWillReceiveProps(nextProps){
self = this;
AsyncFunc(nextProps.id ,(error, result) => {
self.setState({data:result})
});
}
and render with
return (<div id="info">
{Array.isArray(this.state.data) && this.state.data.map((data) => {
return <div key={data._id}>{data.class}{data.predicate}{data.yuklem}</div>
})}</div>
)
As foo is a function, you have to pass to child component as:
return ( <Info data = {() => this.foo()}> </Info>)
Also, data is an array, you have to render using .map(), as follows:
export default class Info extends Component {
constructor(props){
super(props)
}
/*componentWillReceiveProps(nextProps){
console.log(nextProps);
*/
render() {
console.log(this.props.data);
return (
<div id="info">{this.props.data.map(( element, index ) => {
console.log(element);
<span key={index}> {element}</span>})}
</div>
)
}
}
As you have mentioned that this.data.props returns an array, and in order to render elements within an array, you need to map over the array elements and also check that the data is an array or not before rendering as initially the value may not be available or not an array
export default class Info extends Component {
constructor(props){
super(props)
}
/*componentWillReceiveProps(nextProps){
console.log(nextProps);
*/
render() {
console.log(this.props.data);
return (
<div id="info">
{this.props.data && Array.isArray(this.props.data) && this.props.data.map((data, index) => {
return <div key={index}>{data}</div>
})}</div>
)
}
}

How can I update the parent's state in React?

My structure looks as follows:
Component 1
- |- Component 2
- - |- Component 4
- - - |- Component 5
Component 3
Component 3 should display some data depending on state of Component 5.
Since props are immutable, I can't simply save its state in Component 1 and forward it, right? And yes, I've read about Redux, but I don't want to use it. I hope that it's possible to solve it just with react. Am I wrong?
For child-parent communication you should pass a function setting the state from parent to child, like this
class Parent extends React.Component {
constructor(props) {
super(props)
this.handler = this.handler.bind(this)
}
handler() {
this.setState({
someVar: 'some value'
})
}
render() {
return <Child handler = {this.handler} />
}
}
class Child extends React.Component {
render() {
return <Button onClick = {this.props.handler}/ >
}
}
This way the child can update the parent's state with the call of a function passed with props.
But you will have to rethink your components' structure, because as I understand components 5 and 3 are not related.
One possible solution is to wrap them in a higher level component which will contain the state of both component 1 and 3. This component will set the lower level state through props.
This is how to do it with the new useState hook.
Method - Pass the state changer function as a props to the child component and do whatever you want to do with the function:
import React, {useState} from 'react';
const ParentComponent = () => {
const[state, setState]=useState('');
return(
<ChildComponent stateChanger={setState} />
)
}
const ChildComponent = ({stateChanger, ...rest}) => {
return(
<button onClick={() => stateChanger('New data')}></button>
)
}
I found the following working solution to pass the onClick function argument from the child to the parent component:
Version with passing a method()
//ChildB component
class ChildB extends React.Component {
render() {
var handleToUpdate = this.props.handleToUpdate;
return (<div><button onClick={() => handleToUpdate('someVar')}>
Push me
</button>
</div>)
}
}
//ParentA component
class ParentA extends React.Component {
constructor(props) {
super(props);
var handleToUpdate = this.handleToUpdate.bind(this);
var arg1 = '';
}
handleToUpdate(someArg){
alert('We pass argument from Child to Parent: ' + someArg);
this.setState({arg1:someArg});
}
render() {
var handleToUpdate = this.handleToUpdate;
return (<div>
<ChildB handleToUpdate = {handleToUpdate.bind(this)} /></div>)
}
}
if(document.querySelector("#demo")){
ReactDOM.render(
<ParentA />,
document.querySelector("#demo")
);
}
Look at JSFiddle
Version with passing an Arrow function
//ChildB component
class ChildB extends React.Component {
render() {
var handleToUpdate = this.props.handleToUpdate;
return (<div>
<button onClick={() => handleToUpdate('someVar')}>
Push me
</button>
</div>)
}
}
//ParentA component
class ParentA extends React.Component {
constructor(props) {
super(props);
}
handleToUpdate = (someArg) => {
alert('We pass argument from Child to Parent: ' + someArg);
}
render() {
return (<div>
<ChildB handleToUpdate = {this.handleToUpdate} /></div>)
}
}
if(document.querySelector("#demo")){
ReactDOM.render(
<ParentA />,
document.querySelector("#demo")
);
}
Look at JSFiddle
I want to thank the most upvoted answer for giving me the idea of my own problem basically the variation of it with arrow function and passing param from child component:
class Parent extends React.Component {
constructor(props) {
super(props)
// without bind, replaced by arrow func below
}
handler = (val) => {
this.setState({
someVar: val
})
}
render() {
return <Child handler = {this.handler} />
}
}
class Child extends React.Component {
render() {
return <Button onClick = {() => this.props.handler('the passing value')}/ >
}
}
Hope it helps someone.
I like the answer regarding passing functions around. It's a very handy technique.
On the flip side you can also achieve this using pub/sub or using a variant, a dispatcher, as Flux does. The theory is super simple. Have component 5 dispatch a message which component 3 is listening for. Component 3 then updates its state which triggers the re-render. This requires stateful components, which, depending on your viewpoint, may or may not be an anti-pattern. I'm against them personally and would rather that something else is listening for dispatches and changes state from the very top-down (Redux does this, but it adds additional terminology).
import { Dispatcher } from 'flux'
import { Component } from 'React'
const dispatcher = new Dispatcher()
// Component 3
// Some methods, such as constructor, omitted for brevity
class StatefulParent extends Component {
state = {
text: 'foo'
}
componentDidMount() {
dispatcher.register( dispatch => {
if ( dispatch.type === 'change' ) {
this.setState({ text: 'bar' })
}
}
}
render() {
return <h1>{ this.state.text }</h1>
}
}
// Click handler
const onClick = event => {
dispatcher.dispatch({
type: 'change'
})
}
// Component 5 in your example
const StatelessChild = props => {
return <button onClick={ onClick }>Click me</button>
}
The dispatcher bundles with Flux is very simple. It simply registers callbacks and invokes them when any dispatch occurs, passing through the contents on the dispatch (in the above terse example there is no payload with the dispatch, simply a message id). You could adapt this to traditional pub/sub (e.g., using the EventEmitter from events, or some other version) very easily if that makes more sense to you.
I found the following working solution to pass the onClick function argument from the child to the parent component with a parameter:
Parent class:
class Parent extends React.Component {
constructor(props) {
super(props)
// Bind the this context to the handler function
this.handler = this.handler.bind(this);
// Set some state
this.state = {
messageShown: false
};
}
// This method will be sent to the child component
handler(param1) {
console.log(param1);
this.setState({
messageShown: true
});
}
// Render the child component and set the action property with the handler as value
render() {
return <Child action={this.handler} />
}}
Child class:
class Child extends React.Component {
render() {
return (
<div>
{/* The button will execute the handler function set by the parent component */}
<Button onClick={this.props.action.bind(this,param1)} />
</div>
)
} }
Whenever you require to communicate between a child to the parent at any level down, then it's better to make use of context. In the parent component define the context that can be invoked by the child, such as:
In the parent component, in your case component 3,
static childContextTypes = {
parentMethod: React.PropTypes.func.isRequired
};
getChildContext() {
return {
parentMethod: (parameter_from_child) => this.parentMethod(parameter_from_child)
};
}
parentMethod(parameter_from_child){
// Update the state with parameter_from_child
}
Now in the child component (component 5 in your case), just tell this component that it wants to use the context of its parent.
static contextTypes = {
parentMethod: React.PropTypes.func.isRequired
};
render() {
return(
<TouchableHighlight
onPress = {() => this.context.parentMethod(new_state_value)}
underlayColor='gray' >
<Text> update state in parent component </Text>
</TouchableHighlight>
)}
You can find the Demo project in this GitHub repository.
It seems that we can only pass data from parent to child as React promotes unidirectional data flow, but to make the parent update itself when something happens in its "child component", we generally use what is called a "callback function".
We pass the function defined in the parent to the child as "props" and
call that function from the child triggering it in the parent
component.
class Parent extends React.Component {
handler = (Value_Passed_From_SubChild) => {
console.log("Parent got triggered when a grandchild button was clicked");
console.log("Parent->Child->SubChild");
console.log(Value_Passed_From_SubChild);
}
render() {
return <Child handler = {this.handler} />
}
}
class Child extends React.Component {
render() {
return <SubChild handler = {this.props.handler}/ >
}
}
class SubChild extends React.Component {
constructor(props){
super(props);
this.state = {
somethingImp : [1,2,3,4]
}
}
render() {
return <button onClick = {this.props.handler(this.state.somethingImp)}>Clickme<button/>
}
}
React.render(<Parent />,document.getElementById('app'));
HTML
----
<div id="app"></div>
In this example we can make data pass from sub child → child → parent by passing function to its direct child.
Most of the answers given previously are for React.Component-based designs. If you are using useState in the recent upgrades of the React library, then follow this answer.
I've used a top rated answer from this page many times, but while learning React, I've found a better way to do that, without binding and without an inline function inside props.
Just look here:
class Parent extends React.Component {
constructor() {
super();
this.state = {
someVar: value
}
}
handleChange = (someValue) => {
this.setState({someVar: someValue})
}
render() {
return <Child handler={this.handleChange} />
}
}
export const Child = ({handler}) => {
return <Button onClick={handler} />
}
The key is in an arrow function:
handleChange = (someValue) => {
this.setState({someVar: someValue})
}
You can read more here.
Simply pass the parent's setState function via props to the child component.
function ParentComp() {
const [searchValue, setSearchValue] = useState("");
return <SearchBox setSearchValue={setSearchValue} searchValue={searchValue} />;
}
then in child component:
function SearchBox({ searchValue, setSearchValue }) {
return (
<input
id="search-post"
type="text"
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
placeholder="Search Blogs ..."
/>
)
}
A second example to handle click from child component:
// We've below function and component in parent component
const clickHandler = (val) => {
alert(`httpRequest sent. \nValue Received: ${val}`);
};
// JSX
<HttpRequest clickHandler={clickHandler} />
this is how you get function from parent component then pass a value and fire clickHandler through it.
function HttpRequest({ clickHandler }) {
const [content, setContent] = useState("initialState");
return (
<button onClick={() => clickHandler(content)}>
Send Request
</button>
);
}
export default HttpRequest;
We can create ParentComponent and with a handleInputChange method to update the ParentComponent state. Import the ChildComponent and we pass two props from the parent to the child component i.e., the handleInputChange function and count.
import React, { Component } from 'react';
import ChildComponent from './ChildComponent';
class ParentComponent extends Component {
constructor(props) {
super(props);
this.handleInputChange = this.handleInputChange.bind(this);
this.state = {
count: '',
};
}
handleInputChange(e) {
const { value, name } = e.target;
this.setState({ [name]: value });
}
render() {
const { count } = this.state;
return (
<ChildComponent count={count} handleInputChange={this.handleInputChange} />
);
}
}
Now we create the ChildComponent file and save it as ChildComponent.jsx. This component is stateless because the child component doesn't have a state. We use the prop-types library for props type checking.
import React from 'react';
import { func, number } from 'prop-types';
const ChildComponent = ({ handleInputChange, count }) => (
<input onChange={handleInputChange} value={count} name="count" />
);
ChildComponent.propTypes = {
count: number,
handleInputChange: func.isRequired,
};
ChildComponent.defaultProps = {
count: 0,
};
export default ChildComponent;
If you want to update the parent component,
class ParentComponent extends React.Component {
constructor(props){
super(props);
this.state = {
page: 0
}
}
handler(val){
console.log(val) // 1
}
render(){
return (
<ChildComponent onChange={this.handler} />
)
}
}
class ChildComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
page: 1
};
}
someMethod = (page) => {
this.setState({ page: page });
this.props.onChange(page)
}
render() {
return (
<Button
onClick={() => this.someMethod()}
> Click
</Button>
)
}
}
Here onChange is an attribute with "handler" method bound to its instance. We passed the method handler to the Child class component, to receive via the onChange property in its props argument.
The attribute onChange will be set in a props object like this:
props = {
onChange: this.handler
}
and passed to the child component.
So the child component can access the value of name in the props object like this props.onChange.
It's done through the use of render props.
Now the child component has a button “Click” with an onclick event set to call the handler method passed to it via onChange in its props argument object. So now this.props.onChange in the child holds the output method in the parent class.
Reference and credits: Bits and Pieces
If this same scenario is not spread everywhere you can use React's context, especially if you don't want to introduce all the overhead that state management libraries introduce. Plus, it's easier to learn. But be careful; you could overuse it and start writing bad code. Basically you define a Container component (that will hold and keep that piece of state for you) making all the components interested in writing/reading that piece of data to/from its children (not necessarily direct children).
Context - React
You could also use a plain React properly instead.
<Component5 onSomethingHappenedIn5={this.props.doSomethingAbout5} />
Pass doSomethingAbout5 up to Component 1:
<Component1>
<Component2 onSomethingHappenedIn5={somethingAbout5 => this.setState({somethingAbout5})}/>
<Component5 propThatDependsOn5={this.state.somethingAbout5}/>
<Component1/>
If this is a common problem, you should starting thinking moving the whole state of the application to somewhere else. You have a few options, the most common are:
Redux
Flux
Basically, instead of managing the application state in your component you send commands when something happens to get the state updated. Components pull the state from this container as well so all the data is centralized. This doesn't mean you can't use local state any more, but that's a more advanced topic.
We can set the parent state from a child component by passing a function into the child component as props as below:
class Parent extends React.Component{
state = { term : ''}
onInputChange = (event) => {
this.setState({term: event.target.value});
}
onFormSubmit = (event) => {
event.preventDefault();
this.props.onFormSubmit(this.state.term);
}
render(){
return (
<Child onInputChange={this.onInputChange} onFormSubmit=
{this.onFormSubmit} />
)
}
}
class Child extends React.Component{
render(){
return (
<div className="search-bar ui segment">
<form className="ui form" onSubmit={this.props.onFormSubmit}>
<div class="field">
<label>Search Video</label>
<input type="text" value={this.state.term} onChange=
{this.props.onInputChange} />
</div>
</form>
</div>
)
}
}
This way, the child will update the parent state onInputChange and onFormSubmit are props passed from parents. This can be called from event listeners in the child, hence the state will get updated there.
Parent Component
function Parent() {
const [value, setValue] = React.useState("");
function handleChange(newValue) {
setValue(newValue);
}
// We pass a callback to Child
return <Child value={value} onChange={handleChange} />;
}
Child Component
function Child(props) {
function handleChange(event) {
// Here, we invoke the callback with the new value
props.onChange(event.target.value);
}
return <input value={props.value} onChange={handleChange} />
}
Here is a short snippet to get two ways binding data.
The counter show the value from the parent and is updated from the child
class Parent extends React.Component {
constructor(props) {
super(props)
this.handler = this.handler.bind(this)
this.state = {
count: 0
}
}
handler() {
this.setState({
count: this.state.count + 1
})
}
render() {
return <Child handler={this.handler} count={this.state.count} />
}
}
class Child extends React.Component {
render() {
return <button onClick={this.props.handler}>Count {this.props.count}</button>
}
}
ReactDOM.render(<Parent />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
This is the way I do it:
type ParentProps = {}
type ParentState = { someValue: number }
class Parent extends React.Component<ParentProps, ParentState> {
constructor(props: ParentProps) {
super(props)
this.state = { someValue: 0 }
this.handleChange = this.handleChange.bind(this)
}
handleChange(value: number) {
this.setState({...this.state, someValue: value})
}
render() {
return <div>
<Child changeFunction={this.handleChange} defaultValue={this.state.someValue} />
<p>Value: {this.state.someValue}</p>
</div>
}
}
type ChildProps = { defaultValue: number, changeFunction: (value: number) => void}
type ChildState = { anotherValue: number }
class Child extends React.Component<ChildProps, ChildState> {
constructor(props: ChildProps) {
super(props)
this.state = { anotherValue: this.props.defaultValue }
this.handleChange = this.handleChange.bind(this)
}
handleChange(value: number) {
this.setState({...this.state, anotherValue: value})
this.props.changeFunction(value)
}
render() {
return <div>
<input onChange={event => this.handleChange(Number(event.target.value))} type='number' value={this.state.anotherValue}/>
</div>
}
}
As per your question, I understand that you need to display some conditional data in Component 3 which is based on the state of Component 5. Approach:
The state of Component 3 will hold a variable to check whether Component 5's state has that data
An arrow function which will change Component 3's state variable.
Passing an arrow function to Component 5 with props.
Component 5 has an arrow function which will change Component 3's state variable
An arrow function of Component 5 called on loading itself
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Class Component3 extends React.Component {
state = {
someData = true
}
checkForData = (result) => {
this.setState({someData : result})
}
render() {
if(this.state.someData) {
return(
<Component5 hasData = {this.checkForData} />
//Other Data
);
}
else {
return(
//Other Data
);
}
}
}
export default Component3;
class Component5 extends React.Component {
state = {
dataValue = "XYZ"
}
checkForData = () => {
if(this.state.dataValue === "XYZ") {
this.props.hasData(true);
}
else {
this.props.hasData(false);
}
}
render() {
return(
<div onLoad = {this.checkForData}>
//Conditional Data
</div>
);
}
}
export default Component5;
To set state of parent in the child you can use callback.
const Child = ({handleClick}) => (
<button on click={() => handleClick('some vale')}>change value</button>
)
const parent = () => {
const [value, setValue] = useState(null)
return <Child handleClick={setValue} />
}
In your structure it seems Components 1 an 3 are brothers. So you has 3 options:
1- Put the state into the parent of them(not recommended for 4 layer parent-child).
2- Use useContext and useRducer(or useState) together.
3- Use state managers like redux, mobx ...
This seem to work for me
Parent:
...
const [open, setOpen] = React.useState(false);
const handleDrawerClose = () => {
setOpen(false);
};
...
return (
<PrimaryNavigationAccordion
handleDrawerClose={handleDrawerClose}
/>
);
Child:
...
export default function PrimaryNavigationAccordion({
props,
handleDrawerClose,
})
...
<Link
to={menuItem.url}
component={RouterLink}
color="inherit"
underline="hover"
onClick={() => handleDrawerClose()}
>
{menuItem.label}
</Link>
You can do it by passing a reference for the parent to child, as:
Having a parent component A in A.js with a method updateAState
Having a child component B in B.js
Having a wrapper function that renders <A><B></B></A> in C.js
In C.js you can use useRef as following:
import React, { useRef } from "react";
export default function C()
{
const parentARef = useRef();
const handleChildBClick = () => parentARef.current.updateAState();
return (
<A ref={parentARef}>
<B onClick={handleChildBClick}>
</B>
</A>
);
}
Guidance Reference: https://stackoverflow.com/a/56496607/1770571
Data cannot be passed from child to parent in React. Data must be passed from parent to child. In this case, you can use either the built-in Context API or a third-party state management solution such as Redux, Mobx, or Apollo GraphQL. However, if your app structure is too small, you can store your data in your parent element and then send it to your child via prop drilling. But if your project is larger, it will be messy.
<Footer
action={()=>this.setState({showChart: true})}
/>
<footer className="row">
<button type="button" onClick={this.props.action}>Edit</button>
{console.log(this.props)}
</footer>
Try this example to write inline setState, it avoids creating another function.

Categories