简体   繁体   中英

Dynamically set defaultChecked with value of setState within data map with react hooks

I am trying to implement a enable and disable toggle button on a list of rows from a data map returned from a function to fetch results from a backend

issue is i need to set defaultChecked to whatever value of el.enabled is within the map of json data but have been un-successful

here is what the data map looks like

data = [
    {
        "id": 1,
        "enabled": true,
    },
    {
        "id": 2,
        "enabled": false,
    },
    {
        "id": 3,
        "enabled": true,
    },
    {
        "id": 4,
        "enabled": false,
    }
]

data is derived from below function that fetched value from API backend

const [data, setData] = useState([]);
    
const fetchData = async () => {
   let json = await getData();
   setData(json);
}

and here is the updateToggle function that enabled or disabled each row

const [statusMode, setStatusMode] = useState();

const updateToggle = async (rowID) => {
    // rowID = el.id
    const updateData = {'id': rowID}

    if ( statusMode == true ) {
      const request = await disable(updateData);
      setStatusMode(false)
    }
    
    if ( statusMode == false ) {
      const request = await enable(updateData);
      setStatusMode(true)
    }
};

and here is what i have that is not working but shows what am trying to achieve

{data.map((el, k) => {
    return (
        <div>
          <div class="checkbox">
            <label class="switch">
                <input type="checkbox" defaultChecked={setStatusMode(el.enabled, k)} onClick={() => updateToggle(el.id, k)} />
            </label>
          </div> 
        </div>
    );
  })}

issue is happening right here defaultChecked={setStatusMode(el.enabled, k)} as i want to set the value to whatever i get from el.enabled for the specific row

error am getting at the moment is

Too many re-renders. React limits the number of renders to prevent an infinite loop.

how can i achieve this? or is there a better way to go about this?

The issue is that you are passing and immediately invoking the state updater function, leading to the render looping.

const [statusMode, setStatusMode] = useState();

{data.map((el, k) => {
  return (
    <div>
      <div class="checkbox">
        <label class="switch">
          <input
            type="checkbox"
            defaultChecked={setStatusMode(el.enabled, k)} // <-- updates state!!
            onClick={() => updateToggle(el.id, k)}
          />
        </label>
      </div> 
    </div>
  );
})}

You should set the defaultChecked value to that of the values being mapped over. Let the onClick handler handle updating the state.

const [statusMode, setStatusMode] = useState();

{data.map((el, k) => {
  return (
    <div key={el.id}>
      <div class="checkbox">
        <label class="switch">
          <input
            type="checkbox"
            defaultChecked={el.enabled} // <-- el.enabled
            onClick={() => updateToggle(el.id, k)}
          />
        </label>
      </div> 
    </div>
  );
})}

Update

Ok, I've gathered that you:

  1. Asynchronously fetch and store in state some "data".
  2. You render this data into checkboxes.
  3. You want to toggle the state and call some endpoints.

Code

const [data, setData] = useState([]);

const fetchData = async () => {
  const json = await getData();
  setData(json);
};

const updateToggle = async (rowID) => {
  // rowID = el.id
  const updateData = {'id': rowID}

  // find elements first
  const dataEl = data.find(el => el.id === rowID);

  // conditionally enable/disable in backend
  if (dataEl.enabled) {
    const request = await disable(updateData);
  } else {
    const request = await enable(updateData);
  }

  // update local state
  setData(data => data.map(el => el.id === rowId
    ? { ...el, enabled: !el.enabled }
    : el
  ));
};

...

{statusMode.map((el) => {
  return (
    <div key={el.id}>
      <div class="checkbox">
        <label class="switch">
          <input
            type="checkbox"
            checked={el.enabled}
            onClick={() => updateToggle(el.id)}
          />
        </label>
      </div> 
    </div>
  );
})}

You could use the enabled data as a prop and assign it to the initial state of the component

You could see the working example here -> https://codesandbox.io/s/clever-cookies-dn7z7?file=/src/App.js:0-873

import { useState } from "react";
import "./styles.css";

const data = [
  {
    id: 1,
    enabled: true
  },
  {
    id: 2,
    enabled: false
  },
  {
    id: 3,
    enabled: true
  },
  {
    id: 4,
    enabled: false
  }
];

export default function App() {
  return (
    <div className="App">
      {data.map((el, k) => {
        return (
          <div>
            <div class="checkbox">
              <label class="switch">
                <SingleCheckbox singleData={el} key={el.id} />
              </label>
            </div>
          </div>
        );
      })}
    </div>
  );
}

function SingleCheckbox({ singleData }) {
  const [statusMode, setStatusMode] = useState(singleData.enabled);

  return (
    <input
      type="checkbox"
      defaultChecked={statusMode}
      onClick={() => setStatusMode(!statusMode)}
    />
  );
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM