简体   繁体   中英

How to setState from within a mapped item within React component

I'm trying to change the state of a component that is part of a mapped array of objects from a json file. I want to ONLY change the item containing the clicked button and none of the others.

I've been attempting to set a property (projectID) with an onClick and while I can get it to toggle one element of state (expanded or not expanded), it does it to ALL the returned results. So I've been trying to get the projectId (set in the data) and use that to set a conditional. But I can't seem to get projectId to update with the click. I briefly played around with context but I think there's something simpler I'm missing. I've attempted it within the onClick (as shown) and from within onViewChange, but that didn't seem to work as I can't access item.id from outside the mapped item.

I'm using a conditional based on a prop handed down from a couple levels up to set the category, showing only the objects I want. That part seems to be working. So I removed my expanded state as it's not necessary to the issue here.

 import React from 'react';
 import projects from '../data/projects.json';

 class ProjectCard extends React.Component {

  constructor(props) {
    super(props);
    this.state={
        projects,
        expanded: false,
        projectId: -1
    }
  }

  onViewChange = (e) => {
    this.setState({
        expanded: !this.state.expanded
    });
    console.log(this.state.projectId)
  };

  render() {
    return (
        <React.Fragment>
        {this.state.projects.map((item) => {
            if (!this.state.expanded && item.category === this.props.category) { 
                return (
                        <div key={item.id} className="project-card">
                            <div className="previewImg" style={{backgroundImage: `url(${item.previewImg}` }} ></div>
                            <div className="copy">
                                <h2>{item.title}</h2>
                                <p>{item.quickDesc}</p>
                            </div>
                            <div className="footer">
                            {item.tools.map((tool) => {
                                return ( 
                                    <div key={tool} className="tools" style={{backgroundImage: `url(${tool}` }}></div>
                                );
                            }
                            )}
                                <button onClick={() => this.onViewChange({projectId: item.id})} className="btn float-right"><i className="fas fa-play"></i></button>
                            </div>
                        </div> 
                        );
                    }
            }

        )}
        </React.Fragment>
    );
  };
};

 export default ProjectCard;

I've set a console log to tell me if projectId changes and it always comes back as my default value (-1). The pasted version is the only one that doesn't throw errors with regards to undefined values/objects, but still no changes. If I can get projectId to change based on the item.id from the clicked button, I think I can figure out the conditional and take it from there.

You aren't actually setting the state with the new projectId in your click handler. First step is just simply passing the item's ID to the click handler, not an object:

onClick={() => this.onViewChange(item.id)}

And second part is to actually use that argument in your click handler:

onViewChange = (id) => {
    this.setState({
        expanded: !this.state.expanded,
        projectId: id
    });
 };

Also, setState() is asynchronous so you can't do what you did and console.log your state on the next line and expect it to have changed. Log the state at the top of your render function to see when it changes (after state/props change, render is called). Or another option is to use the optional second argument of setState , which is a callback that's executed with the new state:

this.setState({id: 5}, () => console.log(this.state.id)) <-- id will be 5

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