Re-render child after parent state change with get request - javascript

So I'm a beginner with react and I was wondering how to re-render the child after setting the state in the parent (from the child). Here's a code sample. I have a function that calls a GET request using Axios and when I press the button in the child component ideally it will update the state in the parent and also re-render the child but it only does the former.
Parent:
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
data: []
}
}
fetchData = () => {
axios
.get(url)
.then(res => this.setState({data: res.data}))
}
Render() {
return (<Child data={this.state.data} fetchData={this.fecthData}/>)
}
// ...
Child:
class Child extends Component {
// ...
render() {
const { data, fetchData } = this.props
// render data
return <button onClick={fetchData}>Change data then fetch</button>
}
}
Also, are you supposed to make a local state in the Child and set it as a copy of the Parent's state or just passing it down as a prop is okay?

Your parent component holds the data and the child uses it. It seems to me you're doing it the right way. Here is a fully working example:
Codesandbox
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
data: []
};
this.updateData = this.updateData.bind(this);
}
async fetchData() {
const response = await fetch("https://jsonplaceholder.typicode.com/posts");
return response.json();
}
updateData() {
this.setState({ data: [] }) // Creates a flicker, just so you see it does refresh the child
this.fetchData().then((res) => this.setState({ data: res }));
}
render() {
return <Child data={this.state.data} onAction={this.updateData} />;
}
}
Note I renamed your child prop fetchData into onAction (I don't know what's the name of the action that triggers a refresh, could be onRefresh). It's always best to see components props with separation between data attributes and event attributes.
Even standard components have it this way: <input value={user.firstname} onChange={doSomething} />. So, better to prefix events by on, then the parent decides what to do with it. It's not the child's concern.
class Child extends Component {
render() {
const { data, onAction } = this.props;
return (
<>
<button onClick={onAction}>Change data then fetch</button>
{data.map((item) => (
<div key={item.id}>
{item.id} - {item.title}
</div>
))}
</>
);
}
}

Related

Why getDerivedStateFromProps does not allow to re-render with the state update? Not a problem for componentWillReceiveProps - ReactJS

