0

I don't understand why when I check (availability:1) or uncheck (availability:0) the checkbox, I don't have this info : availability:1 (for example), I only get availability: (empty same for trust and comments), when I console log to see the content of my data sent (whereas I can see the content of my status in console log).

export default function Display() {
  const { menuId } = useParams();
  const [forms, setForms] = useState();
  const [status, setStatus] = useState("");

  useEffect(() => {
    axios.post("", menuId:parseInt(menuId))
      .then((res) => {
        console.log(res);
        setForms(res.data.forms[0]);
      })
      .catch((err) => {
        console.log(err);
      });
  }, [menuId]);

 
  const [data, setData] = useState({
    availability: "",
    trust:"",
    comments:"",
    status:""
  });

  function submit(e) {
    e.preventDefault();
    axios.post(data.availability, data.trust, data.comments, data.status).then((res) => {
      console.log(res.data);
    });
  }

  return (
      <div>       
          <div>
            <button
              type="button"
              onClick={() => setStatus({ status: "save" })}
            >
              Save
            </button>
          </div>
          <div>
            <button
              type="button"
              primary
              onClick={() => setStatus({ status: "not saved" })}
            >
              Not saved
            </button>
          </div>
      </div>
      <hr />
      <form onSubmit={(e) => submit(e)}>
        <span>
          Availability : <Checkbox value={!!forms.types.availability} />
         ...
        </span>
      </form>
  );
}

Checkbox :

export default function Checkbox({ v }) {
    const [checked, setChecked] = useState(v);
    return (
        <label>
            <input
                type="checkbox"
                checked={checked}
                onChange={(e) => setChecked(checked => !checked)}                   
            />
            {v}
        </label>
    );
}

Do you see why please ?

My json from api for menuId:1:

