0

I am updating an array addItems as soon a checkbox is clicked, using action/reducer and dispatch but when I check the array it is not up to date. to resolve this I have introduced async/await promises (I am new to promises) but when I check in then the array is still not updated! What am I missing?

const [addItems, dispatch] = useReducer((state, action) => {
switch (action.type) {
  case "add":
    return [
      ...state,
      {
        id: state.length,
        data: action.data
      }
    ];
  default:
    return state;
}
}, []);

const callDispatch = (sampleId, testId, checked) => {
      console.log("sample: " + sampleId + " t: " + testId + " bool: " + checked);          

      const linkedData = {
        sampleId: sampleId,
        TestId: testId,
        isSelected: checked
      };
  
      return Promise.resolve(
      dispatch({
        type: "add",
        data: linkedData
      }));
    };

const linkDataHandler = async(samplesData, test, e) => {
    
    const isSelected =  e.target.checked;

    await callDispatch(samplesData.id, test.testList.id, isSelected) 
    .then( r =>{
        //addItems is still not updated when checked here
    }})
    .catch(error => console.log(error));

 <input
    type="checkbox"
    onClick={(e) => {
      linkDataHandler(sampleObj, testObj, e);
    }}
  />

A working sample: https://codesandbox.io/s/staging-moon-xv1ku5?file=/src/App.js

Sol1: Tried using Callback as follows but didnot work

import React, { useReducer, useEffect, useState } from "react";
import ReactDOM from "react-dom";
import "./styles.css";
import { useRef } from "react";

export default function App() {
const [addItems, dispatch] = useReducer((state, action) => {
switch (action.type) {
  case "add":
    return [
      ...state,
      {
        id: state.length,
        data: action.data
      }
    ];
  default:
    return state;
 }
 }, []);

  const linkDataHandler = (samplesData, test, e) => {
  const isSelected = e.target.checked;
  console.log(isSelected);
  callDispatch(samplesData, test, isSelected, (user) => {
  console.log(addItems);
  });
};
const callDispatch = (sampleId, testId, checked, callback) => {
const linkedData = {
  sampleId: sampleId,
  TestId: testId,
  isSelected: checked
};

callback(
  dispatch({
    type: "add",
    data: linkedData
  })
);
};

return (
<div className="App">
  <h1>Hello CodeSandbox</h1>
  <input
    type="checkbox"
    onClick={(e) => {
      linkDataHandler(1, 1, e);
    }}
  />
  ABC
</div>
);
}

Sol2: tried adding useeffect

useEffect(() => {
console.log(addItems.length);
  }, addItems);

The final argument passed to useEffect changed size between renders. The order and size of this array must remain constant.

Samra
  • 1,815
  • 4
  • 35
  • 71
  • 1
    you can't update state with async/await, instead you should use a callback function – elguapo Mar 03 '22 at 00:16
  • how exactly @elguapo ? – Samra Mar 03 '22 at 00:24
  • This is what effects are for. Try `useEffect(() => {do stuff}, [addItems])`. This will run whenever addItems updates. – windowsill Mar 03 '22 at 00:54
  • Well what if there are 100 other controls on the page and i dont want "do stuff" when any of them changes state? Will useEffect only trigger for addItems? I am already using useEffect to reload a grid with new data – Samra Mar 03 '22 at 00:57
  • Read the docs for useEffect. The second parameter is a list of dependencies. The effect only runs when one of the dependencies updates. – windowsill Mar 03 '22 at 02:03
  • so if I add addITems to dependencies, it gives me the error that the size changed between renders as mentioned in my post above – Samra Mar 03 '22 at 02:09
  • 1
    you gotta put it in brackets, useEffect(() => { console.log(addItems.length); }, [addItems]); – elguapo Mar 03 '22 at 18:25

0 Answers0