Async update parent props to child component - reactjs

How to send updated props of parent component to child component after async operation. Is there any lifecycle method available
class SignUp extends Component {
constructor(props){
super(props);
this.state = {...props};
}
componentDidMount(){
this.props.UserSignupType();
}
render(){
<Inputs {...this.state} >
}
}
const stateToProps = state => ({
signupInputs: state.signupInputs
});
connect(stateToProps, null)(SignUp);
class Inputs extends Component {
constructor(props){
super(props);
this.state = {...props};
}
render(){
if(!this.state.isLoaded) {
return <Loading />
} else {
let inputs = [];
this.state.inputs.forEach(function(input){
inputs.push(<TextField value={input.text}>)
});
return <View>{inputs}</View>
}
}
}
Note: Possible duplicate of Pass props to child components after Async update

First correction: You do not need to add data in state from props if you are not updating it. Also if you are updating it, it should be in state of the child component. So parent component can directly pass props to child component like this:
<Inputs {...this.props} >
Now in Inputs component you can directly use props if it's not going to change or you can add it to state using componentWillReceiveProps function as shown below:
componentWillReceiveProps(nextProps){
this.setState({...nextProps});
}

The state can be updated via getDerivedStateFromProps.
Its a static method, where you can compare the state and the new props and update the state.
static getDerivedStateFromProps(nextProps, prevState) {
// Store myId in state.
if (nextProps.myId !== prevState.myId) {
// this updates the state
return {
myId: nextProps.myId
};
}
// This implies it won't update the state
return null;
}

Why do you need to set the state of the child component to the props received? I would just use the props directly.
In your case I am not sure that the constructor gets called on refresh, thus the state of the child component does not get updated. Try using the props directly.

Related

When I put props in state, state is not updated even if props is updated

When a parent component passes a props named title to a child component and puts the props into the child component's state, when the parent component's title is moved to the child component instead, the props is updated, but the state is not updated and the past title is retained.
Props are updated, but the State using Props is not updated.
How do I update the state of a child component when the parent component is updated?
constructor() {
super();
this.state = {
title:''
};
}
<Child title={this.state.title}/>)}
constructor(props) {
super(props)
this.state = {
title: props.title
};
}
Don't store the same state in multiple locations; synchronizing them will be a pain. Instead, pass down the title and a state setter function to the child, eg:
<Child
title={this.state.title}
setTitle={title => { this.setState({ title }); }}
/>
And don't keep state in the child - only use the title prop. Call the setTitle prop when you need to update from the child.
Copying props to state seems redundant and anti-pattern, but if you need to do it (for reasons like editing details in a form), you can copy the props into state using constructor and update (conditionally) it when the props' value is changed using componentDidUpdate lifecycle method:
class Child extends Component {
constructor(props) {
super(props);
this.state = {
title: this.props.title // Copy
};
}
componentDidUpdate(prevProps, prevState) {
if (this.props.title !== prevProps.title) {
this.setState({
title: this.props.title // Update conditionally when it is changed in props
});
}
}
render() {
return <>{this.state.title}</>;
}
}

How to update the value of a prop upon a state change in React

