Added React child component doesn't receive updated value - javascript

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"/>

Related

Child Component not updating after state changes

I am learning react, and I am making a simple ToDoApp. I set some todo data from a JSON file in the state of my App Component and use the values to populate a Child component. I wrote a method to be called each time the onChange event is fired on a checkbox element and flip the checkbox by updating the state. Thing is this code worked perfectly fine before, but it's not anymore. The state gets updated accordingly when I change the checkbox, but it doesn't update in the child element, I'd like to know why. Here's my code
App.js
import React from "react";
import TodoItem from "./TodoItem";
import toDoData from "./toDosData";
class App extends React.Component {
constructor() {
super();
this.state = {
toDoData: toDoData
};
this.handleOnChange = this.handleOnChange.bind(this);
}
handleOnChange(key)
{
this.setState(prevState => {
let newState = prevState.toDoData.map(currentData => {
if(currentData.id === key)
currentData.completed = !currentData.completed;
return currentData;
});
return {toDoData: newState};
});
}
render() {
let toDoComponents = this.state.toDoData.map(toDoDatum =>
<TodoItem key={toDoDatum.id} details={{
key: toDoDatum.id,
text: toDoDatum.text,
completed: toDoDatum.completed,
onChange: this.handleOnChange
}} />);
return (
<div>
{toDoComponents}
</div>
);
}
}
export default App;
TodoItem.js
import React from "react";
class TodoItem extends React.Component {
properties = this.props.details;
render() {
return (
<div>
<input type="checkbox" checked={this.properties.completed}
onChange={() => this.properties.onChange(this.properties.key)}
/>
<span>{this.properties.text}</span>
</div>
)
}
}
export default TodoItem;
Thanks in advance.
Why do you need to assign your details prop to properties in your class? If you do that properties does not reflect the prop changes and your child component can't see the updates. Just use the props as it is:
render() {
const { details } = this.props;
return (
<div>
<input
type="checkbox"
checked={details.completed}
onChange={() => details.onChange(details.key)}
/>
<span>{details.text}</span>
</div>
);
}
}
Also, since you don't use any state or lifecycle method in TodoItem component, it can be a functional component as well.
const TodoItem = ({ details }) => (
<div>
<input
type="checkbox"
checked={details.completed}
onChange={() => details.onChange(details.key)}
/>
<span>{details.text}</span>
</div>
);
One more thing, why don't you pass the todo itself to TodoItem directly?
<TodoItem
key={toDoDatum.id}
todo={toDoDatum}
onChange={this.handleOnChange}
/>
and
const TodoItem = ({ todo, onChange }) => (
<div>
<input
type="checkbox"
checked={todo.completed}
onChange={() => onChange(todo.id)}
/>
<span>{todo.text}</span>
</div>
);
Isn't this more readable?
Update after comment
const toDoData = [
{ id: 1, text: "foo", completed: false },
{ id: 2, text: "bar", completed: false },
{ id: 3, text: "baz", completed: false }
];
const TodoItem = ({ todo, onChange }) => (
<div>
<input
type="checkbox"
checked={todo.completed}
onChange={() => onChange(todo.id)}
/>
<span>{todo.text}</span>
</div>
);
class App extends React.Component {
constructor() {
super();
this.state = {
toDoData: toDoData
};
this.handleOnChange = this.handleOnChange.bind(this);
}
handleOnChange(key) {
this.setState(prevState => {
let newState = prevState.toDoData.map(currentData => {
if (currentData.id === key)
currentData.completed = !currentData.completed;
return currentData;
});
return { toDoData: newState };
});
}
render() {
let toDoComponents = this.state.toDoData.map(toDoDatum => (
<TodoItem
key={toDoDatum.id}
todo={toDoDatum}
onChange={this.handleOnChange}
/>
));
return <div>{toDoComponents}</div>;
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<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" />

React: Remove current instance of component [duplicate]

I have have code that creates <li> elements. I need to delete elements one by one by clicking. For each element I have Delete button. I understand that I need some function to delete items by id. How to do this function to delete elements in ReactJS?
My code:
class TodoApp extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.state = {items: [], text: ''};
}
render() {
return (
<div>
<h3>TODO</h3>
<TodoList items={this.state.items} />
<form onSubmit={this.handleSubmit}>
<input onChange={this.handleChange} value={this.state.text} />
<button>{'Add #' + (this.state.items.length + 1)}</button>
</form>
</div>
);
}
handleChange(e) {
this.setState({text: e.target.value});
}
handleSubmit(e) {
e.preventDefault();
var newItem = {
text: this.props.w +''+this.props.t,
id: Date.now()
};
this.setState((prevState) => ({
items: prevState.items.concat(newItem),
text: ''
}));
}
delete(id){ // How that function knows id of item that need to delete and how to delete item?
this.setState(this.item.id)
}
}
class TodoList extends React.Component {
render() {
return (
<ul>
{this.props.items.map(item => (
<li key={item.id}>{item.text}<button onClick={this.delete.bind(this)}>Delete</button></li>
))}
</ul>
);
}
}
You are managing the data in Parent component and rendering the UI in Child component, so to delete item from child component you need to pass a function along with data, call that function from child and pass any unique identifier of list item, inside parent component delete the item using that unique identifier.
Step1: Pass a function from parent component along with data, like this:
<TodoList items={this.state.items} _handleDelete={this.delete.bind(this)}/>
Step2: Define delete function in parent component like this:
delete(id){
this.setState(prevState => ({
data: prevState.data.filter(el => el != id )
}));
}
Step3: Call that function from child component using this.props._handleDelete():
class TodoList extends React.Component {
_handleDelete(id){
this.props._handleDelete(id);
}
render() {
return (
<ul>
{this.props.items.map(item => (
<li key={item.id}>{item.text}<button onClick={this._handleDelete.bind(this, item.id)}>Delete</button></li>
))}
</ul>
);
}
}
Check this working example:
class App extends React.Component{
constructor(){
super();
this.state = {
data: [1,2,3,4,5]
}
this.delete = this.delete.bind(this);
}
delete(id){
this.setState(prevState => ({
data: prevState.data.filter(el => el != id )
}));
}
render(){
return(
<Child delete={this.delete} data={this.state.data}/>
);
}
}
class Child extends React.Component{
delete(id){
this.props.delete(id);
}
render(){
return(
<div>
{
this.props.data.map(el=>
<p onClick={this.delete.bind(this, el)}>{el}</p>
)
}
</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'/>

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.

Remove class name sibling component when click the current component and make toggling in current to remove class it self

i'm new in react and try to make a button that will remove the sibling class if i click in the current button, and also the current button can remove it self class (toggling),i already try and it seems im not find the clue. here my code below .
export default class Child extends Component {
render(){
return(
<button
onClick={this.props.onClick}
className={this.props.activeMode ? 'active' : ''}>
{this.props.text}
</button>
)
}
}
export default class Parent extends Component {
constructor(props) {
super(props);
this.state = {
selectedIndex: null,
};
}
handleClick (selectedIndex) {
this.setState({
selectedIndex,
});
}
render () {
const array = ["Button 1","Button 2"];
return (
<div>
{array.map((obj, index) => {
const active = this.state.selectedIndex === index;
return <Child
text={obj}
activeMode={active}
key={index}
onClick={() => this.handleClick(index)} />
})}
</div>
)
}
}
I'm not shure that I understood your clearly right
Did you meen something like this?
class Child extends React.Component {
render(){
return(
<button
onClick={this.props.onClick}
className={this.props.activeMode ? 'active' : ''}>
{this.props.text}
</button>
)
}
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedIndex: null,
};
}
handleClick (selectedIndex) {
let selected = this.state.selectedIndex === selectedIndex ? null : selectedIndex;
this.setState({
selectedIndex: selected,
});
}
render () {
const array = ["Button 1","Button 2"];
return (
<div>
{
array.map((obj, index) => {
return <Child text={obj}
activeMode={this.state.selectedIndex === index}
key={index}
onClick={() => this.handleClick(index)} />
})
}
</div>
)
}
}
ReactDOM.render(
<Parent name="World" />,
document.getElementById('container')
);
.active {
color: green;
}
<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">
<!-- This element's contents will be replaced with your component. -->
</div>

My component doesn't rerender

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>

Categories

Resources