Reputation: 3411
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'));
Upvotes: 2
Views: 1155
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.
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
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