React.js - how to not overwrite data in Locale Storage?

Hi,
I created a project when I fetch data.
I can edit the data and save in Locale Storage, but when I add new name the old one is overwritten.
How can I add all changes to Locale Storage?
Thank you in advance.

import React, { useEffect, useState } from "react";
import axios from "axios";
import { Row, Col } from "react-grid-system";
import { getProductId } from "../../utils/_utils";
import { H1, ProductUl, ProductLi } from "../../assets/styles/styles";
import { Card, IMG } from "./styles";

const ProductView = ({ match }) => {
  const [products, setProducts] = useState([]);

  useEffect(() => {
    const fetchData = async () => {
      const result = await axios(
        process.env.PUBLIC_URL + "/data/products.json"
      );
      setProducts(result.data);
    };

    fetchData();
  }, []);

  const productParam = getProductId(match.params.productId);

  const [valueName, setValue] = useState(
    localStorage.getItem(`productName_${productParam}`) || ""
  );
  const [valueNumber, setValueNumber] = useState(
    localStorage.getItem(`productNumber_${productParam}`) || ""
  );
  const [valueDesc, setValueDesc] = useState(
    localStorage.getItem(`productDesc_${productParam}`) || ""
  );

  const [valueImgName_01, setValueImgName_01] = useState(
    localStorage.getItem(`productImgName_01${productParam}`) || ""
  );
  const [valueImgName_02, setValueImgName_02] = useState(
    localStorage.getItem(`productImgName_02${productParam}`) || ""
  );

  useEffect(() => {
    localStorage.setItem(`productName_${productParam}`, valueName);
    localStorage.setItem(`productNumber_${productParam}`, valueNumber);
    localStorage.setItem(`productDesc_${productParam}`, valueDesc);
    localStorage.setItem(`productImgName_01${productParam}`, valueImgName_01);
    localStorage.setItem(`productImgName_02${productParam}`, valueImgName_02);
  }, [
    productParam,
    valueDesc,
    valueImgName_01,
    valueImgName_02,
    valueName,
    valueNumber
  ]);

  const onChangeName = event => setValue(event.target.value);
  const onChangeNumber = event => setValueNumber(event.target.value);
  const onChangeDesc = event => setValueDesc(event.target.value);
  const onChangeImgName01 = event => setValueImgName_01(event.target.value);
  const onChangeImgName02 = event => setValueImgName_02(event.target.value);

  return (
    <>
      {products
        .filter(product => productParam === getProductId(product.number))
        .map(product => {
          const productImg01 = product.images[0];
          const productImg02 = product.images[1];

          return (
            <div key={product.number}>
              <Row>
                <Col>
                  <H1>Product detail</H1>
                </Col>
              </Row>
              <Row>
                <Col lg={6}>
                  <Card>
                    <ProductUl>
                      <ProductLi>
                        <span>Name: </span>
                        <p>{valueName}</p>
                        <input type="text" onChange={onChangeName} />
                      </ProductLi>
                      <ProductLi>
                        <span>Number: </span>
                        <p>{valueNumber}</p>
                        <input type="text" onChange={onChangeNumber} />
                      </ProductLi>
                      <ProductLi>
                        <span>Description: </span>
                        <p>{valueDesc}</p>
                        <input type="text" onChange={onChangeDesc} />
                      </ProductLi>
                      {productImg01 && (
                        <ProductLi>
                          <span>Image name1: </span>
                          <p>{valueImgName_01}</p>
                          <input type="text" onChange={onChangeImgName01} />
                          <IMG src={productImg01.url} alt="product-img" />
                        </ProductLi>
                      )}
                      {productImg02 && (
                        <ProductLi>
                          <span>Image name2: </span>
                          <p>{valueImgName_02}</p>
                          <input type="text" onChange={onChangeImgName02} />
                          <IMG src={productImg02.url} alt="product-img" />
                        </ProductLi>
                      )}
                    </ProductUl>
                  </Card>
                </Col>
              </Row>
            </div>
          );
        })}
    </>
  );
};

export default ProductView;

Hi @deringmagdalena, I guess that value should be dynamic, rather than constant for all products then? Anyway I’d suggest to encapsulate the product logic in a dedicated component, so that each product has its own state(s) and productParam; this way you also can’t overwrite local storage entries of other products (provided that productParam is unique to each product).

But note that writing to the local storage is rather slow, and doing so on every input event might slow down your app quite a bit… so I’d suggest to either debounce persisting the data, or use the (asynchronous) IndexedDB instead (which might be more appropriate for this kind of data anyway).

Thank you, I will try to go your suggested way.

One more question:
I want to replace this part of code for switch statement:

 const onChangeName = event => setValue(event.target.value);
  const onChangeNumber = event => setValueNumber(event.target.value);
  const onChangeDesc = event => setValueDesc(event.target.value);
  const onChangeImgName01 = event => setValueImgName_01(event.target.value);
  const onChangeImgName02 = event => setValueImgName_02(event.target.value);

It is possible?

No. A switch block will execute only one of the branches, you (probably) need to assign all of those functions you have there.

Why do you want to use a switch statement? Using something for the sake of using it is usually not a good idea.

Sure, e.g. you might switch by the element name:

function Product (props) {
  const [name, setName] = useState(props.name)
  const [number, setNumber] = useState(props.number)
  
  const onChange = event => {
    const { name, value } = event.target
  
    switch (name) {
      case 'name':
        setName(value)
        break
  
      case 'number':
        setNumber(value)
        break
  
      // ...
    }
  }

  return (
    <fieldset>
      <input name='name' value={name} onChange={onChange} />
      <input name='number' type='number' value={number} onChange={onChange} />
    </fieldset>
  )
}

