My component doesn't rerender - javascript

When I click on button, I see '1' in console, but never see '2'. Why it happens? Can you help me please to resolve this issue? I realy dont know why my second component doesn't update.
class App extends PureComponent {
constructor() {
super();
this.state = {
name: 'Vasya'
}
this._onChange = this._onChange.bind(this);
}
_onChange(name) {
this.setState({
name: name
});
}
render() {
console.log(1);
return {
<div>
<Button onClick={this._onChange('Petr')} />
<AnotherComponent username={this.state.name} />
</div>
}
}
}
class AnotherComponent extends PureComponent {
const {
username
} = this.props
render() {
console.log(2);
return {
<div>
test
</div>
}
}
}
export default App;

A few code problems in your example!
when you return your React elements from render(), they must be wrapped in parens () not curlies {}
use React.Component, not React.PureComponent, or you'll get issues
<Button> isn't a thing, use <button>
The main problem then is an infinite loop - when you render, this line:
<Button onClick={this._onChange('Petr')} />
...this calls the _onChange() function at render time and passes the result to Button as the onClick prop. This isn't what you want - you want the onClick prop to be a function that calls _onChange(). So
<button onClick={ () => this._onChange('Petr')} />
Full working example:
class App extends React.Component {
constructor() {
super();
this.state = {
name: 'Vasya'
}
this._onChange = this._onChange.bind(this);
}
_onChange(name) {
this.setState({
name: name
});
}
render() {
console.log(1);
return (
<div>
<button onClick={ () => this._onChange("Petr") } />
<AnotherComponent username={this.state.name} />
</div>
);
}
}
class AnotherComponent extends React.Component {
render() {
console.log(2);
return (
<div>
test
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("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>
<div id="app"></div>

Related

Added React child component doesn't receive updated value

I'm attempting to add a child to my react app via button and have it's value get updated via button click.
When I click the button the 'hard coded' child gets updated but the child added via button isn't being updated. I'm missing something fundamental.
I tried passing props to the child assuming state updates would propagate but it isn't working.
https://codepen.io/esoteric43/pen/YzLqQOb
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
text: "Starting",
children: []
};
}
handleClick() {
this.setState({ text: "Boom" });
}
handleAdd() {
this.setState({
children: this.state.children.concat(
<Child1 key={1} text={this.state.text}></Child1>
)
});
}
render() {
return (
<div>
<Child1 text={this.state.text}></Child1>
{this.state.children}
<button onClick={() => this.handleClick()}>Change</button>
<button onClick={() => this.handleAdd()}>Add</button>
</div>
);
}
}
class Child1 extends React.Component {
render() {
return <div>{this.props.text}</div>;
}
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<App />
);
To reproduce in the above codepen link, click add then click change. The added element is not updated. Both children should be updated when the change button is clicked.
You have to map the previous state values as well in handleClick method:
handleClick() {
this.setState((prevState) => ({
...prevState,
children:
prevState.children.length > 1
? [
...prevState.children
.slice(0, prevState.children.length - 1)
.map((_) => "Boom"),
"Boom"
]
: ["Boom"]
}));
}
Check it working on this CodeSandbox.
You need to update the last item in children. Your current solution updates the initial text.
Try like this:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
text: "Starting",
children: ["Starting"]
};
}
handleClick() {
this.setState((prevState) => ({
...prevState,
children:
prevState.children.length > 1
? [
...prevState.children.slice(0, prevState.children.length - 1),
"Boom"
]
: ["Boom"]
}));
}
handleAdd() {
this.setState({
children: this.state.children.concat(
<Child1 key={1} text={this.state.text}></Child1>
)
});
}
render() {
return (
<div>
{this.state.children.map((child, index) => (
<div key={index}>{child}</div>
))}
<button onClick={() => this.handleClick()}>Change</button>
<button onClick={() => this.handleAdd()}>Add</button>
</div>
);
}
}
class Child1 extends React.Component {
render() {
return <div>{this.props.text}</div>;
}
}
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>
Do you mean something like this?
class App extends React.Component {
constructor() {
super()
this.state = {
children: []
}
}
handleAddClick = ()=>{
this.setState(prev=>({children:[...prev.children, `child ${this.state.children.length+1}`]}))
}
handleChangeClick=(item)=> {
let draft = [...this.state.children];
draft[draft.findIndex(i=>i===item)]='boom';
this.setState({children: draft});
}
render() {
return(
<div>
<button onClick={this.handleAddClick}>Add child</button>
{
this.state.children.map(child=>(<Child onChange={this.handleChangeClick} title={child}/>))
}
</div>
)
}
}
class Child extends React.Component {
render() {
return <div>
{this.props.title}
<button onClick={()=>this.props.onChange(this.props.title)}>change me</button>
</div>
}
}
ReactDOM.render(
<App />,
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"/>

Updating parent state from child components not working in reactjs

I was going through react official documentation when I struck upon an example which updates the parent component through child component callbacks. I was able to understand how the flow works. However, when I tried to optimize the code further it failed to update the component via callbacks.
The Original Code:
https://codepen.io/gaearon/pen/QKzAgB?editors=0010
My code change:
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {isLoggedIn: false};
this.button = <MyButton message="Login" onClick={this.handleLoginClick} />;
}
handleLoginClick() {
this.setState({isLoggedIn: true});
}
handleLogoutClick() {
this.setState({isLoggedIn: false});
}
render() {
const isLoggedIn = this.state.isLoggedIn;
if (isLoggedIn) {
this.button = <MyButton message="Logout" onClick={this.handleLogoutClick} />;
} else {
this.button = <MyButton message="Login" onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{this.button}
</div>
);
}
}
function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
class MyButton extends React.Component {
constructor(props) {
super(props);
this.message=props.message;
this.click=props.onClick;
}
render() {
return (
<button onClick={this.click}>
{this.message}
</button>
);
}
}
ReactDOM.render(
<LoginControl />,
document.getElementById('root')
);
Ok the main problem here is that you are trying to assign to many things to "this".
React does not track changes and re-renders when component's method or properties changes.
try to avoid this pattern and use state and props directly.
Only changes to state or props will cause a component to re-render.
In you situation you can look at this code:
class LoginControl extends React.Component {
state = {isLoggedIn : false}
handleLoginClick = () => {
this.setState({isLoggedIn: true});
}
handleLogoutClick = () => {
this.setState({isLoggedIn: false});
}
button = () => {
const message = this.state.isLoggedIn ? "Logout" : "Login";
const onClick = this.state.isLoggedIn ? this.handleLogoutClick : this.handleLoginClick;
return <MyButton message={message} onClick={onClick} />
}
render() {
return (
<div>
<Greeting isLoggedIn={this.state.isLoggedIn} />
{this.button()}
</div>
);
}
}
function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
class MyButton extends React.Component {
constructor(props) {
super(props);
this.message=props.message;
this.click=props.onClick;
}
render() {
return (
<button onClick={this.props.onClick}>
{this.props.message}
</button>
);
}
}
ReactDOM.render(
<LoginControl />,
document.getElementById('root')
);