I have a parent React component (MainComponent) that renders a child component (theKeyComponent) and passes a constant as a prop (myID). The parent component also tracks the state 'activeLink'.
import theKeyComponent from "../components/theKeyComponent.jsx";
export default class MainComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
activeLink: '#section1'
}
}
render(){
const myId = this.state.activeLink === '#section1' ? '0001' : '0002';
return(){
<theKeyComponent myID={myID} />
<otherComponent otherPropsHere={otherProps} />
}
}
}
Here's a bit from inside the KeyComponent
export default class HelpScoutBeacon extends React.Component {
static propTypes = {
myID: PropTypes.string.isRequired
}
componentDidMount() {
myAPImethod('init', this.props.myID);
}
render(){return(){}}
}
I want to change the value of the constant myID depending on the value of 'activeLink'. This is not a problem when both components are mounted for the first time. However, when the value of 'activeLink' changes 'myID' doesn't change since the child component is already mounted.
I'm struggling to see what would be the 'React way' of doing this. Should 'myID' be set as another state and the function that sets the state for activeLink should include another one to set the state of myID? Or is this overcomplicating things and there's an easier way to re-render only that particular child component so that it considers the new value of myID.
I'm new to React so I was hoping I could get some clarification form SO community.
This is not a problem when both components are mounted for the first
time. However, when the value of 'activeLink' changes 'myID' doesn't
change since the child component is already mounted.
The issue is with regards to how you handle the trigger of the API call.
componentDidMount will only trigger when the component was initially mounted. This will not be triggered if a state is updated. You are going to want to use componentDidUpdate React Lifecycle as well in augmentation to componentDidMount. componentDidUpdate will trigger when your activeLink state changes because you pass it as props to theKeyComponent
componentDidUpdate() {
myAPImethod('init', this.props.myID);
}
Reference: https://reactjs.org/docs/react-component.html#componentdidupdate
import theKeyComponent from "../components/theKeyComponent.jsx";
export default class MainComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
activeLink: '#section1'
}
}
static getDerivedStateFromProps(props, state) {
if (props.activeLink !== state.activeLink ) {
// updating the state here if there are any changes in props.
return {
activeLink: props.activeLink,
};
}
// Return null if the props hasn't changed
return null;
}
use getDerivedStateFromProps hook to find out changes of props. you need to update the changes of props in state.
render(){
// now, here your state is updated.
const myId = this.state.activeLink === '#section1' ? '0001' : '0002';
return (
<theKeyComponent myID={myID} />
<otherComponent otherPropsHere={otherProps} />
)
}
Any suggestions are welcome.

Trouble with race condition in component lifecycle methods - How to get data in time?

I've tried a number of configurations to solve this issue, but basically I'd like parent component to run an async function, morph the result, and pass that as props to my child. I'm not really sure where to incorporate lifecycle methods, namely because ComponentDidMount will run, set state, and re-render the parent component just fine and a console.log of state inside the parent render method shows the state updated correctly with my fields. But the child component renders and shows an empty array in props.
Any ideas on this one?
import * as React from 'react';
interface CustomState {
fields: any;
}
class Parent extends React.Component<{}, CustomState> {
constructor(props) {
super(props);
this.state = {
fields: [],
};
}
public async componentDidMount() {
let fields = await asyncCall();
const viewFields = this.buildViewFields(fields);
this.setState({
fields: viewFields,
});
}
private buildViewFields(fields) {
const viewFields = fields.map((f) => {
return {
name: f.Title,
};
});
return viewFields;
}
public render() {
// ISSUE: this.state.fields if logged here shows viewfields
return <ChildComponent fields={this.state.fields}></ChildComponent>;
}
}
export default Parent;
Here's my child component:
class ChildComponent extends React.Component<Props, State> {
constructor(props) {
super(props);
this.state = {
viewFields: this.props.fields,
};
}
public render() {
console.log(this.state); // DOES NOT HAVE VIEWFIELDS
return (
<GrandChildComponent
fields={this.state.viewFields}
/>
);
}
}
export default ChildComponent;
You don't need to do this part
this.state = {
viewFields: this.props.fields,
};
You can just use props.fields right away inside your ChildComponent.
What happens right now is:
Parent renders, fields is empty
Child renders, it sets viewFields to empty fields array inside constructor
Parent fetches actual fields and calls setState with them, rerenders self and Child
Child rerenders, but constructor is not invoked again.
If you want to actually setState inside Child from Parent props you need to use lifecycle method componentDidUpdate. It is rarely needed though and usually is just bad practice.

Access updated state values of child from parent in react

