1

I have a functional component and state hooks now the thing is I want to push all the checked items into a particular array of the e.target.name of the useState hook

const [former, setformer] = useState({        
    blocka : [],
    blockb : [],
    blockc : []
})
//This handle event I got some idea from one of stackoverflow : https://stackoverflow.com/questions/37129437/how-do-i-use-react-and-forms-to-get-an-array-of-checked-checkbox-values
const handleCheck = (e) => {
    const fieldname = e.target.name
    const options = former.fieldname
    let index
    if (e.target.checked) {
        options.push(+e.target.value)
    } else {
        index = options.indexOf(+e.target.value)
        options.splice(index, 1)
    }
    setformer({ ...former, [options]: options})
}

<input type="checkbox" name="blocka" onChange={handleCheck} value="a" />
<input type="checkbox" name="blocka" onChange={handleCheck} value="b" />
<input type="checkbox" name="blocka" onChange={handleCheck} value="c" />

<input type="checkbox" name="blockb" onChange={handleCheck} value="d" />
<input type="checkbox" name="blockb" onChange={handleCheck} value="e" />
<input type="checkbox" name="blockb" onChange={handleCheck} value="f" />

<input type="checkbox" name="blockc" onChange={handleCheck} value="g" />
<input type="checkbox" name="blockc" onChange={handleCheck} value="h" />
<input type="checkbox" name="blockc" onChange={handleCheck} value="i" />

now the issue is its throwing a push is not a function Can anyone guide me to complete this code or any changes?

xenon
  • 307
  • 3
  • 11

2 Answers2

1

Since you're already mutating the arrays within the former object directly when you use push/splice, there's no need to use the spread syntax when setting the new former values.

const handleCheck = ({ target }) => {
const { name: blockName, checked, value } = target; // say we click checkbox "a"

// at this point, former = { blocka: [], blockb: [], blockc: [] }
if (checked) {
  former[blockName].push(value); // modifies blocka to become [ "a" ]
} else {
  const index = former[blockName].indexOf(value);
  former[blockName].splice(index, 1);
}

setformer(former); // new former values override old
// { blocka: ["a"], blockb: [], blockc: [] }

};

https://codesandbox.io/s/jolly-platform-eip7p

skoller
  • 89
  • 4
  • You shouldn't mutate state like this. [React docs](https://reactjs.org/docs/react-component.html#state) – Brian Thompson Feb 10 '20 at 18:06
  • @BrianThompson The link you supplied refers to not mutating `this.state`, a property specific to class components in React. In contrast, using a functional component and [the `useState` hook](https://reactjs.org/docs/hooks-reference.html#usestate), as the OPs example code does, means that you can mutate the current value of the stored state variable using its setter function. – skoller Feb 11 '20 at 20:59
  • That's absolutely not true. [Here's one explanation](https://stackoverflow.com/a/55178544/9381601). [Here's it's reference to the docs](https://reactjs.org/docs/hooks-faq.html#is-there-something-like-forceupdate). Regardless of class vs function component, mutating state is wrong. – Brian Thompson Feb 11 '20 at 21:15
  • "using its setter function" is the key part. But you're mutating then updating with the mutated value, not just updating. – Brian Thompson Feb 11 '20 at 21:20
  • To illustrate, open your code sandbox and add a console log before the `return` that says "re-render" or prints the `formValues`. Then click a few checkboxes and see how many times it re-renders and if your `formValues` change. You'll find that it never does, and they never do. The checkboxes are displaying their values because they are uncontrolled inputs now. But the state is never updated. – Brian Thompson Feb 11 '20 at 21:30
1

The real issue here is that you're trying to access a computed object property with the wrong syntax. You also need to make a true copy of the state before mutating it.

const handleCheck = (e) => {
  const fieldname = e.target.name
  // [...old] to create a new array out of the previous values. Protects against mutation
  const options = [...former[fieldname]] // Access object as an array for computed property name

  if (e.target.checked) {
    options.push(e.target.value)
  } else {
    let index = options.indexOf(e.target.value)
    options.splice(index, 1)
  } 
  // use same computed property name as above
  setformer({ ...former, [fieldname]: options})
}
Brian Thompson
  • 13,263
  • 4
  • 23
  • 43
  • But when I call the state I get the previous stage of the state, so is there any way to get the recent update? – xenon Feb 24 '20 at 17:45
  • Where are you trying to access the state and getting the old value? – Brian Thompson Feb 24 '20 at 17:47
  • in the same on handlecheck event if I console log I get [0] then, second time I get the previous state – xenon Feb 24 '20 at 17:55
  • State setters are asynchronous, they don't update right away. In order to view the updated state with hooks, you would have to add your console log to the component body, or in a `useEffect`. The reason you may have been able to view it before is because the state had been mutated and bypassed the actual state update (which you never want to do). – Brian Thompson Feb 24 '20 at 18:05
  • Can you give some example of how you are referencing to? – xenon Feb 24 '20 at 19:02
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/208448/discussion-between-xenon-and-brian-thompson). – xenon Feb 24 '20 at 21:04