简体   繁体   中英

ReactJS: Dynamic previous & next buttons

I am new to ReactJS and trying to understand how to accomplish a basic concept using best practices. The code below will render a list of items, when an item is clicked a stateful component is toggled and data is passed from to the state object before being consumed by on re-render.

What I need help with is a method to retrieve data from siblings from the array example below. Once the data has been fetched for previous/next projects that data should be accessible to the component. Can these siblings be accessed with a key value? If so, how would I use key?

DATA :

window.Seed = (function() {
  const projects = [
    {id:1, title:'Project 1', description:'Project 1 Description'},
    {id:2, title:'Project 2', description:'Project 2 Description'},
    {id:3, title:'Project 3', description:'Project 3 Description'}
  ]
};

REACT CODE:

class ProjectList extends React.Component {

  state = {
    projects: [],
    isOpen: false,
    modalTitle: '',
    modalDescription: '',
    modalId: ''
  }

  componentDidMount() {
    this.setState({ projects: Seed.projects });
  }

  handleModalOpen = (id, title, description) => {
    this.setState({
      isOpen: true,
      modalId: id,
      modalTitle: title,
      modalDescription: description
    });
  };

  handleModalClose = () => {
    this.setState({ isOpen: false });
  };

  render() {
    const projects = this.state.projects;

    // GOAL #1: Pass previous/next project data as props
    // =================================================
    // Pass projects.map data from previous & next siblings
    // to the current component or explore a new method to 
    // retrieve data from state? I can already tell that this
    // probably is not the most dynamic approach to handle
    // previous/next data.

    const projectComponents = projects.map((project) => (
      <Project
        key={'project-' + project.id}
        id={project.id}
        title={project.title}
        url={project.url}
        description={project.description}
        background={project.background}
        onModalOpen={this.handleModalOpen}
        prevTitle={???}
        nextTitle={???}
        prevDescription={???}
        nextDescription={???}
      />
    ));

    if (this.state.isOpen) {
      return (
        <Modal
          id={this.state.modalId}
          title={this.state.modalTitle}
          description={this.state.modalDescription}
          onModalClose={this.handleModalClose}
        />
      );
    } else {
      return (
        <div className="projects-container">
          <div id="projects" className="wrapper">
            {projectComponents}
          </div>
        </div>
      );
    }
  }
}

class Project extends React.Component {
  render() {
    return (
      <aside className='item'>
        <a onClick={this.props.onModalOpen.bind(
          this, this.props.id, this.props.title, this.props.description
          // I think that I need to bind previous/next data here on click.
          // this.props.prevTitle, this.props.nextTitle
          // this.props.prevDescription, this.props.nextDescription
        )}>
          <img src={this.props.background} />
        </a>
      </aside>
    );
  }
}

class Modal extends React.Component {
  render() {
    return (
      <div className="modal">

        // GOAL #2: Get sibling data from mapped array
        // =================================================
        // I would like to be able to pass previous and next
        // project data as props to populate these headings.

        <h2>Current Project: {this.props.title}</h2>
        <h2>Previous Project: {this.props.prevTitle} </h2>
        <h2>Next Project: {this.props.nextTitle} </h2>

        // GOAL #3: Replace content with data stored in state
        // =================================================
        // I would like to be able to 'onClick' these buttons 
        // to replace the content of the <Modal />. Essentially,
        // this should behave like a lightbox or carousel.

        // Initial Project Description
        <p>{this.props.description}</p>

        // this.props.description = this.props.prevDescription
        <button onClick={???}>Previous Project</button>

        // this.props.description = this.props.nextDescription
        <button onClick={???}>Next Project</button>
        <button onClick={this.props.onModalClose}>Close Modal</button>

      </div>
    );
  }
}

DEMO

Thanks for reading!

UPDATED SOLUTION

As a general principle, data should flow through React components from parent to child, not from sibling to sibling. In theory, it might be possible to pass data between siblings but this makes things orders of magnitude more complex. The beauty of the top-down data flow is that any time the data in the parent changes, the children will (probably, excluding optimizations like pure rendering) re-render. So you just have to change the data in the parent and the children will automatically update as they need to. Here are suggestions for approaching your goals this way. This isn't perfect but I hope it illustrates the point:

Goal #1: Array.prototype.map takes a function with these parameters: function(currentValue, index, array) {...} . For each project, the previous project is array[index - 1] and the next project is array[index + 1] . All you have to do is reference the previous and next projects this way in the map function to get their title, description, etc.

Goal #2: Instead of using this.state.isOpen , use this.state.activeProject . When the project is clicked, set this.state.activeProject to the index of that project. Then you can infer that the modal is open if !!this.state.activeProject (if this.state.activeProject is truthy). Use the activeProject id/index to pass desired data from that project to the Modal component in the ProjectList component render method.

Goal #3: Update this.state.activeProject to the index/id of the previous or next project. This will cause a re-render of ProjectList with the new props. To do this, you'll want to pass onNextClick and onPrevClick props. You can partially apply this.state.activeProject + 1 and this.state.activeProject - 1 to onNextClick and onPrevClick , respectively.

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