I'm trying to move from componentWillReceiveProps to getDerivedStateFromProps and in some cases, I was successful but when the case is append the props to the existing state, then things start to not behaving the same way. When a make an update to the component's state, the state changes (and the component did after updated) but still renders the previous state. Something weird happens when using getDerivedStateFromProp instead of componentWillReceiveProps. It seems that method does not handle well with 'internal' changes. In the following example, I have getDerivedStateFromProp on Child and it works, but because is only rendering the props. This behavior was also observed in a simpler example where I didn't have any child components and was just rendering state changes.
The code below shows a child component that is used to print/show the data received by props while uses a delete data handler (to remove data that is stored at Parent from child component interaction). When using getDerivedStateFromProps() I can't access to this.state and the prevState doesn't mean the same since the state is accumulative. And when I remove data from the child component, doesn't update the props of the child (while using componentWillReceiveProps was OK). So, I do not find a way to substitute my UNSAFE_componentWillReceiveProps
componentWillReceiveProps:
UNSAFE_componentWillReceiveProps(nextProps){
this.setState({
data: [...this.state.data,...nextProps.data]
})
}
getDerivedStateFromProps:
static getDerivedStateFromProps(nextProps,state) {
if (!isEqual(nextProps.data, state.data)) {
return {
data: [...state.data, ...nextProps.data]
};
}
return null;
}
The original code that works as intended (before getDerivedStateFromProps on Parent Comp.)
DataConsole - Parent Component:
export class DataConsole extends Component {
// Used for unsubscribing when our components unmount
unsub = null;
static defaultProps = {
data: [],
};
constructor(props) {
super(props);
this.state = {
data: [],
};
this.handleTableRowClick = this.handleTableRowClick.bind(this);
}
UNSAFE_componentWillReceiveProps(nextProps){
this.setState({
data: [...this.state.data,...nextProps.data]
})
}
handleTableRowClick(key) {
console.log(
"handleTable",
key,
this.state.data[key],
this.state.data.length
);
const e = this.state.data.splice(key, 1)
//console.log("remove?", e , this.state.data.length)
this.setState({
undoDataRemove: e
});
}
render() {
return (
<div>
<Container
fluid
style={{ paddingLeft: 0, paddingRight: 0 }}
className="DataContainer"
>
<Row noGutters>
<Col sm={8} className="ConsoleTable">
<div>
<DataViewer
data={this.state.data}
rowClickHandler={this.handleTableRowClick}
/>
</div>
...
DataViewer - Child Component
import isEqual from "react-fast-compare";
...
export class DataViewer extends Component {
static defaultProps = {
data: [],
};
constructor(props){
super(props)
this.state={data: []}
}
componentDidUpdate() {
console.log("DataViewer updated");
}
static getDerivedStateFromProps(nextProps, prevProps) {
console.log(nextProps, prevProps)
if (!isEqual(nextProps.data, prevProps.data)) {
return {
data: nextProps.data
};
}
return null;
}
render() {
return (
<div className={"TableData"}>
<Table responsive="lg" striped borderless hover>
<tbody>
{this.state.data.map((elem, ids) => {
if (!isEmpty(elem)) {
return (
<tr key={ids} onClick={() => this.props.rowClickHandler(ids)}>
<td>{ids + 1}</td>
{Object.keys(elem).map(function (value, idx) {
return (
<td key={idx}>
{value}:{elem[value]}
</td>
);
})}
</tr>
);
} else {
return null;
}
})}
</tbody>
</Table>
</div>
);
}
}
There is a bug in your code that causes your problem, and it is unrelated to getDerivedStateFromProps and UNSAFE_componentWillReceiveProps.
The faulty line is this:
const e = this.state.data.splice(key, 1)
It changes this.state.data without calling setState. Never do that. The only way you are ever allowed to make any changes to this.state is via this.setState or by returning something to be merged into state from getDerivedStateFromProps, not any other way.
In order to more easily cope with changes deep in your state tree, immutable libraries come in handily. immer is currently among the top candidates in this category. Using immer, you can modify state any way you want, as long as you wrap it into a produce call using the pattern this.setState(produce(this.state, newState => { /* change newState here */ })):
import produce from 'immer';
// ...
this.setState(produce(this.state, newState => {
const e = newState.data.splice(key, 1);
newState.undoDataRemove = e;
}));
Hope, this can help.

Passing data from child to parent , but unable to setState

So, i am trying to pass data from child component to parent component. I was able to do this through using props and is able to print the data using console.log. However, when i try to setState in the parent component using this data from child component. I am stuck in infinite loop of updating state which caused error "Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops."
My code is of below
Parent component
import React, { Component } from 'react'
import TagsPaper from './tagsPaper'
import CurrentTagPaper from './currentTagPaper'
import ApiCall from './Backend/apiCall';
export default class Tags extends Component {
constructor(props) {
super(props);
this.state = {
tags: ["apple"],
toUpdate: true,
};
}
componentDidMount() {
}
getTags = (e) => {
const array = e;
console.log(array)
this.setTags(array)
return array
}
setTags(e) {
this.setState({
tags: e
})
}
render() {
return (
<div >
<p>
Get recommendations for articles that matches your
interests when you follow more tags:
</p>
<b>
You are following
</b>
<TagsPaper></TagsPaper>
<b>
From this article
</b>
{/* pass Tag array name here*/}
<CurrentTagPaper tagArray={this.state.tags} />
<ApiCall getTags={this.getTags} />
</div>
)
}
}
Child component
import React, { Component } from 'react';
import '../App.css';
export default class ApiCall extends Component {
constructor(props) {
super(props);
this.state = {
response: '',
post: '',
responseToPost: '',
tags: this.props.tags
};
this.sendTags = this.sendTags.bind(this);
}
componentDidMount() {
this.getTagApi()
}
getTagApi(){
this.callApi()
.then(res => this.setState({ response: res.express }))
.catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch('http://localhost:5000/api/scrap');
const body = await response.json();
if (response.status !== 200) throw Error(body.message);
return body;
};
sendTags = (value) => {
this.props.getTags(value);
}
handleSubmit = async e => {
e.preventDefault();
const response = await fetch('http://localhost:5000/api/forms', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ post: this.state.post }),
});
const body = await response.text();
this.setState({ responseToPost: body });
};
render() {
let tagArray = [...this.state.response];
this.sendTags(tagArray);
return (
<div className="App">
{/* {tagArray.map(e => <p key={e}>{e}</p>)} */}
<form onSubmit={this.handleSubmit}>
<p>
<strong>Post to Server:</strong>
</p>
<input
type="text"
value={this.state.post}
onChange={e => this.setState({ post: e.target.value })}
/>
<button type="submit">Submit</button>
</form>
<p>{this.state.responseToPost}</p>
</div>
);
}
}
When the parent changes state, it renders the child. When the child renders, it updates the parent state. There would be a few clever ways to fix this, but your ultimate problem is that copying data into both a parent and child's state isn't very idiomatic.
Generally, you should "hoist" any state used by both parent and child to only exist in the parent, and this "hoisting" should happen right off the API call.
In ApiCall, you could switch to
getTagApi(){
this.callApi()
.then(res => this.sendTags(res.express))
.catch(err => console.log(err));
}
In your parent Tags component, you would need to pass the parents tag state back down.
<ApiCall getTags={this.getTags} tags={this.state.tags} />
Your ApiTags would then just use the parent's tags.
let tagArray = this.props.tags;
You also would not want to copy this.props.tags into this.state during construction.
React expects that there be one source-of-truth for all data. If a higher-level component needs the data as well as a lower-level component, you should always hoist the data, as in, pass it up whenever it changes, and pass it back down as a prop on every render.

