Reputation: 69
I am trying to build a todo list app.The items will be kept as state variables in App.js and then this state variable are passed to another component (ItemDisplay) that will display it. But whenever I add an item to the state, the component ItemDisplay is not updating. What am I doing wrong?
App.js
import './App.css';
import { useEffect, useState } from 'react';
import Textarea from './textArea';
import ItemDisplay from './itemDisplay';
function App() {
const [items,setItems] = useState([])
const addText = (e,val)=>{
// const tempval = items
// setItems(tempval.push(val))
// console.log(items)
var newarr = items
newarr.push(val)
setItems(newarr)
// console.log(items)
// console.log(newarr)
}
return (
<div className="container">
<div className="heading">
<h2>Todo list</h2>
</div>
<Textarea addText={addText}></Textarea>
<ItemDisplay items={items}></ItemDisplay>
</div>
);
}
export default App;
ItemDsiplay.js
const items = props.items
//console.log(props.items)
return (
<div>
{items.map(item=>(<p>item</p>))}
</div>
);
}
export default ItemDisplay;
TextArea.js
const Textarea = (props) => {
const [todoVal, setTodoval] = useState('')
return (
<div className="textportion">
<input
type="text"
value = {todoVal}
onChange={(e)=>setTodoval(e.target.value)}
placeholder="Enter the list here">
</input>
<button onClick={(e)=>{props.addText(e,todoVal)}}>Add</button>
</div>
);
}
export default Textarea;
Upvotes: 1
Views: 1162
Reputation: 364
You must create a new array to trigger an update. Add an item like this: setItems(prevItems => [...prevItems, val])
. This will create a new array, copy the existing items, and add the new value at the end.
In addition, you should add a unique key
prop to every item rendered by a loop so that React knows when to re-render. Like this: {items.map(item => (<p key={item}>{item}</p>))}
Upvotes: 1
Reputation: 11
In your ItemDisplay.js, you are assigning props.items to a variable. This happens only once. First of all for the component to update automatically you need to assign props.items to a state variable using useState hook. If you need to update the component every time props.items changes, you need to use the useEffect hook:
const [items, setItems] = useState([])
useEffect(()=>{
setItems(props.items)
}, [props.items])
return (
<div>
{items.map(item=>(<p>item</p>))}
</div>
);
Try this in your ItemDisplay.js component. Please import useState and useEffect hooks in the component.
Ya, you are right. It doesn't update because in your App.js you are assigning var newarr = items
. This actually assigns a reference to the items array into newarr. Instead change it to var newarr = [...items]
. This creates a copy of it.
Modified codes are as given below:
import { useState } from 'react';
import Textarea from './TextArea';
import ItemDisplay from './ItemDisplay';
function App() {
const [items, setItems] = useState([])
const addText = (e, val) => {
var newarr = [...items]
newarr.push(val)
setItems(newarr)
}
return (
<div className="container">
<div className="heading">
<h2>Todo list</h2>
</div>
<Textarea addText={addText}></Textarea>
<ItemDisplay items={items}></ItemDisplay>
</div>
);
}
export default App;
import React from 'react'
import { useState, useEffect } from 'react'
function ItemDisplay(props) {
const [items, setItems] = useState([])
useEffect(() => {
console.log('updating')
setItems(props.items)
}, [props.items])
return (
<div>
{items.map(item => (<p>{item}</p>))}
</div>
);
}
export default ItemDisplay
import { useState } from "react";
const Textarea = (props) => {
const [todoVal, setTodoval] = useState('')
return (
<div className="textportion">
<input
type="text"
value={todoVal}
onChange={(e) => setTodoval(e.target.value)}
placeholder="Enter the list here">
</input>
<button onClick={(e) => { props.addText(e, todoVal) }}>Add</button>
</div>
);
}
export default Textarea;
This should solve your problem.
Upvotes: 1
Reputation: 1396
I think you keep the array reference in app component so the child component would not update, update item array in immutable way
const addText = (e,val)=>{
var newarr = newarr.concat([val])
setItems(newarr)
}
Upvotes: 0