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.
Related
If I use setState in the child and place a callback in the parent to update the parent state that propagates to child props, then I end up with two render calls.
One for the update to the child state, and one for the prop changing. I can manually use shouldComponentUpdate to ignore the prop change if I want, but the render won't be ready until the state updates.
I know all this can be done easily in react 16-18, but migrating is not simple at the moment.
I am wondering if the solution is to make the child state the source of truth. I can solve all my problems this way, but I thought in react you typically made the parent the source of truth.
Parent Component
Child Component
ChildComponent
function = () => {
this.setState ( {updatedStateProperty}, callback())
}
ParentComponent
callback = () => {
this.setState ( {propSentToChild})
}
What happens is the child component changes state, then render occurs, then the callback occurs, prompting another render.
I want to either
A. change child state, then have the callback called before render
or
B. update child state, then ignore the parents passed props
I can do B, but I'm unsure whether it is proper form to basically make the child's version of the shared state the source of truth
I think you're kind of close. What you really want to do is pass the state to the parent, handle setting the state there, and let the new state trickle down to your child component via props. This is a fairly common pattern for react.
class Parent extends React.Component {
constructor() {
this.state = { foo: "bar", bing: "baz" }
}
stateUpdater(newState) {
this.setState({ ...this.state, ...newState });
}
render() {
return <Child
prop1={this.state.foo}
prop2={this.state.baz}
stateUpdater={this.stateUpdater}
/>
}
}
class Child extends React.Component {
handleClick = () => {
this.props.stateUpdater({ foo: 'bazaar' });
}
render() {
return <div>
The foo is {this.props.foo} and the baz is {this.props.baz}.
<button onClick={this.handleClick}>Click Me!</button>
</div>
}
}
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.
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.
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.
I have MyComponent using setData() to write data in state and getData() to read the state. Is it best practice in React ? It works fine for me but not sure if what I am doing is the simplest possible way, please advice
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
setData(){
this.setState({data:"123"});
}
getData() {
console.log(this.state.data); // 123 OK !!!
}
componentDidMount() {
this.setData();
}
componentDidUpdate() {
this.getData();
}
render() {
return (
<div>
{this.state.data} // 123 OK !!!
</div>
);
}
}
There is absolutely no reason to do this.
Just use this.state.data wherever you need to use it.
If you want to use the state data in a child component, then pass it as a prop and you may also pass a function to the child component that changes the state of the parent component.
(source)
If you want to change the data as some component then you must use setState .
If you want to get the data then you must use this.state.data where data is state variable .
if you want to pass data from parent to child component by using pros.