-1

so I have a rendered list which was created from an array and each element in the list has a menu button which displays a modal for each element, now the problem is if i click on the button of index 0 instead of showing for only index 0 it show for all, same thing for all other indexes

import React, {useState} from 'react'
import ListModal from './listModal';


export const Todo = ({ Todos, deleteItem }) => {
    const [show, setshow] = useState(false);
    const unshow = () => {
        setshow(current => !current)
    }
    return (
        <ul>
            {
                Todos.map((item, index) => (
                    <li key={index}>
                        <button className="deleteBoard" onClick={unshow}>•••</button>
                        <span>{item}</span>
                        <ListModal value = {item} show={show} unshow = {unshow} />
                    </li>
                ))
            }
        </ul>
    );
};

ListModal is set to display:"none" so when i click the button it displays list modal vice versa

import React, { useState } from 'react'
import firebase from 'firebase/app';
import { db } from '../Firebase/Firebase';
import { useRouteMatch } from 'react-router-dom';


const ListModal = ({ value, show, unshow }) => {
    const [text, settext] = useState(value)
    const styles = {
        display: show ? 'flex' : 'none'
    }
    const textChange = (e) => {
        settext(e.target.value)
    }
    return (
        <div className="listModal" style={styles}>
            <div className="listContent">
                <textarea value={text} rows="6" onChange={textChange}></textarea>
                <button>Save</button>
                <button onClick={unshow}>Cancel</button>
            </div>
            <div className="listOption">
                <ul>
                    <li onClick={() => {deleteItem(value);moveCard()}}>Delete</li>
                </ul>
            </div>

        </div>
    )
}
export default ListModal

Thanks in advance, I don't mind rewriting please just help me out. thanks

Miriam
  • 45
  • 1
  • 6

3 Answers3

0

in the parent component i set up the modal and pass in the values

Todos.map((item, index) => (
                    <li key={index}>
                        <ListModal value={item}/>
                    </li>
                ))

Then in the child component i set up the open and close functions

const [show, setshow] = useState(false);

const styles = {
        display: show ? 'flex' : 'none'
    }
   
    const isOpen = () => {
        setshow(show => !show);
    }

afterwards in the child component you can render the item info and the button with an onclick

//Whatever stuff you want to render from the passed in data


return (
        <span>{value}</span>
        <button onClick={() => {() => isOpen()}}>View</button>

        <div className="listModal" style={styles}>
            <div className="listContent">
                <textarea value={text} rows="6" onChange={textChange}></textarea>
                <button>Save</button>
                <button onClick={unshow}>Cancel</button>
            </div>
            <div className="listOption">
                <ul>
                    <li onClick={() => {deleteItem(value);moveCard()}}>Delete</li>
                </ul>
            </div>

        </div>
    )


Keaton Benning
  • 471
  • 5
  • 13
0

I'd suggest you do the following approach that still can be improved more:

  • instead of unshow method, create two methods showModal and hideModal
    const showModal = (item) => {
      setSelectedTodo(item);
      setshow(true);
    }
    const hideModal = () => {
      setSelectedTodo('');
      setshow(false);
    }
  • use another useState for the selected Todo
    const [selectedTodo, setSelectedTodo] = useState('')
  • have only one ListModal instance after the list, that gets the item from state
    ...
    <ul>
    {
      Todos.map((item, index) => (
        ...
      ))
    }
    </ul>
    <ListModal value={selectedTodo} show={show} unshow = {hideModal} />
    ...
  • on clicking any button, pass the current Todo to the showModal method

    <button className="deleteBoard" onClick={()=> showModal(item)}>•••</button>

Zac
  • 1,497
  • 9
  • 11
  • the unshow method does exactly the same thing as the two methods you recommended, and can you please explain your solution a bit more – Miriam Jan 11 '21 at 21:51
  • the reason why i started using this method instead of your stated method is because i realized it was shorted and performed the same function, but if you feel the other method is better pls elaborate why and i'll fix it – Miriam Jan 11 '21 at 21:57
  • Sure, so first thing first, it should be named based on its functionality, I think by `unshow` you mean `hide`, but this method not hiding modal, it's toggling it, show and hide, so we can name it toggleModal, for example Second, why split it into two methods? in my approach we adding more logic on showing modal (passing and setting the selected Todo), that's only needed in show, so we need to have a separate method to do the show logic, However, there're some better approaches that need more separations, I've suggested this one for simplicity – Zac Jan 11 '21 at 22:25
  • okay now i understand, i actually followed your example and was able to solve it but i had not changed the unshow function. I will post my answer then change it . Thank you – Miriam Jan 12 '21 at 10:24
0

I didn't need to change anything in my listModal component. Here is the parent component where i made some changes

import React, { useState } from 'react'
import ListModal from './listModal';

export const Todo = ({ Todos, deleteItem }) => {
    const [show, setshow] = useState(false);
    const [selected, setselected] = useState("")
    const toggle = () => {
        setshow(show => !show)
    }
    const select = (item) => {
        setselected(item)
    }
    return (
        <>
            <ul>
                {
                    Todos.map((item, index) => (
                        <li key={index}>
                            <button className="deleteBoard" onClick={() => { toggle(); select(item) }}>•••</button>
                            <span>{item}</span>
                        </li>
                    ))
                }
            </ul>
            <ListModal val={selected}  deleteItem={deleteItem} show={show} toggle={toggle} />
        </>
    );
};

Explanation:: Instead of putting listModal in map method thereby creating a listModal component for every element rendered in the list. I kept it outside the map method so i have only one listModal component for every element in the map method. All that changes are the values displayed by listModal.

I also created a method select that takes the the parameter (item) which is the value displayed and stored it in selected. selected is a state, i then passed selected down to listModal.

That's pretty much all i did, sorry if my explanation is poor.

Miriam
  • 45
  • 1
  • 6