Parent state change not updating child props - javascript

I'm trying to update a child based on the props provided by it's parent. The way it works right now is that the parent's state contains a variable called 'paused' which is provided to the child like this:
class Parent extends Component {
constructor(props) {
super(props)
this.state = {
history: this.props.history,
paused: false,
}
}
render() {
let paused = this.state.paused
return (
<ChildContainer
graph={
<Child
paused={paused}
/>
}
/>
)
}
Child then uses it like this:
render() {
return (
<div>
{'paused:' + this.props.paused}
</div>
)
}
Paused is a boolean, the usage above is just for testing, since I couldn't get it to update where I want, the behaviour is the same like this.
Paused is being updated in the parent, but not the child.
I've read a lot of questions like this, but I'm at a loss.
Any help would be greatly appreciated.

I can't seem to find a problem with this based on the code you've provided. Here is working proof.
If ChildContainer has any logic that could interfere then I could be wrong, but as is, this works:
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {
history: this.props.history,
paused: false,
}
}
render() {
let paused = this.state.paused // I agree with #Kuo-hsuan Hsu this is unnecessary
return (
<ChildContainer
toggle={() => this.setState({ paused: !this.state.paused })}
graph={<Child paused={paused}/>}
/>
)
}
}
class ChildContainer extends React.Component {
render() {
return (
<div>
{this.props.graph}
<button onClick={this.props.toggle}>Toggle</button>
</div>
);
}
}
class Child extends React.Component {
render() {
return (
<div>{'paused:' + this.props.paused}</div>
)
}
}
ReactDOM.render(<Parent />, document.getElementById('root'));
<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="root" />

Related

Lifting State Up And Functional Components