What is the difference between this.state.function and this.function in ReactJS

I am learning the concept of States in React. I am trying to understand the difference between using this.handleChange, and this.state.handleChange.
I would be grateful if someone could explain to me, the exact difference between the two, and why would this.state.handleChange not work?
class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {
inputValue: ''
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({
inputValue: event.target.value
});
}
render() {
return (
<div>
< GetInput input={this.state.inputValue} handleChange={this.handleChange} />
{ /* this.handleChanges, and this.state.handleChanges */ }
< RenderInput input={this.state.inputValue} />
</div>
);
}
};
class GetInput extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>Get Input:</h3>
<input
value={this.props.input}
onChange={this.props.handleChange}/>
</div>
);
}
};
class RenderInput extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>Input Render:</h3>
<p>{this.props.input}</p>
</div>
);
}
};
You can technically call this.state.handleChange so long as you add handleChange in your state.
But it doesn't really make sense since you don't want React to keep a track of it, and it will probably not change (unless you are doing some clever tricks).
constructor(props) {
super(props);
this.state = {
handleChange: e => {
e.preventDefault();
console.log("this.state.handleChange");
}
};
}
One would normally declare a member function in a class.
handleChange = e => {
e.preventDefault();
console.log("this.handleChange");
};
Here is the full working code
(working demo available on CodeSandBox).
import React from "react";
import ReactDOM from "react-dom";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
handleChange: e => {
e.preventDefault();
console.log("this.state.handleChange");
}
};
}
handleChange = e => {
e.preventDefault();
console.log("this.handleChange");
};
render() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<button onClick={this.handleChange}>this.handleChange</button>
<button onClick={this.state.handleChange}>
this.state.handleChange
</button>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
When you say this.state.something this means something is in the state field of the class. When you say this.someFunction this means something is in the class itself. this here is pointing out our class.
class App extends React.Component {
state = {
something: "Something",
}
someFunction = () => console.log(this.state.something);
render() {
return (
<div>
<button onClick={this.someFunction}>Click</button>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById("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>
<div id="app"></div>
So, you can't use this.state.handleChange since there is no handleChange in the state. It is a function belongs to the class. This is why we use this.handleChange.
you can store a function in state
constructor(super){
super(props)
this.state = {
generateANumber: () => this.setState({ number: Math.floor(Math.random() * 100) }),
number: 0
}
}
then if you want to call it in your render method
render() {
return <p> {this.state.number} <button onClick={() => this.state.generateANumber()} Press Me To Generate A New Number </button> </p>
}
This is the concept of storing a function in state. This.function just means the function belongs to that class so you can use it using the this keyword.

React - How to pass props to a component passed as prop

I have a React component (React v15.5.4) that you can pass other components to:
class CustomForm extends React.Component {
...
render() {
return (
<div>
{this.props.component}
</div>
);
}
}
And I have a different component that uses it:
class SomeContainer extends React.Component {
...
render() {
let someObjectVariable = {someProperty: 'someValue'};
return (
<CustomForm
component={<SomeInnerComponent someProp={'someInnerComponentOwnProp'}/>}
object={someObjectVariable}
/>
);
}
}
Everything renders fine, but I want to pass someObjectVariable prop to the child component inside CustomForm (in this case that'll be SomeInnerComponent), since in the actual code you can pass several components to it instead of just one like the example.
Mind you, I also need to pass SomeInnerComponent its own props.
Is there a way to do that?
You can achieve that by using React.cloneElement.
Like this:
class CustomForm extends React.Component {
...
render() {
return (
<div>
{React.cloneElement(this.props.component,{ customProps: this.props.object })}
</div>
);
}
}
Working Code:
class Parent extends React.Component{
render() {
return(
<Child a={1} comp={<GChild/>} />
)
}
}
class Child extends React.Component{
constructor(){
super();
this.state = {b: 1};
this.updateB = this.updateB.bind(this);
}
updateB(){
this.setState(prevState => ({b: prevState.b+1}))
}
render(){
var Comp = this.props.comp;
return (
<div>
{React.cloneElement(Comp, {b: this.state.b})}
<button onClick={this.updateB}>Click to update b</button>
</div>
);
}
}
const GChild = props => <div>{JSON.stringify(props)}</div>;
ReactDOM.render(
<Parent />,
document.getElementById('container')
);
<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='container' />
You can do in the same as you did for SomeInnerComponent.
Just pass named props.
Inside CustomForm,
render() {
const MyComponent = this.props.component; //stored it in some variable
return (
<div>
<MyComponent customProps = {this.props.object} /> //access object here and passed it or passed individual props
</div>
);
}
EDIT :
Please find the working demo here.
You have a couple of options to achieve what your asking.
class SomeContainer extends React.Component {
...
render() {
let someObjectVariable = {someProperty: 'someValue'};
return (
<CustomForm
component={<SomeInnerComponent propFromParent={someObjectVariable}/>}
object={someObjectVariable}
/>
);
}
}
Or you can clone the component prop and apply the new props as Mayank said. In your case
class CustomForm extends React.Component {
...
render() {
return (
<div>
{React.cloneElement(this.props.component,
{propFromParent:this.props.someObjectVariable})}
</div>
);
}
}
You can use react-overrides for this.
Create CustomForm:
import o from "react-overrides";
const InnerComponent = () => null; // default
class CustomForm extends React.Component {
...
render() {
return (
<div>
<InnerComponent {...o} />
</div>
);
}
}
Pass props and component of InnerComponent at overrides prop:
class SomeContainer extends React.Component {
...
render() {
let someObjectVariable = {someProperty: 'someValue'};
return (
<CustomForm
object={someObjectVariable}
overrides={{
InnerComponent: {
component: SomeInnerComponent,
props: {
someProp: 'someInnerComponentOwnProp'
}
}
}}
/>
);
}
}
<TextField place={"India"}> </TextField>
and in your component TextField
class TextField extends Component {
constructor(props){
super(props);
}
render() {
return (
<div>
<input />
<button> {this.props.place} </button>
</div>
)
}
}
i think what you are trying to achieve is something like this you have to pass your InnerComponent as an arrow function () => ..
class SomeContainer extends React.Component { ... render() {
let someObjectVariable = {someProperty: 'someValue'};
return (
<CustomForm
component={() => <SomeInnerComponent someProp={'someInnerComponentOwnProp'}/>}
object={someObjectVariable}
/>
); } }

Update the state of the grandparent component

I'd like to update a state value of the grandparent component:
class GrandChild extends React.Component {
changeNumber() {
// this.setState(prevState => ({
// number: prevState.number + 1 // Add 1
// }));
// I want to set the state of the App Component instead of the GrandChild component
}
render() {
return(
<div>
<h1>The number is {this.props.number}</h1>
<button onClick={this.changeNumber}>Increase number by 1</button>
</div>
)
}
}
class Child extends React.Component {
render() {
return(
<div>
<GrandChild number={this.props.number}/>
</div>
)
}
}
class App extends React.Component {
constructor() {
super()
this.state = {
number: 1
}
}
render() {
return (
<Child number={this.state.number}/>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
I did code this in CodePen for those who want to test the code: https://codepen.io/anon/pen/oEeQdr
I hope there's a simple solution for this.
Thanks in advance.
class GrandChild extends React.Component {
changeNumber=()=> {
this.props.changeNumber();//call parent `changeNumber` method
}
render() {
return(
<div>
<h1>The number is {this.props.number}</h1>
<button onClick={this.changeNumber}>Increase number by 1</button>
</div>
)
}
}
class Child extends React.Component {
render() {
return(
<div>
<GrandChild number={this.props.number} changeNumber={this.props.changeNumber} /> //passed `changeNumber` as it is to `GrandChild`
</div>
)
}
}
class App extends React.Component {
constructor() {
super()
this.state = {
number: 1
}
}
changeNumber=()=>{
this.setState((prevState)=>{
console.log(prevState);
return {
number : prevState.number + 1
}
});
}
render() {
return (
<Child number={this.state.number} changeNumber = {this.changeNumber}/> //passed `changeNumber` to Child
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
Working codepen
Check working code below:
import React from 'react';
import ReactDOM from 'react-dom';
class GrandChild extends React.Component {
render() {
return (
<div>
<h1>The number is {this.props.number}</h1>
<button onClick={this.props.incNumber}>Increase number by 1</button>
</div>
);
}
}
class Child extends React.Component {
render() {
return (
<div>
<GrandChild number={this.props.number} incNumber={this.props.incNumber} />
</div>
);
}
}
class App extends React.Component {
constructor() {
super();
this.state = {
number: 1
};
}
incrementNumber = () => {
this.setState({ number: this.state.number + 1 });
};
render() {
return (
<Child number={this.state.number} incNumber={this.incrementNumber} />
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
You can send method body as a prop to Child Class, as a pointer to it, and when you wil lcal this method in the Child, it will properly executed in the Parent, where was declared.
class GrandChild extends React.Component {
render() {
return (
<div>
<h1>The number is {this.props.passedProps.number}</h1>
<button onClick={() =>
this.props.passedProps.increaseNumber()}>Increase number by 1</button>
</div>
);
}
}
class Child extends React.Component {
render() {
return (
<div>
<GrandChild passedProps={this.props}/>
</div>
);
}
}
class App extends React.Component {
constructor() {
super()
this.state = {
number: 1
}
}
increaseNumber = () => {
this.setState({ number: this.state.number + 1 });
}
render() {
return (
<Child number={this.state.number} increaseNumber={this.increaseNumber}/>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));

Categories

Resources