React child component not re-rendering on state change - javascript

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.

Related

Troubles with state

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.

React Update Child's state from Parents Component

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>
...
)}

Child component not updating on state change

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.

Updating child Components with state change in Reactjs

So I know this question has been asked a couple of times and the general concession is that props cant be changed when it has already passed down to a child. The situation I have here is that basically i have a different onClick function in a different file that updates the the id="movie-header" with an innerHTML, the DOMSubtreeModified and componentDidUpdatedetects the change and pass down the new props to Child "Ebay".
So the question here is how do I get the Ebay component to update its state and make use of the new value with every change to the state in the moviemodalwindow(the parent of the Ebay)
MovieModalWindow.js
import React from "react";
import "../MovieGo.css";
import Ebay from "../Store/Ebay";
class MovieModalWindow extends React.Component {
constructor() {
super();
this.state = {
name: 1
};
}
componentDidMount() {
var element = document.getElementById("movie-header");
element.addEventListener("DOMSubtreeModified", this.myFunction(element));
var name = this.state.name + 1;
this.setState({ name: [...this.state.name, name] });
}
myFunction = input => event => {
this.setState({ name: input.innerHTML });
};
componentDidUpdate(prevProps, prevState) {
if (prevState.name != this.state.name) {
window.localStorage.setItem("keyword", this.state.name);
}
}
render() {
return (
<div id="myModal" class="modal">
<div class="modal-content">
<span onClick={onClose} class="close">
×
</span>
<h1 id="movie-header" />
<div className="middle-window">
<div className="left">
<Ebay id="ebay" keyword={this.state.name} />
</div>
</div>
<h3>PLOT</h3>
<p id="moviedetails" />
</div>
</div>
);
}
}
export default MovieModalWindow;
Ebay.js File
import React from "react"
class Ebay extends React.Component{
constructor(){
super();
this.state={
data:[],
}
}
componentWillUpdate(prevProps, prevState){
if (prevProps.keywords!=this.props.keywords){
console.log(window.localStorage.getItem("keyword"))
}
render(){
const{newInput} =this.props
return(
<div>
</div>
)
}
}
export default Ebay
I'm unsure if I'm answering the question you're asking, so apologies if this isn't what you're asking.
Step 1. Make Ebay's prop's change when you need this update to happen. (I think you stated you already have this occurring?)
Step 2: Make Ebay's state update when the props change. Here you can just watch for prop changes with componentWillReceiveProps and update the state accordingly.
class Ebay extends React.Component {
constructor() {
super();
this.state = { data: [] };
}
componentWillRecieveProps(nextProps) {
if (nextProps.keyword !== this.props.keyword) {
this.setState({ data: ['something new'] });
}
}
render() { ... }
}

Tie an action to a state change in pure React

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. ;)

Categories

Resources