fetch Data when context changes in React

I need to make a new api request to fetch data for a given dataId.
this value lives in the Context.
import { MyContext } from './Context'
class MyComponent extends Component {
constructor(props) {
super(props)
this.state = {
dataId: this.context.state.dataId // tried setting state first but didn´t work.
}
this.details = this.details.bind(this)
}
details() {
fetch('https://api.mydomain.com/' + this.context.state.dataId)
.then(response => response.json())
.then(data => this.setState({ data: data }));
}
componentDidMount() {
this.details()
}
render() {
return(
<MyContext.Consumer>
{(context) => (
<div>data: {JSON.stringify(data)} dataId: {context.state.dataId}</div>
)}
</MyContext.Consumer>
)
}
}
MyComponent.contextType = MyContext;
export default MyComponent
from others components I can set new values like
this.context.setDataId(1)
and this will show up correctly but the problem is that is not making a new fetch to get new data for the dataId that changed in the Context.
not sure what´s the correct lifecycle method I can use to detect changes in the context and make a new call to this.details()
I didn´t add the Context code here because it works fine. but if you need to see it please let me know.
In react, you must use life cycle hooks to inspect data such as props or context, to know if the state needs to update for your component. The most common life cycle hook for this purpose is componentDidUpdate(). it gives you the ability to decide whether or not your component needs to update state/rerender based on changes in props that caused the component to update. the following should work for your use case:
import { MyContext } from './Context'
class MyComponent extends Component {
state = {
data:[],
dataId:null
}
details = () => {
// we only want to update if dataId actually changed.
if(this.context.state.dataId !== this.state.dataId){
this.setState({dataId:this.context.state.dataId});
fetch('https://api.mydomain.com/' + this.context.state.dataId)
.then(response => response.json())
.then(data => this.setState({ data: data }));
}
}
componentDidMount() {
this.details()
}
componentDidUpdate() {
this.details();
}
render() {
return(
<MyContext.Consumer>
{(context) => (
<div>data: {JSON.stringify(this.state.data)} dataId: {context.state.dataId}</div>
)}
</MyContext.Consumer>
)
}
}
MyComponent.contextType = MyContext;
export default MyComponent;

React component of grandparent callback doesnt re-render page after being call in grandchild

I am calling a handle method (to change state) in a <grandchild> component but it stop rendering after a couple of callback in the <grandparent> component.
I have tried to:
setting bind correctly with both this.bind in construct and arrow method.
making sure the call back is call everytime the prop.callback is call.
This is an example of what I'm trying to do with graphql server:
Grandparent Component
//Graphql constant for query
const ApolloConstant = gpl`
...etc
class Grandparent extends Component {
constructor(props) {
super(props);
this.state = { vars: 'query_string' }
}
handler = (args) => {
this.setState({vars: args})
}
render() {
return (
// For requerying graphql with search
<input onChange={() => this.setState(vars: e.target.value)} />
<Query variable={this.state.vars}>
...code -> some_data_arr
{<ChildComponent data={some_data_arr} handler={this.handler}/>}
</Query>
);
}
}
Child Component
//This component will take in an arr of obj and display a obj list
// if one of the obj is clicked then render a child component to display that single obj
class Child extends Component {
constructor(props) {
super(props);
this.state = {
singleData: null
}
}
render() {
return (
// Ternary operator here for conditional rendering
{
this.state.singleData
? <Grandchild data={this.state.singleData} handleParentData={this.props.handler} />
: this.display_data(this.props.data)
}
);
}
//Method to call to display objects
display_data = () => {
this.props.map() =>
<div onClick={this.setState({singleData: data})} > ...some code to display data <div/>
}
}
Grandchild Component
class Grandchild extends Component {
render() {
return (
{...do some code with object props here}
<Button onclick={(this.props.handleParentData(vars))} >Btn</Button>
);
}
}
When I test this, everything works for the first 3-4 render then no more re-rendering even though the callback is going through. I check to see if the method in <grandparent> is being call and it does but the state stop changing. Even after going to a new route (react router) and then coming back, I still cant change state with that callback.
<Button onclick={(this.props.handleParentData(vars))} >Btn</Button>
I think the problem is the function being called right into the onclick prop, you should probably have it wrapped in another function so it is only called when you actually trigger the onclick listener:
handleClick = () => {
this.props.handleParentData(vars)
}
render() {
return (
{...do some code with object props here}
<Button onclick={(this.handleClick)} >Btn</Button>
);
}

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