Search code examples
reactjsreact-hooksreact-props

How can I pass an event to a function itself being passed as props


I am working on a contact list application . Basically using material-ui list of several individuals/persons. Here is a rough outline :

PersonListItem --- Child Component : contains all the person's detail displayed on a card PersonList -- Parent Component : basically maps through all persons and displays all the cards as a list

Recently, I added a star component to each card , the idea being if I want to mark a person as favorite then I would click the star and it turns yellow.

To achieve this I learned and implemented React's lift-state-up with hooks pattern. So now the flow is like so:

Child Component => onClick = {someMethod} => someMethod passed as props to Parent => Parent updates prop component => Child re-renders the component

But now when I click on any single contact's star to mark favorite, all contacts get marked as favorite.

I was thinking to use event.target to get the id of individual star but how do I pass and access the event.target.id in the props ?

Here is my current code:

const PersonListItem = (props) {
    const{person, onSelectChanged, favorite, FavColorColor, markFavorite} = props;

    return(
        <ListItem button
            gender = {undefined}
            onClick = {onSelectChanged}
        <Avatar className = {classes.avatar} src={person.imageLocation === NO_USER_IMAGE : null : person.imageLocation}>
        <IconButton StarOutLinedIcon onClick = {markFavorite} style={{backgroundColor: `${FavColor}`}/>
        <ListItemText primary = 
        {
            {person.name}
            
        }
        secondary = {<div className={classes.smallText}>{person.company}{person.jobTitle}</div>/>
        
        </ListItem>
    )

}

export default PersonListItem;

const PersonList = (props) => {

    const[favorite, setFavorite] = useState(0);
    const[FavColor, setColor] = useState(' ');
    
    const markFavorite = (value) => {
        setFavorite(value = !value);
        {!favorite ? setColor("yellow") : setColor(" ")}
    }
    
    return(
            <div className={classes.root}>
                <MuiList className={props.className} isLoading={isLoading}>
                {
                    !isLoading && person
                    .map((person, index) => {
                        return <PersonListItem key={index}
                                    person = {person}
                                    onSelectChanged = () =>{
                                        HideAdd();
                                    }
                                    markFavorite = {markFavorite} FavColorColor = {FavColor}/>
                                                
                    }
                }
                
                </MuiList>
            </div>
    )

}

export default PersonList;

//Material UI ListItem
classs MuiList extends React.Component{
    
    componentDidMount() {
        i(this.props.fetch)
        {
            this.props.fetch();
        }
    }
    
    render() {
        const attributes = {...this.props};
        delete attributes.isLoading;
        delete attributes.fetch;
        delete attributes.fetchData;
        const items = this.props.isLoading;
                    ? <ListItem key={0}>
                        <CircularProgress/>
                        <ListItemText primary='Loading...'/>
                        </ListItem>
                    : this.props.children;
        return <List {...attributes>{items}</List>
    }
}

export default MuiList;


Solution

  • I would replace favorite state for an object with person ids that will map if they are true or false. also remove favColor state. it's a derived state from favorite, you don't need this state.

    now, I would change the markFavorite function(maybe change name to toggleFavorite). it would receive an id from that person. it checks if it's in favorites; if it's undefined then key doesn't exist yet, so it's been marked as favorite), otherwise only toggle its value:

    const[favorites, setFavorites] = useState({});
    
    const markFavorite = (id) => {
      setFavorites(prevFavorites => {
        const favorites = { ...prevFavorites }
        favorites[id] = favorites[id] === undefined || !favorites[id]
        return favorites
      })
    }
    

    then at your person mapping I would pass markFavorite as an arrow function passing person.id (here I assume person has an id, it's better than passing the index from your map).

    also pass isFavorite with the boolean value of favorites[person.id] (!! returns the boolean value. it's not actually necessary the !! since undefined resolves to false, it's more for consistency type)

    markFavorite={() => markFavorite(person.id)} isFavorite={!!favorites[person.id]}/>
    

    at your PersonListItem you would have to change the IconButton style definition to a ternary condition:

    <IconButton StarOutLinedIcon onClick = {markFavorite} style={{backgroundColor: isFavorite ? 'yellow' : '' }}/>
    

    this way each person can be marked independently, not affecting others.