Author avatar

Gaurav Singhal

Merge Properties of Array Elements with React.js

Gaurav Singhal

  • Sep 23, 2020
  • 7 Min read
  • 82 Views
  • Sep 23, 2020
  • 7 Min read
  • 82 Views
Web Development
Front End Web Development
Client-side Frameworks
React

Introduction

When you're updating a UI with data from the server, you often need to modify the JSON responses received on the frontend by merging some external properties to the array. These properties can be needed for mapping data to your state and using that state to create controlled components. For instance, if your server returns a list of users and you need to add a checkbox next to each user as a controlled component, you need to append an additional property to every element of the array that keeps track of the selected users.

This guide shows how you can simply loop over a JSON array and merge properties of array elements using JavaScript's advanced array methods such as map() and filter().

Implementation in React

The example demonstrated in this guide covers a simple use case in which your server returns a list of tasks to the frontend and you need to show a checkbox next to each task to mark it as completed or yet to be completed. A generic use case for this example would be an online portal where a manager generates tasks for the employees and the employee updates these tasks at the end of the day by marking them as complete or incomplete.

Integrating Data from an API

For the sake of demonstration, assume that all tasks are returned from the https://jsonplaceholder.typicode.com/todos endpoint. The above endpoint returns some dummy JSON containing a list of todos that can be considered as tasks with reference to this example. You need a state to keep track of this data and a function that makes a GET request to fetch all the data using fetch API. Consider the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import React,{useState,useEffect} from 'react';
import './App.css';

function App() {
  const [tasks,setTasks]=useState([])
  const getTasks=()=>{
    fetch('https://jsonplaceholder.typicode.com/todos')
      .then(response => response.json())
      .then(json =>{
        console.log(json)
    }
  useEffect(()=>{
    getTasks();
  },[])
  
  return (
    <div className="App">
        
    </div>
  );
}

export default App;
jsx

You need to call getTasks() inside useEffect() as shown above in order to make this API call the first time your component mounts on the DOM.

Modifying and Merging Properties of Array Elements

Inspecting the console, you can observe that the array of todos already contains a property completed. Also, the endpoint returns a large number of todos, so filter out the array using the filter() method by keeping only the first 10 todos for simplifying the data and remove the completed property from each todo. The latter is done because the API your app consumes will not return this property from the server.

1
2
3
4
5
6
7
8
9
10
  const getTasks=()=>{
    fetch('https://jsonplaceholder.typicode.com/todos')
      .then(response => response.json())
      .then(json =>{
        let allTasks=json;
        allTasks=allTasks.filter((currentTask)=>{return currentTask.id<=10 && 	    {id:currentTask.id,title:currentTask.title}})
        console.log('taks: ',allTasks)
        
      })
    }
jsx

The completed property is external to this data and must be kept on the frontend only. The next step is to merge this property with the array and set it false for every element by default using the map() method. You should also set your state to allTasks.

1
2
3
4
 allTasks=allTasks.map((currentTask)=>{
     return {...currentTask, completed:false}
 })
 setTasks(allTasks)
javascript

Rendering Data on the DOM

Using a component loop, iterate over the tasks array and render them on the DOM with a controlled input checkbox next to each task.

1
2
3
4
5
6
7
8
9
10
11
return (
    <div className="App">
        {
          tasks && tasks.map(task=>{
            return(
              <p key={task.id}>{task.title} <input onChange={()=>handleChange(task.id)} type="checkbox" checked={task.completed}/></p>
            )
          })
        }
    </div>
  );
jsx

You can leverage the id returned from the API for every task as its key. The checkbox is in line with your state's data, rendering it a controlled component. On selecting or de-selecting the checkbox you need to fire a function that checks if a task has been marked completed and modifies that property accordingly. Attach an onChange event listener to the checkbox input and fire a function that takes the task's id as a parameter.

Accessing and Modifying External Properties

Inside the handleChange() method, loop through your tasks and invert the completed property for the task whose id matches the id passed.

1
2
3
4
5
6
7
8
9
10
 const handleChange=(id)=>{
    let temp=tasks;
    temp=temp.map((t)=>{
      if(t.id===id)
      t.completed=!t.completed
      return t;
    })
    setTasks(temp)
    console.log(id)
  }
javascript

Have a look at the entire code below .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import React,{useState,useEffect} from 'react';
import './App.css';

function App() {
  const [tasks,setTasks]=useState([])
  const getTasks=()=>{
    fetch('https://jsonplaceholder.typicode.com/todos')
      .then(response => response.json())
      .then(json =>{
        let allTasks=json;
        allTasks=allTasks.filter((currentTask)=>{return currentTask.id<=10 && {id:currentTask.id,title:currentTask.title}})
        console.log('taks: ',allTasks)
        allTasks=allTasks.map((currentTask)=>{return {...currentTask, completed:false}})
        setTasks(allTasks)
      })
    }
  useEffect(()=>{
    getTasks();
  },[])
  useEffect(()=>{
    console.log(tasks)
  },[tasks])
  const handleChange=(id)=>{
    let temp=tasks;
    temp=temp.map((t)=>{
      if(t.id===id)
      t.completed=!t.completed
      return t;
    })
    setTasks(temp)
    console.log(id)
  }
  return (
    <div className="App">
        {
          tasks && tasks.length>0 && tasks.map(task=>{
            return(
              <p key={task.id}>{task.title} <input onChange={()=>handleChange(task.id)} type="checkbox" checked={task.completed}/></p>
            )
          })
        }
    </div>
  );
}

export default App;
jsx

Conclusion

You can conveniently merge properties of array elements from a JSON response by manipulating them using JavaScript methods such as map() and filter(). Merging external properties is a good technique as it allows you to handle the data on the frontend the way you want to, but you must ensure that you send back the same structure of data to the server that your database expects.

0