Omar
Omar

Reputation: 3411

how to compare id of html element inside of a map function with the id of an object outside of the map function

I have been trying to find a way to compare the id of an HTML element inside of the map function with the id of an object outside the map function. I'm looking for some guidance on how to do this and what the best practice is. When post 1 is clicked It should only show post 1. I've put together a simple example so it is easy to see what I'm trying to do.

In the filter method how do I make the comparison of id of element clicked and id that matches from the posts array of objects.

import React, { Component } from 'react';
import { render } from 'react-dom';
class App extends Component {
  constructor() {
    super();
       this.showPost = this.showPost.bind(this),      

    this.state = {

      posts: [{
          question: 'post 1',
        body: 'BLAH BLAH BLAH BLAH',
        postType: 'Question',
        Id: 123,
      },
      {
          question: 'post 2',
        body: 'BLAH BLAH BLAH BLAH',
        postType: 'Question',    
        Id: 133,
      },
      ],

      openedPost: false,

    };
  }

showPost() {
  console.log("post shown");
  this.setState({ openedPost: true });
}

  render() {
    const posts = this.state.posts.map((item) => 
    <div key={item.Id}> 
      <h1 Id={item.Id} onClick={this.showPost}> {item.question} </h1> 
    </div>
        )
      if(this.state.openedPost) {
       const clickedPost = this.state.posts.filter(function(item) {
           return 
    })

      }
    return (
      <div>
      {posts}
      {clickedPost}
      </div>
    );
  }
}

render(<App />, document.getElementById('root'));

project

Upvotes: 2

Views: 1155

Answers (2)

Sagiv b.g
Sagiv b.g

Reputation: 31024

Instead of passing an id as a DOM attribute and reading it back from the DOM, i recommend to do it with components (this is the react way in my opinion).

Let react deal and "touch" the DOM and let it do it's magic with The Diffing Algorithm and Reconciliation

You should create a <Post /> component that can accept a postId as a prop, as well as an onClick handler that can invoke the parent's handler and pass it the relevant postId.

A running example:

class Post extends React.Component {
  handleClick = () => {
    const { postId, onClick } = this.props;
    onClick(postId);
  }

  render() {
    const { question } = this.props;
    return (
      <div onClick={this.handleClick}>
        <h1>
          {question}
        </h1>
      </div>
    );
  }
}

class App extends React.Component {
  constructor() {
    super();
    this.showPost = this.showPost.bind(this),

      this.state = {
        posts: [{
          question: 'post 1',
          body: '1: BLAH BLAH BLAH BLAH',
          postType: 'Question',
          Id: 123,
        },
        {
          question: 'post 2',
          body: '2: BLAH BLAH BLAH BLAH',
          postType: 'Question',
          Id: 133,
        }],
        openedPost: null
      };
  }

  showPost = (postId) => {
    console.log("post shown id:" + postId);
    this.setState({ openedPost: postId });
  }

  renderPosts = () => {
    const { posts } = this.state;
    return posts.map(post => <Post key={post.Id} onClick={this.showPost} question={post.question} postId={post.Id} />);
  }

  render() {
    const { posts, openedPost } = this.state;
    const rendredPosts = this.renderPosts();
    const clickedPost = openedPost && posts.find(item => item.Id == this.state.openedPost).body;

    return (
      <div>
        {rendredPosts}
        {clickedPost}
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Edit
As a followup to your comment:

so in

return posts.map(post => <Post key={post.Id} onClick={this.showPost} question={post.question} postId={post.Id} />);

we dont have to pass an Id into this.showPost can you explain exactly how it knows to grab the id of that specific element?

This method returns an array of <Post/> component, this component can get a prop of postId (you can name it whatever you like). It also get a prop of an onClick callback as you know.
Inside this component we have an internal handleClick handler that invoking the callback passed from the parent and invoking it while passing it the verry same props.postId the parent passed.

  handleClick = () => {
     // see here we use both the onClick and postId props passed by the parent
    const { postId, onClick } = this.props;
    onClick(postId);
  }  

See this is where I'm struggling to understand. Why wouldnt we pass item.Id into that, how does it automatically know to grab the id of the one clicked?

So i hope now you understand that we did in fact passed the post.Id but not to the onClick callback directly, instead we pass it to the component as a prop itemId, and the component invoked the onClick with this prop via this.props.itemId.

In react the data (should) flow in a uni-directional way.
Parents pass data to children via props (or context).

Sometimes though, we need to pass data back from the child to the parent. Like in your case, the child should pass its ID when clicked.
The way to do that is using callbacks. The parent pass a callback to the child and its the child's responsibility to invoke this method and inject some data to it.

When you pass a callback to a DOM element you can't pass any data back, as the DOM element is not a component you control. the only data that comes back is the event of the trigger (the click event in this case) and you can access the target of this event, that is the real DOM element that triggered the click event and grab attributes from it, like the id.
This is a bad practice as i mentioned above with The Diffing Algorithm and Reconciliation.

So what is the solution and proper way of doing this?

Take control of your elements by composing components.

With components you can do some logic or decide when and how handlers will get invoked, what kind of data they will pass and of course you can reuse them and test them!

I hope that answered your questions and helped you understand a bit more about it.

Upvotes: 1

Austin Ezell
Austin Ezell

Reputation: 763

I would store which post was clicked in the showPost method.

showPost(event){
  const clickedPost = this.state.posts.find( // .find is not supported in IE
    post=>post.id===event.target.id
  )
  this.setState({clickedPost})
}

Upvotes: 0

Related Questions