I'm setting the state of my parent component in componentDidMount and passing its value to a child component via props, but even though the input is filled, when I run console.log(this.props.value) in the componentDidMount event of the child component, it is undefined. I need this value updated in this event.
How to get the correct prop value in this scenario?
Example code:
class Text extends React.Component {
componentDidMount(){
console.log(this.props.value);
}
render() {
return (
<div>
<input type="text" value={this.props.value} />
</div>
);
}
}
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
data: {}
};
}
componentDidMount(){
let data = {
RequestId: "0000-000"
}
this.setState({ data });
}
render() {
return (
<Text value={this.state.data["RequestId"]} />
);
}
}
// Render it
ReactDOM.render(
<Form />,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.5.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.5.2/umd/react-dom.production.min.js"></script>
<div id="root"></div>
What happen in your case is the child component mount before the logic change from the parent. Here a way to make it work. Also what you can do it's use the componentDidUpdate lifecycle method to trigger the change.
Remember componentDidMount get call only ONE time. So at the moment the parent get it the child is already mount. But as you can see the value of the input is filled that's because react component rerender on props change. BUT not REMOUNT.
With the if part here, the component render only when data RequestId is filled, so we can then trigger the componentDidMount with the value you want.
class Text extends React.Component {
componentDidMount(){
console.log(this.props.value);
}
render() {
return (
<div>
<input type="text" value={this.props.value} />
</div>
);
}
}
class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
data: {}
};
}
componentDidMount(){
let data = {
RequestId: "0000-000"
}
this.setState({ data });
}
render() {
if (!this.state.data["RequestId"]) { return null }
return (
<Text value={this.state.data["RequestId"]} />
);
}
}
// Render it
ReactDOM.render(
<Form />,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.5.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.5.2/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Related
I am trying to pass some constant values which is stored in state of parent component to child component and would like to pass down in tree.
is it right to store the constant values in state of App.tsx or any other suggestion
how can i access for example GrTag or sTag from App.tsx to Subchild component.
App.tsx
class App extends Component {
constructor(props) {
super(props);
this.state = {
sTag : "00D",
pTag : "010",
adTag : "020",
dbTag : "030",
GrTag : "040",
serTag : "00E",
modTag : "060",
iTag: "018"
};
}
render() {
return(
<div >
<Child {...this.state}/> //is this right way to pass
</div>
);
}
}
Child.tsx
class Child extends Component {
constructor(props) {
super(props);
}
render(){
return(
<div>
{
<Subchild /> //want to pass the state values from App.tsx
}
</div>
);
};
}
Subchild.tsx
class Subchild extends Component {
constructor(props) {
super(props);
}
render(){
return(
<div>
{
// want to print sTag value here
}
</div>
);
};
}
The way you are spreading the state {...this.state} will pass all the state values as props to the child components . If you need all the state values in your Child component as props then what you are doing is fine .
But if you just need the stag from the App inside the subChild then you can do
<Child {...this.state}/>
In your Child.tsx
<Subchild {...this.props} />
To solve your problem, pass the props from the child to the sub child. Like this;
class Child extends Component {
constructor(props) {
super(props);
}
render(){
return(
<div>
{
<Subchild {...this.props}/> //want to pass the state values from App.tsx
}
</div>
);
};
}
But mind you. This is not a good way to pass data to components.
If you have data that can be shared amongst many components in your app, consider using REACT REDUX or MOBX. I personally recommmend using Redux.
App.tsx
class App extends Component {
constructor(props) {
super(props);
this.state = {
sTag : "00D",
pTag : "010",
adTag : "020",
dbTag : "030",
GrTag : "040",
serTag : "00E",
modTag : "060",
iTag: "018"
};
}
render() {
return(
const data = this.state;
<div >
<Child dataToClid = {data}/> //is this right way to pass
</div>
);
}
}
Child.tsx
class Child extends Component {
constructor(props) {
super(props);
this.state = {
data: this.props.dataToClid
}
}
render(){
const data = this.state;
return(
<div>
{
<Subchild dataToSubClid = {data}/> //want to pass the state values from App.tsx
}
</div>
);
};
}
SubChild.tsx
class Subchild extends Component {
constructor(props) {
super(props);
this.state = {
data: this.props.dataToSubClid
}
}
render(){
const {GrTag, sTag} = this.state;
return(
<div>
<p>{GrTag}</p>
<p>{sTag}</p>
</div>
);
};
}
you can do this way or you can use Context API for pass data from parent to child
Simple example but not sure why I can't get it to work:
class Grid extends React.Component {
componentDidUpdate = () =>{
alert('Should Fire')
}
render() {
return (<div>{this.props.name}</div>)
}
}
class Application extends React.Component {
render() {
return (<div><Grid name="test-grid" /></div>);
}
}
The componentDidUpdate method does not fire in the Grid class. Insight?
Instead you can use componentDidMount(). You are not updating the props which should trigger update event. See from the documentation of componentDidUpdate():
componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.
As the following:
class Grid extends React.Component {
componentDidMount = () =>{
alert('Should Fire')
}
render() {
return (<div>{this.props.name}</div>)
}
}
I hope this helps!
componentDidUpdate function only fires when the parent component re-renders causing the child to re-render.
In your case the parent isn't re-rendering at all. Its just the initial render.
To detect an initial render, you would make use of componentDidMount function. You can see the componentDidUpdate function being fired if you have a state in parent that you update based on an interaction or any other way
class Grid extends React.Component {
componentDidMount(){
alert('Should Fire')
}
componentDidUpdate() {
alert('ComponentDidUpdate Should Fire');
}
render() {
return (<div>{this.props.name}</div>)
}
}
class Application extends React.Component {
state = {
count: 0
}
render() {
return (
<div>
<Grid name="test-grid" />
<button onClick={()=>this.setState(prev => ({count: prev.count + 1}))}> increment</button>
</div>
);
}
}
ReactDOM.render(<Application />, document.getElementById('app'));
<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="app"/>
I know that this is probably the most asked question about React, but none of the answers helped me.
I have 2 classes:
Child
class Preview extends Component {
constructor(...args) {
super(...args);
this.state = {
isCommentOpen: false
};
this.handleComment = ::this.handleComment;
render() {
return(
button type="button" onClick={this.handleComment}>Comment</button>
)}
handleComment(){
this.setState({isCommentOpen: !this.state.isCommentOpen});
}
export default Preview;
Parent
class Profile extends Component {
render(){
return(
<div>
<_.Preview />
//the place where I want to add validation from the component above
{this.state.isCommentOpen ? <span>Cool</span> : null}
</div>
}
You should not mutate or directly assign this.props as shown in the other answer:
this.props.isCommentOpen = !this.props.isCommentOpen // <-- DON'T DO THIS! 🎃
Instead, you should have a callback function to let the Parent component update the child component:
class Profile extends Component {
constructor(props) {
super(props);
this.state = {
isCommentOpen: false;
}
this.handleComment = this.handleComment.bind(this); // <-- important!
}
handleComment() {
this.setState({ isCommentOpen: !this.state.isCommentOpen });
}
render() {
return (
<div>
<Preview handleComment={this.handleComment} />
{ this.state.isCommentOpen ? <span>Cool</span> : null }
</div>
)
}
}
export default Profile
The child component then only needs to call this.props.handleComment:
// Child Component:
class Preview extends Component {
render() {
return(
<button type="button" onClick={this.props.handleComment}>Comment</button>
}
}
export default Preview;
I just creating a project and use a several component for a page and pass data by using props to each components. The problem is, when I have already change data from parent component to child component by using props and I have update the data from parent component, the child component still using the old data.
The example is just like this:
class Child extends Component{
constructor(props){
super(props);
this.state = {
variabel : props.variable
}
}
render() {
return (
<div>
<h1>{this.state.variable}</h1>
</div>
)
}
}
class Parent extends Component{
constructor(props){
super(props);
this.state = {
variabel : 'Hello'
}
}
render() {
return (
<div>
<Child variable={this.state.variable} />
</div>
)
}
}
So, when I run the page and update the variabel state in Parent Component, Child Component still show the old value. How to make it updated as the Parent Component data? Or I must using Redux for this case?
In general you'll only want to keep one particular piece of state in one place. If you reassign it in the constructor of Child, it will not update when the parent's state updates. So something like this pattern should work:
class Child extends Component{
// Note that no constructor is needed as you are not initializing any state or binding any methods.
render() {
return (
<div>
<h1>{this.props.variable}</h1>
</div>
)
}
}
class Parent extends Component{
constructor(props){
super(props);
this.state = {
variable : 'Hello'
}
}
render() {
return (
<div>
<Child variable={this.state.variable} />
</div>
)
}
}
A warning note about not initializing state with props is in the React docs for constructor, as a matter of fact.
Mitch Lillie's answer is the correct one. You should have only one source of truth.
In general, it's a good idea to keep the state in the nearest common ancestor of the components that depend on the state. Then you pass the props down.
If, however, you need to keep a copy of the prop in the child state, you should use the life cycles that React provides.
Codepen Live Demo
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {
variable: props.variable,
};
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (this.props.variable !== prevState.variable) {
this.setState({
variable: this.props.variable,
});
}
}
render() {
const varState = this.state.variable;
const varProps = this.props.variable;
return (
<div>
Child props: {varProps}
<br />
Child state: {varState}
</div>
);
}
}
class Parent extends React.Component {
constructor(props) {
super(props);
setInterval(this.updateTime, 1000); // refresh every second
this.state = {
variable: new Date().toLocaleString(),
};
}
updateTime = () => {
this.setState({
variable: new Date().toLocaleString(),
});
}
render() {
const time = this.state.variable;
return (
<div>
<div>
Parent: {time}
</div>
<Child variable={time} />
</div>
);
}
}
ReactDOM.render(
<Parent />,
document.getElementById('container')
);
I have asynchronous method get called and I need the value from it to be render on the first cycle so it will be passed to the next component that being render and I cant make id done. The component is rendered before the value is returned which cause the the prop be undefined when it passed.
Any idea how can I delay the rendering till the value get returned?
CODE SAMPLE:
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
valueHere:''
}
}
componentWillMount() {
axios.post(url, {
username:this.state.username
})
.then((res) => {
this.setState({
valueHere:res.data
})
})
}
render() {
return(
<AnotherComponent
someValue={this.state.valueHere}
/>
)
}
}
export default class AnotherComponent extends Component {
constructor(props) {
super(props);
}
render() {
console.log(this.props.someValue) // undefined
return(
<View/>
)
}
}
You can use conditional rendering.
So basically, you check if that value exists then render the component else return null/any other component;
render() {
return( this.props.someValue? <ActualComponeent /> : <div> some loader...</div>)
}