How to update nested object state in react , initialize with {} , then json data that I got from api

How to update nested object state in react , initialize with {} , then json data that I got from api.

Why this code is not working:

import React, { useEffect, useState } from 'react';
import { useQuery } from 'react-query';
const App = () => {
  const [testData, setTestData] = useState({});
 
  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch("http://localhost:8080/api/v1/recommendations");
        const result = await response.json(); 
        setTestData(result);
      } catch (err) {
        console.error(`Found error while fetching data: ${err}`);
      }
    };

    fetchData();
  }, []); 


  useEffect(() => {

    setTestData(prevState => {
      const updatedData = {
        ...prevState,
        recommendation: {
          ...prevState.recommendation,
          rules: prevState.recommendation.rules.map(rule => {
            if (rule.scenario_name === "data_rule") {
              return {
                ...rule,
                passwhen: {
                  ...rule.passwhen,
                  STATE: "INDIA"
                }
              };
            }
            return rule;
          })
        }
      };
      return updatedData;
    });
  }, []);

  useEffect(() => {
    console.log("data", testData);
  }, [testData]);

  return (
    <div>
      <pre>{JSON.stringify(testData, null, 2)}</pre>
    </div>
  );
};

export default App;


I’m working on a React application where I need to update the state of a nested object. Initially, my state is set to an empty object {}. I fetch JSON data from an API that should populate this nested state. After fetching the data, I want to update specific keys within this nested object. However, I’m facing issues with my approach. In the provided example, the state isn’t being initialized properly, leading to errors when attempting to update nested properties. How should I properly initialize and update my nested object state with the fetched JSON data, ensuring that the state update is handled correctly and immutably?

Api Data

{
    "recommendation": {
        "rules": [
            {
                "scenario_name": "duplicate_check",
                "scenario_target_query": "SELECT * FROM beat2020.orders_info",
                "columns": [
                    "ORDERNO"
                ],
                "tgt_lvl_key": [
                    "ORDERNO"
                ],
                "idx": 0
            },
            {
                "scenario_name": "null_check",
                "scenario_target_query": "SELECT ORDERNO, QUANTITY_ORDERED, PRICE_EACH, SALES_PRICE, ORDERDATE, STATUS FROM beat2020.orders_info",
                "columns": [
                    "ORDERNO",
                    "QUANTITY_ORDERED",
                    "PRICE_EACH",
                    "SALES_PRICE",
                    "ORDERDATE",
                    "STATUS"
                ],
                "idx": 1
            },
            {
                "scenario_name": "data_validation",
                "scenario_source_query": "SELECT ORDERNUMBER, QUANTITYORDERED, PRICEEACH, ORDERLINENUMBER, SALES, FORMAT(ORDERDATE, 'MM-DD'), CASE WHEN STATUS IS NULL THEN 1 ELSE 2 END, QTR_ID, MONTH_ID, YEAR_ID, PRODUCTLINE, MSRP, PRODUCTCODE, CUSTOMERNAME, PHONE, ADDRESSLINE1, ADDRESSLINE2, CITY, IF(STATE IS NULL, 'US', STATE), POSTALCODE, COUNTRY, TERRITORY FROM beat2020.orders",
                "scenario_target_query": "SELECT ORDERNO, QUANTITY_ORDERED, PRICE_EACH, ORDERLINENO, SALES_PRICE, ORDERDATE, STATUS, QTR_ID, MONTH_ID, YEAR_ID, PRODUCTLINE, MSRP, PRODUCTCODE, CUSTOMERNAME, PHONE, ADDRESSLINE1, ADDRESSLINE2, CITY, STATE, POSTALCODE, COUNTRY, TERRITORY FROM beat2020.orders_info",
                "columns": [
                    "ORDERNUMBER",
                    "QUANTITYORDERED",
                    "PRICEEACH",
                    "ORDERLINENUMBER",
                    "SALES",
                    "ORDERDATE",
                    "STATUS",
                    "QTR_ID",
                    "MONTH_ID",
                    "YEAR_ID",
                    "PRODUCTLINE",
                    "MSRP",
                    "PRODUCTCODE",
                    "CUSTOMERNAME",
                    "PHONE",
                    "ADDRESSLINE1",
                    "ADDRESSLINE2",
                    "CITY",
                    "STATE",
                    "POSTALCODE",
                    "COUNTRY",
                    "TERRITORY"
                ],
                "src_lvl_key": [
                    "ORDERNUMBER"
                ],
                "tgt_lvl_key": [
                    "ORDERNO"
                ],
                "idx": 2
            },
            {
                "scenario_name": "data_rule",
                "scenario_target_query": "SELECT * FROM beat2020.orders_info",
                "passwhen": {
                    "ORDERDATE": "DATE MM-DD",
                    "STATUS": "IN 1,2",
                    "STATE": "IN US"
                },
                "idx": 3
            }
        ]
    }
}

To handle nested object state updates correctly in React, especially when dealing with asynchronous data fetching and updates, you need to follow a structured approach. The main issues in your provided code stem from attempting to update state with an incomplete or incorrect structure and ensuring that state updates are performed immutably.

Here’s a refined approach to handle this scenario:

1. Initialize State

Initialize state with a structure that matches the shape of the data you expect. In your case, this would involve initializing testData to an empty object but ensuring it’s correctly structured when data arrives.

2. Fetch and Set Data

Fetch data from the API and set it in the state. Make sure that the data structure matches what you expect. Ensure to handle scenarios where the data might be empty or have unexpected shapes.

3. Update Nested State

Update the nested state immutably by using functional state updates to ensure you are working with the latest state.

Here’s how you can modify your code:

import React, { useEffect, useState } from 'react';

const App = () => {
  const [testData, setTestData] = useState({});

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch("http://localhost:8080/api/v1/recommendations");
        const result = await response.json();
        setTestData(result);
      } catch (err) {
        console.error(`Found error while fetching data: ${err}`);
      }
    };

    fetchData();
  }, []); 

  useEffect(() => {
    if (testData.recommendation && testData.recommendation.rules) {
      setTestData(prevState => ({
        ...prevState,
        recommendation: {
          ...prevState.recommendation,
          rules: prevState.recommendation.rules.map(rule => {
            if (rule.scenario_name === "data_rule") {
              return {
                ...rule,
                passwhen: {
                  ...rule.passwhen,
                  STATE: "INDIA"
                }
              };
            }
            return rule;
          })
        }
      }));
    }
  }, [testData]); // Depend on testData to ensure this runs after data is set

  useEffect(() => {
    console.log("data", testData);
  }, [testData]);

  return (
    <div>
      <pre>{JSON.stringify(testData, null, 2)}</pre>
    </div>
  );
};

export default App;

Key Points:

  1. Initial State: Initialize testData as an empty object but handle nested structures correctly after fetching data.
  2. State Update:
  • The update to testData should only occur if the recommendation and rules properties exist in the state.
  • Using a functional update to setTestData ensures you are working with the latest state.
  1. Dependency Array:
  • The dependency array of [testData] ensures that the effect that updates testData runs whenever testData changes. This is essential for handling the asynchronous nature of data fetching.

By following these practices, you can effectively manage and update nested state in React applications.