… although the useReducer() hook would be more appropriate to incorporate a switch statement, so you don’t have to use multiple state hooks. Actually, you might even use a single state hook here instead:

function Product ({ name, number }) {
  const [state, setState] = useState({ name, number })

  const onChange = event => {
    const { name, value } = event.target
    setState(prevState => ({ ...prevState, [name]: value }))
  }

  return (
    <fieldset>
      <input name='name' value={state.name} onChange={onChange} />
      <input name='number' type='number' value={state.number} onChange={onChange} />
    </fieldset>
  )
}

This would be the least repetitive option, but eventually it depends on the complexity of the state handling in your app – e.g. if you want to map the number to an actual Number(), checking the element name would indeed be necessary.

I want to use switch statement to reducing repetition. This is a main goal.

Thank you for your time.
You have created separate component. I want to directly create switch statement in ProductView component. I tried to implement something like this:

import React, { useEffect, useState } from "react";
import axios from "axios";
import { Row, Col } from "react-grid-system";
import { getProductId } from "../../utils/_utils";
import { H1, ProductUl, ProductLi } from "../../assets/styles/styles";
import { Card, IMG } from "./styles";

const ProductView = ({ match }) => {
  const [products, setProducts] = useState([]);

  useEffect(() => {
    const fetchData = async () => {
      const result = await axios(
        process.env.PUBLIC_URL + "/data/products.json"
      );
      setProducts(result.data);
    };

    fetchData();
  }, []);

  const productParam = getProductId(match.params.productId);

  const [valueName, setValue] = useState(
    localStorage.getItem(`productName_${productParam}`) || ""
  );
  const [valueNumber, setValueNumber] = useState(
    localStorage.getItem(`productNumber_${productParam}`) || ""
  );
  const [valueDesc, setValueDesc] = useState(
    localStorage.getItem(`productDesc_${productParam}`) || ""
  );

  const [valueImgName_01, setValueImgName_01] = useState(
    localStorage.getItem(`productImgName_01${productParam}`) || ""
  );
  const [valueImgName_02, setValueImgName_02] = useState(
    localStorage.getItem(`productImgName_02${productParam}`) || ""
  );

  useEffect(() => {
    localStorage.setItem(`productName_${productParam}`, valueName);
    localStorage.setItem(`productNumber_${productParam}`, valueNumber);
    localStorage.setItem(`productDesc_${productParam}`, valueDesc);
    localStorage.setItem(`productImgName_01${productParam}`, valueImgName_01);
    localStorage.setItem(`productImgName_02${productParam}`, valueImgName_02);
  }, [
    productParam,
    valueName,
    valueNumber,
    valueDesc,
    valueImgName_01,
    valueImgName_02
  ]);

  // const onChangeName = event => setValue(event.target.value);
  const onChangeNumber = event => setValueNumber(event.target.value);
  const onChangeDesc = event => setValueDesc(event.target.value);
  const onChangeImgName01 = event => setValueImgName_01(event.target.value);
  const onChangeImgName02 = event => setValueImgName_02(event.target.value);

  // Added this line
  const handleInputValue = e => {
    const { value } = e.target.value;
    switch ({ Name: "name" }) {
      case "name":
        return setValue(value);
      default:
        return;
    }
  };

  return (
    <>
      {products
        .filter(product => productParam === getProductId(product.number))
        .map(product => {
          const productImg01 = product.images[0];
          const productImg02 = product.images[1];

          return (
            <div key={product.number}>
              <Row>
                <Col>
                  <H1>Product detail</H1>
                </Col>
              </Row>
              <Row>
                <Col lg={6}>
                  <Card>
                    <ProductUl>
                      <ProductLi>
                        <span>Name: </span>
                        <p>{valueName === "" ? product.name : valueName}</p>
                        {/* <input type="text" onChange={onChangeName} /> */}
                        {/* Added this line */}
                        <input
                          type="text"
                          onChange={() => handleInputValue("name")}
                        />
                      </ProductLi>
                      <ProductLi>
                        <span>Number: </span>
                        <p>
                          {valueNumber === "" ? product.number : valueNumber}
                        </p>
                        <input type="text" onChange={onChangeNumber} />
                      </ProductLi>
                      <ProductLi>
                        <span>Description: </span>
                        <p>
                          {valueDesc === "" ? product.description : valueDesc}
                        </p>
                        <input type="text" onChange={onChangeDesc} />
                      </ProductLi>
                      {productImg01 && (
                        <ProductLi>
                          <span>Image name1: </span>
                          <p>
                            {valueImgName_01 === ""
                              ? productImg01.name
                              : valueImgName_01}
                          </p>
                          <input type="text" onChange={onChangeImgName01} />
                          <IMG src={productImg01.url} alt="product-img" />
                        </ProductLi>
                      )}
                      {productImg02 && (
                        <ProductLi>
                          <span>Image name2: </span>
                          <p>
                            {valueImgName_02 === ""
                              ? productImg02.name
                              : valueImgName_02}
                          </p>
                          <input type="text" onChange={onChangeImgName02} />
                          <IMG src={productImg02.url} alt="product-img" />
                        </ProductLi>
                      )}
                    </ProductUl>
                  </Card>
                </Col>
              </Row>
            </div>
          );
        })}
    </>
  );
};

export default ProductView;

But I have got an error -

TypeError: Cannot read property ‘value’ of undefined

You’re calling handleInputValue("name"), but that function seems to expect an event object… and with that destructuring assignment you’re then trying to get the value property of e.target.value, which is one value too much. Also have another look how the switch statement works, you’re currently passing an object literal which will never run into any case clause…