Home > database >  How to use map in React to create independent children elements
How to use map in React to create independent children elements

Time:03-28

I can successfully use map to create multiple elements from inside my array, but the button that I'm creating and the state I'm assigning to each child element seems to be tied back to the first mapping. Code here along with screen shot.

import React, {useState} from 'react';
import './Switch.css'

const Switch = () => {
    const [status, setStatus] = useState(true);  
    
    const meats = [
        "Chicken", 
        "Ground Beef",
        "Sausage"
    ]

    const meatSwitches = meats.map((meat) =>
    
            <>
            <div id="ingredientContainer" className={status === true ? 'containerYes' : 'containerNo'}>
                
                    <h2 id='ingredientLabel'>{meat}</h2>
                    <div id="underLabel">
                        <h3 id='yes'>Sounds Yummy! </h3>
                        <input
                            className="react-switch-checkbox"
                            id={`react-switch-new`}
                            type="checkbox"
                            onClick={() => setStatus(!status)}
                            
                        />
                        <label
                            className="react-switch-label"
                            htmlFor={`react-switch-new`}
                        >
                            <span className={`react-switch-button`} />
                        </label>
                        <h3 id='no'>No, thanks.</h3>
                    </div>
                </div>
            </>
    );

How can I get each child element to have a functioning button, individual of the first and with independent states (in this case of 'true' and 'false').

CodePudding user response:

There are 2 common approaches:

1. Create a Meat child component with its own single boolean state and keep this state at the child level, independent of the parent component Switch. You can pass the status data back to parent if you want, via a callback function onStatusChange, for example with an useEffect().

  const Meat = ({ meat, onStatusChange } ) => {
    const [status, setStatus] = useState(true);
    
    useEffect(() => onStatusChange(status), [status])
    
    return (
    <div
      id="ingredientContainer"
      className={status === true ? 'containerYes' : 'containerNo'}
    >
      // ... your jsx code here
    </div>
  }

And your Switch component will look like this:

const Switch = () => {
    const meats = [
        "Chicken", 
        "Ground Beef",
        "Sausage"
    ]

    const [statusList, setStatusList] = useState([]);  
    const handleStatusChange = (status) => {
      // do what you want with the individual status,
      // perhaps push it in a status array?
      setStatusList((prev) => [...prev, status]);
    };

    const meatSwitches = meats.map((meat) => 
      <Meat key={meat} meat={meat} onStatusChange={handleStatusChange} />
    )

2. Use an array of meat types instead of a boolean state to store checkbox values, then convert it to boolean values later when needed. With this approach you don't have to create a new Meat child component.

const Switch = () => {
    const [status, setStatus] = useState([]);  
    
    const meats = [
        "Chicken", 
        "Ground Beef",
        "Sausage"
    ];
    
    const handleStatusChange = (event, meat) => {
      if (event.target.checked) {
        // push meat type to list
        // when box is checked
        setStatus(prev => [...prev, meat];
      } else {
        // remove meat type from list
        // when box is unchecked
        setStatus(prev => prev.filter(type => type !== meat);
      }
    };

    const meatSwitches = meats.map((meat) =>
      <div
        id="ingredientContainer"
        // check if box is checked
        className={status.includes(meat) ? 'containerYes' : 'containerNo'}
      >
        // code
        <input
          type="checkbox"
          onClick={(event) => handleStatusChange(event, meat)}
        />
        // ...
      </div>
    )

Let me know if this answers your question.

CodePudding user response:

You can set the state to be an array of booleans, and have each button correspond to the index. Example:

const status, setStatus = useState([true, true, true])
const handleChange = (index) => {
  const prev = [...status];
  prev[index] = !prev[index];
  setStatus(prev);
  
}
const meatSwitches = meats.map((meat,index) =>

        <>
        <div id="ingredientContainer" className={status === true ? 'containerYes' : 'containerNo'}>
            
                <h2 id='ingredientLabel'>{meat}</h2>
                <div id="underLabel">
                    <h3 id='yes'>Sounds Yummy! </h3>
                    <input
                        className="react-switch-checkbox"
                        id={`react-switch-new`}
                        type="checkbox"
                        onClick={() => handleChange(index)}
                        
                    />
                    <label
                        className="react-switch-label"
                        htmlFor={`react-switch-new`}
                    >
                        <span className={`react-switch-button`} />
                    </label>
                    <h3 id='no'>No, thanks.</h3>
                </div>
            </div>
        </>
);
  • Related