I have one component 'Checkin', which further includes a child component 'CheckinGraph'. Checkin graph component has three states values which initialize to 0 but, on the ComponentDidMount life cycle, their values change from 0 to specific values. I want to access these state variables from the parent component.
Here is my code by which I access these state variables.
Child Component
class CheckinGraph extends React.PureComponent{
constructor(props)
{
super(props)
this.state={
totalMaleCount:0,
totalFemaleCount:0,
totalUnspecifiedCount:0
}
}
//Callback function for parent
getCount=()=>
{
let { totalMaleCount, totalFemaleCount, totalUnspecifiedCount}=this.state;
let arr=[totalCount,totalMaleCount,totalFemaleCount, totalUnspecifiedCount];
return arr;
}
componentDidMount()
{
this.setState({totalMaleCount:res.data.maleListCount}); //data is coming from API
this.setState({totalFemaleCount:res.data.femaleListCount});
this.setState({totalUnspecifiedCount:res.data.notSpecified});
}
}
Parent component
class CheckIn extends React.Component{
constructor(props)
{
this.state={}
this.graph=React.createRef();
}
componentDidMount()
{
let total=this.graph.current.getCount();
console.log(total);
}
render()
{
return(
<div>
<CheckinGraph ref={this.graph} />
</div>
)
}
}
Now the problem is, I able to access values from child to parent, but it is the initial values, not the updated values. Any idea what I am doing wrong?
Two approaches:
Use Redux to manage state and then dispatch an action from CHild Component
Pass onChange Function to Child Component and whenever value updated to call the function from Child Component.
The onChange Function is where you will have access to updated value and then you can do whatever you want to do in Parent.

Why aren't parent Props Equal to Child state when Child state actually reference props from parent

I am passing props from Parent component into Child's state but They are out of sync.
What I tried:
State Updates May Be Asynchronous, I have taken care of that using a call back instead of returning an object.
Objects are passed by reference, but the prop i used is a string.
I am using React 16 and es6 syntax
class Parent extends React.Component {
state = {
isHidden: false
}
render() {
console.log('PROPS from PARENT', this.state.isHidden)
return <div>
<Child isOpen={this.state.isHidden} />
<button onClick={this.toggleModal}>Toggle Modal</button>
</div>
}
toggleModal = () => this.setState(state => ({isHidden: !state.isHidden}))
}
class Child extends React.Component {
state = {
isHidden: this.props.isOpen
}
render() {
console.log('STATE of CHILD:',this.state.isHidden)
return <p hidden={this.state.isHidden}>Hidden:{this.state.isHidden}</p>
}
}
ReactDOM.render(<Parent/>, document.getElementById('app'));
Here a codepen PEN - notice the redered element is supposed to be hidden based on the state(state depends on props from parent)
Use componentWillReceiveProps which call when changes in props occur.
class Child extends React.Component {
state = {
isHidden: this.props.isOpen
}
componentWillReceiveProps(props) {
if (props.isOpen != this.state.isHidden)
this.setState({
isHidden: props.isOpen
})
}
render() {
console.log('STATE of CHILD:', this.state.isHidden)
return <p hidden = {
this.state.isHidden
} > Hidden: {
this.state.isHidden
} < /p>
}
}
If you remove the state definition from the child, which is not needed, and use only the props that is passed from the parent, I believe that the behaviour of the child will make sense.
If you want to use state in the child, the constructor setting is not enough, you need the set the child state when props changes.
Console.log is asynchronous, so you cannot rely on it here.
Your Child's state does not change with prop change since component does not know anything about state change in your constructor. This is a common pitfall when you depend on your props to construct your local state. You can use componentWillReceiveProps as shown in #Nishant Dixit's answer. But, starting with React 16.3 we have getDerivedStateFromProps function (lifecylce method) for this.
static getDerivedStateFromProps( props, state) {
if( props.isOpen === state.isHidden) {
return null;
}
return {
isHidden: props.isOpen,
}
}
Here, we are comparing our prop and state, if there is a change we are returning desired state. No need to use this.setState.
Related API change blog post including async rendering:
https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html
Although the other answers will make the code work, there is actually a more elegant solution :)
Your child component does not need any state as the state is managed by the Parent (which manages the isHidden property and passes it to the child). So the child component should only care about props.
Try writing the component like this and I believe it should work:
class Child extends React.Component {
render() {
return <p hidden={this.props.isHidden}>Hidden:{this.props.isHidden}</p>
}
}
Dan Abramov who works on the React team tweeted about this problem - essentially saying that you should think hard about whether you can just use props before using state in a component
https://twitter.com/dan_abramov/status/979520339968516097?lang=en

Resources