CodeBug
CodeBug

Reputation: 1667

state update not affecting on view

I'm working on a basic react project, where I have react state with object and array, what I'm trying to get done here is I want to push the object to the images array, and I want to render that in component, so far I googled things I found few methods to get it done array.concat and array.push, I tried both methods it's updating the state but it's not affecting on the component

here below is the code I had tried

const [proClrData, setProClrData] = useState([
  { color: "black", price: "", qty: "", images: [] },
  { color: "white", price: "", qty: "", images: [] },
  { color: "red", price: "", qty: "", images: [] }
]);
const addImages = (key) => {
  var obj = { src: "", imgdata: "" };
  proClrData[key].images = proClrData[key].images.concat(obj);
  console.log(proClrData);
 };

below is component code

 {proClrData.length >= 1 &&
    proClrData?.map((clr, key) => {
      return (
        <div key={key}>
          <p className="pt-3 ">{clr.color}</p>
          <div className="form-group">
            <input
              label="Price"
              name="price"
              value={clr?.price}
              type="number"
              fullWidth
              required
            />
          </div>
          <div className="form-group pt-3">
            <input
              label="Quantity"
              name="qty"
              value={clr?.qty}
              type="number"
              fullWidth
              required
            />
          </div>
          <div className="text-end pt-3">
            <button
              variant="contained"
              color="primary"
              onClick={() => addImages(key)}
            >
              Add Images
            </button>
          </div>
          {clr?.images?.map((img, imgkey) => {
            return (
              <div className="form-group pt-3" key={imgkey}>
                <input
                  label="Image"
                  name="images"
                  value=""
                  accept="image/*"
                  type="file"
                  fullWidth
                  required
                />
              </div>
            );
          })}
        </div>
      );
    })}

here is codeSandbox of working code.

Upvotes: 1

Views: 64

Answers (4)

Drew Reese
Drew Reese

Reputation: 202608

Issue

You are mutating your state object and then not updating state at all.

const addImages = (key) => {
  var obj = { src: "", imgdata: "" };
  proClrData[key].images = proClrData[key].images.concat(obj); // mutation!!
  console.log(proClrData);
};

Solution

Enqueue a state update with the new image data you want in state. Use a functional state update to update from the previous state. Shallow copy the proClrData array, and for the matching element, also shallow copy it so it is a new object reference, and then also shallow copy the nested images array and append the new object.

const [proClrData, setProClrData] = useState([
  { color: "black", price: "", qty: "", images: [] },
  { color: "white", price: "", qty: "", images: [] },
  { color: "red", price: "", qty: "", images: [] }
]);

const addImages = (key) => {
  const obj = { src: "", imgdata: "" };
  setProClrData(data => data.map((el, i) => i === key ? {
    ...el,
    images: [...el.images, obj],
  } : el));
};

Since React state updates are asynchronously processed, you will want to move the console log into an useEffect hook to log the state after it updates and is available on the next render.

useEffect(() => {
  console.log(proClrData);
}, [proClrData]);

Upvotes: 1

Olivier Boiss&#233;
Olivier Boiss&#233;

Reputation: 18083

you must call setProClrData function to update the state.

Remember that you must not mutate the state, instead you must provide a new reference (a new array in your case) and call the updater function, otherwise React will not be aware that is should re-render the component.

const [proClrData, setProClrData] = useState([
  { color: "black", price: "", qty: "", images: [] },
  { color: "white", price: "", qty: "", images: [] },
  { color: "red", price: "", qty: "", images: [] }
]);

const addImages = (key) => {
  const img = { src: "", imgdata: "" };
  const newProClrData = proClrData.map((obj, index) => {
    return index !== key ? data : { ...obj, images: [...data.images, img] };
  });
  setProClrData(newProClrData);
 };

Upvotes: 1

Deep1man3
Deep1man3

Reputation: 192

You update the state directly

Use the function for this:

setProClrData(prev => prev.push() // or concat)

Upvotes: 0

Charlie Araya
Charlie Araya

Reputation: 524

You should modify proClrData accordingly and be using setProClrData in order to trigger a re-render.

Check the documentation: https://reactjs.org/docs/hooks-state.html

Upvotes: 0

Related Questions