I am trying to pass a value to child component yet the value doesn't not update as the parent component value changes. Here is my work
Child component:
class Test extends Component {
constructor(props) {
super(props)
this.state = {
data: this.props.data,
}
}
render() {
return(
<div>{this.state.data}</div>
)
}
}
export default Test;
Parent js file
constructor(props) {
super(props)
this.state = {
val: 0,
}
this.addVal = this.addVal.bind(this)
}
addVal() {
let val = this.state.val
val = val + 1
console.log(val)
this.setState({
val
})
}
render() {
return(
<div>
<Button onClick={this.addVal}> add </Button>
<Test data={this.state.val} />
</div>
)
}
The value gets updated on the parent component however, the child component does not get the updated value. What am I missing?
The constructor() only runs once in the start. When the parent is updated it sends new props to the Test but it doesnot change state.data to props.data after the component is rendered first time
You are printing this.state variable which is not updated. Instead you should print value from this.props
render(){
return(
<div>{this.props.data}</div>
)
}
If you want to detect the new props you can use compnentWillRecieveProps()
componentWillRecieveProps(nextProps){
this.setState({data:nextProps.data})
}
state is unnecessary in the child component. It should be enough to render this.props.data since that is what you are passing to the child from the parent.
Related
I'm just started to learn react, and i have a question
Well, i can impact on state from one component to another. But can i do it in reverse?
Here's what i mean:
import React from 'react';
import Butt from './Button';
class Checkbox extends React.Component {
constructor(props) {
super();
}
render() {
return (
<div>
<Butt arg={13} />
</div>
);
}
}
export default Checkbox;
import React from 'react';
class Butt extends React.Component {
constructor(props) {
super();
this.state = {
s1: props.arg,
};
}
add = () => {
let val = this.state.s1;
val++;
this.setState({ s1: val });
};
render() {
return (
<div>
<label>
<label>
<button onClick={this.add}>add</button>
<div>{this.state.s1}</div>
</label>
</label>
</div>
);
}
}
export default Butt;
Sorry for my silly question. Thanks in advance :)
I am not sure about your question, but in react, there is a one-way flow (from parent to child) for transferring information (props, states, or ...). If you want to have access to states everywhere or set them in each direction you should use Redux or context or any other state management.
You're updating the Butt state from inside Butt so this will work fine. It won't change the value of this.props.arg though, if that's what you're asking.
Props are always non-mutable.
What you can do is have two components share the state of their parent...
class Parent extends React.Component {
state = {
val = 0
}
render () {
return (
<>
<Child1
val={this.state.val}
onChange={newVal => this.setState({ val: newVal })}
/>
<Child2
val={this.state.val}
onChange={newVal => this.setState({ val: newVal })}
/>
</>
)
}
}
Then inside the child components pass the updated value to onChange...
class Child1 extends React.Component {
handleChange() {
this.props.onChange(this.props.val + 1)
}
render() {
return (
<Button onClick={() => this.handleChange()}>
Update value
</Button>
)
}
}
This way you're just passing a new value from Child to Parent and letting Parent decide what to do with it.
Whether Child1 or Child2 sends the new value, both children will get updated when Parent calls this.setState({ val: newVal }) and changes this.state.val.
When chartValue: true an object appears on the chart.
class Chart extends Component {
constructor(props) {
super(props);
this.state = {
chartValue: false,
};
}
componentWillReceiveProps(nextProps) {
this.setState({
chartValue: nextProps.chartValue
});
}
render() {
return (
<div>{this.state.chartValue}</div>
);
}
}
I want to change the value to true via a button in my app component.
class App extends Component {
constructor(props) {
super(props);
this.state = ({
chartValue: false,
});
}
callChartValue() {
this.setState({
chartValue: true
})
}
render() {
return (
<div>
<button onClick={this.callChartValue}>call</button>
<Chart chartValue={this.state.chartValue} />
</div>
)}
}
}
It seems like changing the state in my App.js doesnt translate to a change in the state of the chart component. I've tried without using componentWillReceiveProps but it hasn't worked either.
You have two main issues in your code:
1. You should bind your methods
Otherwise, you won't have access to this inside them.
constructor(props) {
...
// Add this to your constructor
this.callChartValue = this.callChartValue.bind(this);
...
}
2. You shouldn't use strings for referencing the methods
render() {
return (
...
// Wrong
<button onClick="callChartValue">call</button>
// Right
<button onClick={this.callChartValue}>call</button>
...
)}
I realize questions like this have been asked before but from reading several Q&As here it seems like in a lot of cases people are recommending using componentWillUpdate but from my (very) basic understanding of React, if I setState() won't child components re-render if they are affected?
This is my App component (showing the State being set, the function to update the state handleClick, the Display component (which shows the current input from state) and a Button component which shows a number and is passed the function handleClick:
this.State = {
calcValue: 0
}
this.handleClick = this.handleClick.bind(this);
}
handleClick(val) {
this.setState({ calcValue: val })
}
render() {
return(
<div class="calcBody">
<Display currentValue={this.State.calcValue} />
<h1>Calculator</h1>
<div class="numPad">
<Button btn="num col1" operator={1} handleClick={this.handleClick.bind(this)} />
This is the Button component:
class Button extends React.Component {
constructor(props) {
super(props);
}
render() {
return(
/*the button when clicked takes the handleClick function and passes it props based on whatever number is pressed */
<button onClick={() => this.props.handleClick(this.props.operator)}>
<div class={this.props.btn}>{this.props.operator}</div>
</button>
)
}
}
Lastly, this is the Display component:
class Display extends React.Component {
constructor(props){
super(props);
this.props = {
currentValue: this.props.currentValue
}
}
render() {
return(
<h1>{this.props.currentValue}</h1>
);
}
}
I'm wondering why this does not update when handleClick(val) is called?
You're defining state as this.State which is incorrect it should be lowercased: this.state:
this.state = {
calcValue: 0
}
Also, this line:
this.props = {
currentValue: this.props.currentValue
}
doesn't have much sense, as props are passed outside, component shouldn't change them.
I would like to update all child components in an array using a prop passed down from the state of a parent component. A basic example is shown below. Each child component in the array is stateless and has a prop value which is determined by the state of the parent component. However, when the parent components state changes, the child components do not re-render with the change. How can I make the child components re-render when the parents state changes? Thanks!
import React from 'react';
import ReactDOM from 'react-dom';
class Child extends React.Component {
render(){
return (
<p>
<button onClick = {(e) => this.props.onClick(e)}>
click me
</button>
{this.props.test}
</p>
)
};
}
class Parent extends React.Component{
constructor(props){
super(props);
this.state = {msg: 'hello', child_array: []};
this.handleClick = this.handleClick.bind(this);
}
handleClick(e){
e.preventDefault();
const msg = this.state.msg == 'hello' ? 'bye' : 'hello';
this.setState({msg: msg});
}
componentDidMount(){
let store = [];
for (var i = 0; i < 5; i++){
store.push(<Child test = {this.state.msg} key = {i} onClick = {this.handleClick}/>);
}
this.setState({child_array: store});
}
render(){
return(
<div>
{this.state.child_array}
</div>
)
}
}
ReactDOM.render(<Parent />, document.getElementById('root'));
As mentioned in comments
It won’t re render again because you are generating chil components in componentDidMount and this method gets called only once per the component after first render. So when your callback fires the child_array will be empty
Instead what you can do is remove componentDidMount method code and do that in render as like below. In the below case it will render every time the onclick fires in child component
render(){
const store = [];
for (var i = 0; i < 5; i++){
store.push(<Child test = {this.state.msg} key = {i} onClick = {this.handleClick}/>);
}
return(
<div>
{store}
</div>
)
The problem is that you're rendering the child components (and thus baking in the value of this.state.msg) in the parent's componentDidMount() method. You need to render them in the parent's render() method instead.
componentWillReceiveProps will work in your case, everytime you will get props child component will re-render.
class Child extends React.Component {
constructor(props) {
super(props);
let initialData = (
<p>
<button onClick = {(e) => self.props.onClick(e)}>
click me
</button>
{nextProps.test}
</p>
);
this.state = {data: initialData };
}
componentWillReceiveProps(nextProps) {
let self = this;
let updatedHtml = (
<p>
<button onClick = {(e) => self.props.onClick(e)}>
click me
</button>
{nextProps.test}
</p>
);
this.setState({data: updatedHtml})
}
render(){
return (
{data}
)
};
}
Learning React so might be a bit nooby question. Consider this code:
class Application extends React.Component {
render() {
return <Container />
}
}
class Container extends React.Component {
constructor(props) {
super(props)
this.state = {
isOn: false
}
this.handleToggle = this.handleToggle.bind(this);
}
handleToggle(on) {
this.setState({
isOn: on
});
}
render() {
return (
<div>
<p>
{this.state.isOn ? 'on' : 'off'}
</p>
<MyButton handleToggle={this.handleToggle} />
</div>
);
}
}
class MyButton extends React.Component {
constructor(props) {
super(props);
this.state = {
pressed: false
}
this.handleButtonToggle = this.handleButtonToggle.bind(this);
}
handleButtonToggle() {
const on = !this.state.pressed
this.setState({
pressed: on
});
this.props.handleToggle(on);
}
render() {
return (
<div>
<button onClick={this.handleButtonToggle}>
{this.state.pressed ? "pressed" : "depressed"}
</button>
</div>
);
}
}
ReactDOM.render(<Application />, document.getElementById('app'));
As you can see currently when the button inside the Container is clicked handleButtonToggle() is fired which changes the state of the button itself and then calls a function to change the state of the parent Container. Is this the React way to do it? At the moment Container state is changed when the function handleButtonToggle is fired. Ideally I would want Container state to be dependent on MyButton state directly (cuz maybe in future there will be ways to set button state other than through handleButtonToggle, and I don't want to manually call this.props.handleToggle every time the state changes.). In other words is there a way to do something like this.props.handleToggle(this.state.pressed) in the button component when its state changes.
Codepen
After reviewing the code, a better way to write the Button component is to make it a controlled component. If the container component requires to maintain the pressed state, a controlled button component would receive the pressed state from the container component as props.
<MyButton pressed={this.state.pressed} onToggle={this.handleToggle} />
And the render method of the Button component should be:
render() {
return (
<div>
<button onClick={this.props.onToggle}>
{this.props.pressed ? "pressed" : "depressed"}
</button>
</div>
);
}
The actual button toggle will be done in the handleToggle method of the container component:
handleButtonToggle() {
let { pressed } = this.state;
pressed = !pressed;
this.setState({
pressed
});
}
You can either pass a callback as the second argument to setState or implement componentDidUpdate on your component to wait for state changes. The smallest change would be the former:
handleButtonToggle() {
const on = !this.state.pressed
this.setState({
pressed: on
}, () => {
this.props.handleToggle(on);
});
}
or with componentDidUpdate:
componentDidUpdate(prevProps, prevState) {
if (prevState.on !== this.state.on) {
this.props.handleToggle(this.state.on);
}
}
Here is a working codepen: https://codepen.io/damien-monni/pen/XRwewV.
I tried to keep as much as I can of your code so you can focus on really needed changes.
You need to create a controlled component. The state of your button will be stored in the container and pass by props to the child MyButton component.
/*
* A simple React component
*/
class Application extends React.Component {
render() {
return <Container />
}
}
class Container extends React.Component {
constructor(props) {
super(props)
this.state = {
isOn: false
}
this.handleToggle = this.handleToggle.bind(this);
}
handleToggle() {
this.setState({
isOn: !this.state.isOn
});
}
render() {
return (
<div>
<p>
{this.state.isOn ? 'on' : 'off'}
</p>
<MyButton pressed={this.state.isOn} handleToggle={this.handleToggle} />
</div>
);
}
}
class MyButton extends React.Component {
constructor(props) {
super(props);
this.state = {
pressed: this.props.pressed
}
this.handleButtonToggle = this.handleButtonToggle.bind(this);
}
handleButtonToggle() {
this.props.handleToggle();
}
componentWillReceiveProps(nextProps) {
if (nextProps.pressed !== this.props.pressed) {
this.setState({ pressed: nextProps.pressed });
}
}
render() {
return (
<div>
<button onClick={this.handleButtonToggle}>
{this.state.pressed ? "pressed" : "depressed"}
</button>
</div>
);
}
}
/*
* Render the above component into the div#app
*/
ReactDOM.render(<Application />, document.getElementById('app'));
You need to do it because you want to share a state between two of your components. This is documented here.
I let you look at the codepen and ask your questions about it. ;)