{
  "forms": [
    {
      "menuId": 1,
      
          "_id": "123ml66",
          "name": "Pea Soup",
          "description": "Creamy pea soup topped with melted cheese and sourdough croutons.",
          "types": [
            {
              "availability": 1,
              "trust":0,
              "comments":1
            }
          ],
          ...    
    },
    ...
  }
Zokulko
  • 211
  • 4
  • 25

1 Answers1

1

Updating checked state in the child component won't automatically update the forms state in the parent, since they are not related to one another.

Because const [checked, setChecked] = useState(v); only reads the initialState passed in as v from parent component. Then subsequent state changes are stored in checked, and forms state from parent will not be aware of it. That's why availability's value never gets updated.

Instead, you can pass in both state and setter function to child component:

export default function Display() {
  const [forms, setForms] = useState();

  return (
    <div className="App">
      availability:
      {forms ? (
        <Checkbox
          value={!!forms.types[0].availability}
          setForms={setForms}
          forms={forms}
          param={"availability"}
        />
      ) : (
        "loading"
      )}
    </div>
  )
}

export default function Checkbox({ value, setForms, forms, param }) {
  function processNewForms(forms, param) {
    const newForms = { ...forms }; // make a copy first

    newForms.types[0][param] = newForms.types[0][param] === 0 ? 1 : 0; // toggle logic

    return newForms;
  }

    return (
        <label>
            <input
                type="checkbox"
                checked={value}
                onChange={(e) => setForms(processNewForms(forms, param)}                
            />
            {value}
        </label>
    );
}

See updated sandbox for demo

Enfield Li
  • 1,952
  • 1
  • 8
  • 23
  • Not working, I have a **blank page** when I check or uncheck the checkbox, I've wrote `onChange={(e) => setForms(value => !value)}` – Zokulko Jul 13 '22 at 09:05
  • By doing this `setForms(value => !value)`, you are essentially mutating state directly, and that's not how you update a state. For updating a nested object state please refer to [this answer](https://stackoverflow.com/questions/43638938/updating-an-object-with-setstate-in-react#:~:text=Updating%20nested%20state%20object%3A). – Enfield Li Jul 13 '22 at 09:18
  • I see a lot of example with `class`, and here what I've tried: `onChange={(e) => setForms(checked => {et questions = { ...!checked.questions };return { questions }})}`. Now when I click on the checkbox, it's "frozen" (if the checkbox is already filled, even if I click on it it's not unchecked)... – Zokulko Jul 13 '22 at 15:10
  • I have updated the answer and post a sandbox link, let me know if you have other confusions left. For more info, you can refer to [React beta docs](https://beta.reactjs.org/) – Enfield Li Jul 13 '22 at 15:45
  • And please be aware that, the `types` is actually an array. – Enfield Li Jul 13 '22 at 15:51
  • there is an error in your code sandbox, and please is there a way to get something more general ? here in checkbox you're using availability but if I want to do the same thing for 2 others ?? I've tried to do `types: { availability: forms.types.availability === 1 ? 0 : 1 , trust: forms.trust === 1 ? 0 : 1, comments: forms.comments === 1 ? 0 : 1}`, but then why when I check `trust` (0 at the beginning) it unchecks `availability` and `comments` (same are 1) ? and vice-versa?? – Zokulko Jul 15 '22 at 14:07
  • The sandbox was not updated correctly. Because on page load, there's no data to begin with, so `forms` is undefined(a loading state will solve that, but in my example, we just check it has data or not). If you want to update the other two, assume you have their respective checkbox set up, it should be working the same way as `availability`, otherwise, one single checkbox to handle all three will mess up the logic. But before addressing the other two, please check this [updated sandbox](https://codesandbox.io/s/bold-morning-qnxnrd?file=/src/App.js) and make sure you understood the solution. – Enfield Li Jul 15 '22 at 18:18
  • I'm not using one checkbox to handle all three. I use the same logic as yours by doing : `Availability : Trust : Comments : ` in component `Display` – Zokulko Jul 15 '22 at 20:43
  • and `types: { availability: forms.types.availability === 1 ? 0 : 1 , trust: forms.types.trust === 1 ? 0 : 1, comments: forms.types.comments === 1 ? 0 : 1}` in function `Checkbox` – Zokulko Jul 15 '22 at 20:44
  • Ok, you have seperate checkbox, that's correct, but did you put this `types: { availability: forms.types.availability === 1 ? 0 : 1 , trust: forms.types.trust === 1 ? 0 : 1, comments: forms.types.comments === 1 ? 0 : 1}` all in `onChange` function? If not, can you replicate the situation? – Enfield Li Jul 16 '22 at 03:26
  • Yes all in onChange like this :`onChange={(e)=>setForms({...forms, types: { availability: forms.types.availability === 1 ? 0 : 1 , trust: forms.types.trust === 1 ? 0 : 1, comments: forms.types.comments === 1 ? 0 : 1}})}` – Zokulko Jul 16 '22 at 10:32
  • I mean, of course it will change the other two at the same time, every `onChange` function should deal with it's own variable. If you set up three different checkbox, you'd have different variable value to change. Like `{...forms, types: { availability: forms.types.availability === 1 ? 0 : 1}}` for availability checkbox, `{...forms, types: { trust : forms.types.trust=== 1 ? 0 : 1}}` for trust checkbox, and so on. – Enfield Li Jul 16 '22 at 11:46
  • That's why I was wondering if there is a way to have a generic `Checkbox` function, instead? – Zokulko Jul 16 '22 at 14:25
  • In that case, we can do that by passing additional `param` as I demenstrated in the updated answer/sandbox, using `object[]` syntax to access object key, so as to update object key's value using the `param` value passed in from parent component, please take a look at the updated sandbox. – Enfield Li Jul 16 '22 at 16:17
  • Maybe it's a dumb question but why my `availibility` state (same for others `trust` and `comments` except for `status`) is changing in my `const[forms,setForms]` whereas I'm supposed to see also the changes in my `data` ?? – Zokulko Jul 17 '22 at 18:38
  • I'm not sure if you need the `data` state in your original question, since we are updating the three attributes in `forms` directly. If you do need `data` state, you can add `setData()` to onChange function, in order to update it. – Enfield Li Jul 18 '22 at 04:16
  • If you have other questions related to `How to choose state in react`, you can read this topic [Choosing the State Structure](https://beta.reactjs.org/learn/choosing-the-state-structure) from the beta docs. – Enfield Li Jul 18 '22 at 05:15
  • Thank you. Btw I've opened a [new issue](https://stackoverflow.com/q/73022825/14430938), if you have time to take a look please? – Zokulko Jul 18 '22 at 19:24