Move Items Between Two Components in React
React allows you to share data between components through props. Your component tree can be structured such that two sibling components can share the same data through a parent component’s state.
Nov 10, 2020 • 10 Minute Read
Introduction
React allows you to share data between components through props. Your component tree can be structured such that two sibling components can share the same data through a parent component’s state. This idea can be extended to many use cases, such as moving data or items between two components to convey different meanings to the user.
In this guide, you'll learn how to move items between two components in React using a practical example.
Use Case
A practical use case where you would need to move items between two or more components would be a task-managing web app where employers update the status of given tasks. In this example, you'll build a simple version of a task manager app and move items from one component to another using props. For brevity and simplicity, assume that each task has only two possible statuses,Completed or Pending. On clicking a button, you can move a particular task from Pending to Completed and vice versa.
Implementation
Start by creating an empty React project.
npx create-react-app react-taskmanager
Head over to App.css and add the following styles.
.App{
background: lightgray;
height: 100vh;
}
.items{
padding: 60px 160px;
display: flex;
/* align-items: center; */
justify-content: space-between;
}
.pending,.completed{
background: #ffffff;
width: 400px;
padding: 10px 20px;
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
}
.item{
display: flex;
align-items: center;
justify-content: space-between;
}
button{
cursor: pointer;
padding: 5px 10px;
outline: none;
border-radius: 10px;
border: none;
}
.mark_complete{
background: greenyellow;
}
.mark_pending{
background: tomato;
color: #ffffff;
}
Inside the src folder, create three components: Items.js, Pending.js, and Completed.js. As their names suggest, these components will be used for rendering all items, pending items, and completed items, respectively. Have a look at the following boilerplates for reference.
import React from 'react'
export const Pending = () => {
return (
<div className="pending">
<h1>Pending</h1>
</div>
)
}
Similarly, you can create Completed.js.
import React from 'react'
export const Completed = () => {
return (
<div className="completed">
<h1>Completed</h1>
</div>
)
}
Render both these components inside Items.js.
import React from 'react'
import { Completed } from './Completed'
import { Pending } from './Pending'
export const Items = () => {
return (
<div class="items">
<Pending />
<Completed />
</div>
)
}
Creating the Items Component
As the Items Component is the parent component for both Pending.js and Completed.js, it will need a state to store all the items and a method that will move items from one component to another. First, create a piece of state to store the items or tasks with dummy data, as shown.
import React,{useState,useEffect} from 'react'
import { Completed } from './Completed'
import { Pending } from './Pending'
export const Items = () => {
const [items,setItems]=useState([
{
id: 1,
title:'Manage ORM for client XYZ',
status:'Pending'
},
{
id: 2,
title:'Review Summer Intern project report',
status:'Pending'
},
{
id: 3,
title:'Host Landing Page for Gerry Pizza Shop',
status:'Pending'
},
{
id: 4,
title:'Release Junior Developer payment',
status:'Completed'
},
{
id: 5,
title:'Discuss Digital Marketing requirements ',
status:'Completed'
},
{
id: 6,
title:'Discuss technology budget with CTO',
status:'Pending'
}
])
return (
<div class="items">
<Pending/>
<Completed/>
</div>
)
}
Every item is an object that consists of several properties, including a status property. The status property is used to indicate the component in which this item lies. For instance, if the status of an item says Pending, it will be rendered inside the Pending component, and you can move it to the Completed component. Thus, moving an item from one component to another simply means changing the status property for that particular item.
Create a method that takes in an id and newStatus for that item. Loop through all the items and find the item to update using the id passed to this method, and change its status to newStatus.
const updateStatus=(id,newStatus)=>{
let allItems=items;
allItems=allItems.map(item=>{
if(item.id===id){
console.log('in here')
item.status=newStatus;
}
return item
})
setItems(allItems)
}
Finally, pass both items and updateStatus to the Completed and Pending components.
<Pending items={items} setItems={setItems} updateStatus={updateStatus}/>
<Completed items={items} setItems={setItems} updateStatus={updateStatus}/>
Now both the above components can call the updateStatus method by accessing it through props. You can have a look at the entire Items component below.
import React,{useState,useEffect} from 'react'
import { Completed } from './Completed'
import { Pending } from './Pending'
export const Items = () => {
const [items,setItems]=useState([
{
id: 1,
title:'Manage ORM for client XYZ',
status:'Pending'
},
{
id: 2,
title:'Review Summer Intern project report',
status:'Pending'
},
{
id: 3,
title:'Host Landing Page for Gerry Pizza Shop',
status:'Pending'
},
{
id: 4,
title:'Release Junior Developer payment',
status:'Completed'
},
{
id: 5,
title:'Discuss Digital Marketing requirements ',
status:'Completed'
},
{
id: 6,
title:'Discuss technology budget with CTO',
status:'Pending'
}
])
const updateStatus=(id,newStatus)=>{
let allItems=items;
allItems=allItems.map(item=>{
if(item.id===id){
console.log('in here')
item.status=newStatus;
}
return item
})
setItems(allItems)
}
return (
<div class="items">
<Pending items={items} setItems={setItems} updateStatus={updateStatus}/>
<Completed items={items} setItems={setItems} updateStatus={updateStatus}/>
</div>
)
}
Moving Items by Calling updateStatus()
The final step is to consume the props from the Items component inside the Pending and Completed components to render the list of items and fire the updateStatus() for each item, moving that item from its current component to the next component. You can destructure items and updateStatus() directly from props and loop through them, as shown below. Attach an onClick() event to the Mark Complete button, and pass in the current item id and new status Completed as parameters. Inside this component, only render the item having the status Pending.
import React from 'react'
export const Pending = ({items,setItems,updateStatus}) => {
return (
<div className="pending">
<h1>Pending</h1>
{
items && items.map(item=>{
if(item && item.status==='Pending')
return <><p className="item" key={item.id}>{item.title} <button className="mark_complete" key={item.id} onClick={()=>{updateStatus(item.id,'Completed')}}>Mark Complete</button></p></>
})
}
</div>
)
}
Similarly, you can create the Completed component and pass in Pending as the second parameter to the updateStatus() method. Here you only need to render the item having the status Completed.
import React from 'react'
export const Completed = ({items,setItems,updateStatus}) => {
return (
<div className="completed">
<h1>Completed</h1>
{
items && items.map(item=>{
if(item && item.status==='Completed')
return <><p className="item" key={item.id}>{item.title} <button className="mark_pending" key={item.id} onClick={()=>{updateStatus(item.id,'Pending')}}>Mark Pending</button></p></>
})
}
</div>
)
}
When you click the buttons from the list in the card, the corresponding item's status is changed through props, causing the Items component to re-render. Since both the Completed and Pending components` also re-render, the item renders inside the other component on the DOM and it seems that the item has moved from one component to another.
Conclusion
You can use the example demonstrated in this guide for a variety of use cases, such as passing a username from one component to another in a multi-step signup form. Usually, data or items that are shared between two or more components only seem to be owned by them, but in reality they are owned by the parent component. You can explore libraries like React-Router to pass items or data via your router or Context API and Redux to build a global state and move items by dispatching actions. This type of pattern is preferred for large and complex apps to avoid hefty prop drilling.
If you have any questions regarding this guide, feel free to ask me at Codealphabet.