I'm having trouble with lifting state up and converting components to functions. What's wrong with my code.
Instructions: 1: Inside the JS section, create a class component named App. Inside its render() method, have it return the Welcome component. In the ReactDOM.render() method, change Welcome to App.
2: Lift the state from the Welcome component to App, so that the state is initialized inside of App's constructor.
3: Convert the Welcome component to a function component that returns the same welcome message as before. You will need to pass the bootcampName property of state from App to the Welcome component. It's up to you whether or not to destructure it.
class App extends Component {
constructor(props) {
super(props);
this.state = {
bootcampName: "Nucamp"
};
}
render() {
return (
<div className="App">
<Welcome {this.state.bootcampName}>;
</div>
);
};
}
function Welcome(props) {
return (
<h1>Welcome to {this.props.bootcampName}!</h1>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
You have some errors there
You need to close the Welcome Component.
You need to name the prop
Destruct the props in because of this.state do not exist there.
Here the Code:
class App extends Component {
constructor(props) {
super(props);
this.state = {
bootcampName: "Nucamp"
};
}
render() {
return (
<div className="App">
{ /**
* you need to close the Welcome Component
* you need to name the prop
*/}
<Welcome bootcampName={this.state.bootcampName}/>;
</div>
);
};
}
// Here destruct props to use it
function Welcome({bootcampName}) {
return (
<h1>Welcome to {bootcampName}!</h1>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
Name the prop:
<div className="App">
<Welcome bootcampName={this.state.bootcampName} />
</div>
When you use functional components, you no longer need to use this. Try doing this instead:
class App extends Component {
constructor(props) {
super(props);
this.state = {
bootcampName: "Nucamp"
};
}
render() {
return (
<div className="App">
<Welcome bootcampName={this.state.bootcampName}>;
</div>
);
}
}
function Welcome({bootcampName}) {
return (
<h1>Welcome to {bootcampName}!</h1>
);
}

Running method of component child from this.props.children array

import React from "react";
import ReactDOM from "react-dom";
class NestedComponent extends React.Component {
constructor(props) {
super(props);
this.childMethod = this.childMethod.bind(this);
}
childMethod() {
alert("Child method one ran");
}
render() {
return <div>NestedComponent</div>;
}
}
class NestedComponentTwo extends React.Component {
constructor(props) {
super(props);
this.childMethod = this.childMethod.bind(this);
}
childMethod() {
alert("Child method two ran");
}
render() {
return <div>NestedComponentTwo</div>;
}
}
class WrappingComponent extends React.Component {
constructor(props) {
super(props);
this.runMethod = this.runMethod.bind(this);
}
runMethod() {
let child = this.props.children[0];
/** Always returns as undefined */
//if (typeof child.childMethod == "function") {
// child.childMethod();
//}
/**
* EDIT: Close, however the this binding seems to not be working. I can however provide the childs props to the childMethod and work with that.
*/
if(typeof child.type.prototype.childMethod == "funciton"){
child.type.prototype.childMethod();
}
}
render() {
return (
<div>
{this.props.children}
<button onClick={this.runMethod}>run</button>
</div>
);
}
}
const App = ({}) => {
return (
<div>
<WrappingComponent>
<NestedComponent />
<NestedComponentTwo />
</WrappingComponent>
</div>
);
};
if (document.getElementById("example")) {
ReactDOM.render(<App />, document.getElementById("example"));
}
So the goal is to have optional methods attached to a nested component that can execute from the wrapping component, almost like an event emmiter. For some reason though, the method that exists on the child component claims not to exist. However whenever I log the child component pulled from the array of the this.props.children the prototype has the method listed.
Am I missing a special way to access methods of children components through a methods variable perhaps?
Found the variable I can use to access it. If anyone has any more insight into this, or reasons why what I am doing is poor practice please let me know.
Editing the question where this is needed, but the item below is accessing the function of the child:
child.type.prototype.childMethod
Does not appear to maintain the this binding. Passing props down does work however.
You should manage all of this logic in the top level component (the App component)
class NestedComponent extends React.Component {
constructor(props) {
super(props);
this.childMethod = this.childMethod.bind(this);
}
childMethod() {
alert("Child method one ran");
}
render() {
return <div>NestedComponent</div>;
}
}
class NestedComponentTwo extends React.Component {
constructor(props) {
super(props);
this.childMethod = this.childMethod.bind(this);
}
childMethod() {
alert("Child method two ran");
}
render() {
return <div>NestedComponentTwo</div>;
}
}
class WrappingComponent extends React.Component {
render() {
return (
<div>
{this.props.children}
<button onClick={this.props.onClick}>run</button>
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.runMethod = this.runMethod.bind(this);
}
runMethod() {
if (this.nestedComponent) {
this.nestedComponent.childMethod();
}
}
render() {
return (
<div>
<WrappingComponent onClick={this.runMethod}>
<NestedComponent ref={el => this.nestedComponent = el} />
<NestedComponentTwo />
</WrappingComponent>
</div>
);
}
};
if (document.getElementById("example")) {
ReactDOM.render(<App />, document.getElementById("example"));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="example"></div>
Moreover ref with string attribute is deprecated https://reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs

Getting updated child state into parent state

I am trying to get a parent component to retrieve some information from the child component. Specifically, to have the parent component retrieve the current state of the child. When I try the methodology below, and try to render the updated parent, the updating slows down. Here in the snippet it just returns to me a simple script error. Is there a better way than my current approach to retrieve the child state on componentWillUpdate? Thanks!
class Parent extends React.Component {
constructor(props) {
super();
this.state = {
parentState: "default",
}
this.getChildState = this.getChildState.bind(this);
}
getChildState(childState) {
this.setState({
parentState: childState
})
}
render() {
return (
<div>
<h2>current parentState: {this.state.parentState}</h2>
<Child getChildState={this.getChildState} />
</div>
);
}
}
class Child extends React.Component {
constructor() {
super();
this.state = {
onClick: 0,
}
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({
onClick: this.state.onClick + 1
})
}
componentWillUpdate(nextProps, nextState) {
nextProps.getChildState(nextState.onClick);
}
render() {
return (
<div>
<h2>current childState: {this.state.onClick}</h2>
<button onClick={this.handleClick}>Click Me</button>
</div>
);
}
}
ReactDOM.render(<Parent />, app);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="app"></div>
To update the state of the parent, when the state of the child updates, you should use the setState method with the following signature:
setState(updater, [callback])
The handleClick function for the child component should be as follows:
handleClick() {
this.setState({
onClick: this.state.onClick + 1
},()=>this.props.getChildState(this.state.onClick));
}
This would call the getChildState function when the child state gets updated.
For more information on the setState you can check the React docs

React.js render subcomponents from component

I want to create a reusable component where the DOM structure can be different each time the component is rendered. Let's say I have this
class Comp extends React.Component {
constructor() {
super()
this.state = {
click: null,
}
}
render() {
return(
<div>
{this.props.chidren}
</div>
)
}
handleButton1() {
this.setState({click: 'button1'});
}
handleButton2() {
this.setState({click: 'button2'});
}
}
class SubComp1 extends React.Component {
render() {
return(
<button onClick={() => this.props.handleButton1()}>Button 1</button>
)
}
}
class SubComp2 extends React.Component {
render() {
return (
<button onClick={() => this.props.handleButton2()}>Button 2</button>
)
}
}
ReactDOM.render((
<Comp>
<div id="somediv">
<div id="andanother">
<SubComp1 />
</div>
</div>
<div id="andanotherother">
<SubComp2 />
</div>
</Comp>), document.getElementById('app'))
Currently, the two subcomponents do not have access to their respective handler functions. What's the best way of passing the functions handleButton1 and handleButton2 to the subcomponents assuming that their position in the DOM is dynamic and might change depending on the layout of the page.
I have thought of 2 solutions so far:
Iterating inside the props.children until I find the element of interest then clone it with the property
Using ref and somehow render the subcomponents after the main component has been rendered through the componentDidMount callback.
What are your thoughts on this?
This is a place where using React's Context would be the most straightforward solution.
Another solution would be to use Redux actions, but that would make your component less reusable and more tightly coupled with your application, which you may or may not care about.
Why not do something like this:
class Comp extends React.Component {
constructor() {
super()
this.state = {
click: null,
}
}
render() {
return(
<div>
{this.props.chidren}
</div>
)
}
handleButton(button) {
this.setState({click: button});
}
}
Then in the subcomponents you can do something like
class SubComp1 extends React.Component {
render() {
return(
<button onClick={() => this.props.handleButton('button1')}>Button 1</button>
)
}
}
class SubComp2 extends React.Component {
render() {
return (
<button onClick={() => this.props.handleButton('button2')}>Button 2</button>
)
}
}
One Alternative option which might fit your needs is to build a higher order component, which decorates another component with some additional functionality, below is a quick example of how this may work for you,
The higher order component:
const Comp = ComposedComponent =>
class Comp extends React.Component {
constructor(props) {
super(props)
this.handleButton = this.handleButton.bind(this);
this.state = {
click: null,
}
}
handleButton(button) {
this.setState({click: button});
}
render() {
return(
<ComposedComponent
onClick={this.handleButton}
/>
)
}
}
export default Comp;
The child component:
class SubComp1 extends React.Component {
render() {
return(
<button onClick={() => this.props.onClick('button1')}>Button 1</button>
)
}
}
How to use it:
const ExtendedComp = Comp(SubComp1);
<ExtendedComp />
would this be suitable for your task?

Why doesn't this child component rerender?

I'm experimenting with ReactJS and I'm trying to understand how child component rendering is triggered. In ReactJS, if I set up an example like this:
var externalCounterVar = 10
class Counter extends React.Component {
constructor(props){
super(props);
this.state = props;
}
render() {
console.log('rendering counter')
return (
<div> {externalCounterVar} </div>
)
}
}
class Main extends React.Component {
constructor(props){
super(props);
}
handleClick() {
externalCounterVar += 1;
}
rerender(){
this.render();
}
render() {
console.log('rendering');
return (
<div>
<button onClick={this.rerender.bind(this)} />
<Counter counter={externalCounterVar} />
</div>
)
}
}
ReactDOM.render(<Main />, document.getElementById('root'));
I'm not sure I understand why when you "rerender" it calls the render method of Main but not Counter? It seems like it should call both render methods since it's rendering Main and Counter is a child of Main.
So when rerender is called, 'rendering' will print but 'rendering counter' will not.
It looks like you're overlooking one of the main benefits of using React, namely how state works.
You never, ever need to call this.render within a React component
You should never set state dynamically, ie: this.state = ...
You should always use this.setState to set your state.
Rewritten, your code should look something like the following:
const externalCounterVar = 10
class Counter extends React.Component {
render() {
console.log('rendering counter')
return (
<div> {this.props.counter} </div>
)
}
}
class Main extends React.Component {
state = {
counter: externalCounterVar
}
handleClick() {
this.setState({counter: this.state.counter + 1});
}
render() {
console.log('rendering');
return (
<div>
<button onClick={this.handleClick.bind(this)} />
<Counter counter={this.state.counter} />
</div>
)
}
}
By calling this.setState, React automatically knows it needs to rerender your component, and as a result, all child components will also be rerendered.
Hope this helps!
In this case you don't have to use rerender method, also with purpose re-render all child components you need update state with method setState. And also accordingly to this you have to "move state up".
Here my example:
class Counter extends React.Component {
render() {
console.log('rendering counter');
return (<div> {this.props.counter} </div>);
}
}
class Main extends React.Component {
constructor(props){
super(props);
this.state = {counter: props.counter};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({counter: ++prevState.counter}));
}
render() {
console.log('rendering');
return (
<div>
<button onClick={this.handleClick} />
<Counter counter={this.state.counter} />
</div>
);
}
}
var externalCounterVar = 10;
ReactDOM.render(
<Main counter={externalCounterVar} />,
document.getElementById('root')
);
In some situations you can use this.forceUpdate() to call re-render.
But, if you can not do this, do not do.
https://facebook.github.io/react/docs/react-component.html#forceupdate

Categories