Search code examples
reactjsreact-functional-component

Changing classname of a specific element on click from a mapped array an keeping the same classname until clicked again in react


const allTodos = [{id: 1, name: 'whatever user type'}, { }, { }, .... { }] // Defined as an array using setState in other file. Imported in this file as allTodos using props.

export const Todos = (props) => {

const [index, setIndex] = useState(0)

props.allTodos.map((prev) => {
return (

<div id="item_container">

<button type='button' className = `check_button ${prev.id === index ? 'checked' : 'not_checked'}`
onClick = {() => setIndex(prev.id)}>

<img src = {check} alt="" id = "check_img"></img>

</button>

<li id="li_item">{prev.name}</li>
</div>

)}
}

Explanation : Here, I set up a const index using useState that wiil change its value to the id of the element clicked upon in order to change the className of that element.

Question : Now, I succeeded in doing that but everytime I click on other element, the className gets added to that element but gets removed from the previous element it was added upon. Now I want to somehow preserve the className to those every elements I click on until I click on them again to change their className. By the way the styling that I desire to change is the background of that button/element.


Solution

  • You need to be able to keep track of every index that's checked - for this you'll need an array (or a number you do bit calculations with). A stateful index number doesn't contain enough information.

    const allTodos = [{ id: 1, name: 'whatever user type' }, { id: 2, name: 'foo' }];
    
    const Todos = ({ allTodos }) => {
        const [todos, setTodos] = React.useState(() => allTodos.map(todo => ({ ...todo, checked: false })));
        const handleClick = (i) => () => {
          setTodos(todos.map((todo, j) => j !== i ? todo : ({ ...todo, checked: !todo.checked })))
        };
        return todos.map(({ id, name, checked }, i) => (
            <div id="item_container">
                <button
                    type='button'
                    className={`check_button ${checked ? 'checked' : 'not_checked'}`}
                    onClick={handleClick(i)}
                >
                    <img alt="" id="check_img"></img>
                </button>
                <div>{name}</div>
            </div >
        ))
    }
    
    ReactDOM.render(<Todos allTodos={allTodos} />, document.querySelector('.react'));
    .checked {
      background-color: green;
    }
    .not_checked {
      background-color: yellow;
    }
    <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <div class='react'></div>

    You also need:

    • When not using plain ' " quotes, use {} delimiters around the prop
    • <li>s should only be children of <ul>s
    • Duplicate IDs are invalid HTML
    • You need to return the mapped JSX from the Todos component
    • You are missing a ) at the end of allTodos.map