Reputation: 29
Yeah, so I'm trying to learn react and I'm doing a simple project to understand it better.
basically what I'm trying to do is use useState from one file and let it be changed by an onClick function on the other file like so:
list.js
export const List= [
{id:1, obj:'item1'},
{id:2, obj:'item2'},
{id:3, obj:'item3'},
{id:4, obj:'item4'},] ;
index.js
import React,{useState} from 'react'
import {List} from './list'
import ItemList from './ItemList'
function Items(){
const [items,setItems] = React.useState(List);
return (
<>
<h1>{items.length} Items in total</h1>
{items.map((itemArr)=>{
return (
<ItemList {...itemArr}/>
)
})}
</>
)
}
ReactDom.render(<Items />,document.getElementById('root')
)
ItemList.js
import React from 'react'
function ItemList(props) {
const {id,obj} = props;
const removeItem = (id) =>{
//Code Here
}
return (
<div>
<h3>{id}</h3>
<h4>{obj}</h4>
<button onClick={()=> removeItem(id)}>Remove</button>
</div>
)
}
export default ItemList
What I want to do is when I click the button, the removeItem
function will remove whatever the item is with the id
and use .filter
. But the problem i'm facing is that I can't seem to find out on how I can call and return something to setItems
so that I can change the value. And I can't find out a way to pass setItem
to itemList.js
.
Upvotes: 1
Views: 92
Reputation: 202751
You can pass setItems
as a prop to ItemList
function Items(){
const [items,setItems] = React.useState(List);
return (
<>
<h1>{items.length} Items in total</h1>
{items.map((itemArr) => {
return (
<ItemList {...itemArr} setItems={setItems} /> // <-- pass callback
)
})}
</>
)
}
And consume off props in child. Use a functional state update to access the previous state, filtering it and returning the next state.
function ItemList(props) {
const { id, obj, setItems } = props; // <-- destructure from props
const removeItem = (id) =>{
setItems(items => items.filter(item => item.id !== id)) // <-- update state
}
return (
<div>
<h3>{id}</h3>
<h4>{obj}</h4>
<button onClick={() => removeItem(id)}>Remove</button>
</div>
)
}
Generally though it is preferable to let the component owning the state also maintain all the functions that operate on that state. In other words, you should define the removeHandler
in the parent and pass a reference to it. This allows and makes it the parent component's responsibility to maintain the state invariant, not any child component's responsibility.
function Items(){
const [items,setItems] = React.useState(List);
const removeItem = (id) =>{
setItems(items => items.filter(item => item.id !== id))
}
return (
<>
<h1>{items.length} Items in total</h1>
{items.map((itemArr) => {
return (
<ItemList {...itemArr} removeItem={removeItem} />
)
})}
</>
)
}
function ItemList(props) {
const { id, obj, removeItem } = props;
return (
<div>
<h3>{id}</h3>
<h4>{obj}</h4>
<button onClick={() => removeItem(id)}>Remove</button>
</div>
)
}
Upvotes: 1
Reputation: 131
You need to pass a callback function to the from Items
Component to ItemList
Component.
index.js
import React,{useState} from 'react'
import {List} from './list'
import ItemList from './ItemList'
function Items(){
const [items,setItems] = React.useState(List);
onClickHandler(id){
....
change state
....
}
return (
<>
<h1>{items.length} Items in total</h1>
{items.map((itemArr)=>{
return (
<ItemList onClickHandler={onClickHandler} {...itemArr}/>
)
})}
</>
)
}
ReactDom.render(<Items />,document.getElementById('root')
)
ItemList.js
import React from 'react'
function ItemList(props) {
const {id,obj,onClickHandler} = props;
return (
<div>
<h3>{id}</h3>
<h4>{obj}</h4>
<button onClick={()=> onClickHandler(id)}>Remove</button>
</div>
)
}
export default ItemList
In React data can be passed in one direction only, so to update the data in the parent component from the child component you need to pass functions as props
Upvotes: 0