I have a page that renders questions that have been posted. I want to create a button that displays only answered questions based on the state = {isAnswered: true}.
Is the state isAnswered is true then onClick will display answered questions only where isAnswered is set to true in the object.
How can I used this Filter button to conditionally render these based on their state.
Should the function be stored as constant called in the render function or before this?
this.state.posts is an array of these objects on the back end:
Here is what I have attempted.
class Posts extends Component { state = { posts: [] } render () { let posts = <p style={{ textAlign: 'center' }}>Something went wrong!</p>; let {isAnswered} = this.state; const renderAuthButton = () => { if (isAnswered === true) { if ( !this.state.error ) { posts = this.state.posts.map( (post) => { return ( <Post key={post.key} id={post.key} title={post.title} type={post.type} body={post.body} answer={post.answer} onChange={(value, id) => this.postAnswerHandler(value,id)} clicked={(body) => this.displayAnswerHandler(body)} /> ); } ); } } } } return ( <button onClick={renderAuthButton()}>Filter</button> {posts} )
Advertisement
Answer
You are misinterpreting your data structure. this.state
has a property this.state.posts
which is an array. Each element in the array is an object with multiple properties including isAnswered
.
When you do this:
let {isAnswered} = this.state;
You are looking for a property this.state.isAnswered
which does not exist. There is no top-level isAnswered
property. It is something that exists within each post
object and is different for every post. So you need to be looking at isAnswered
inside of your loop.
There’s honestly a lot that’s weird and backwards here. Don’t create a callback inside of render()
! Don’t return JSX from a callback!
Here’s my attempt to clean it up. I am adding a property to this.state
which tells us whether or not to filter the posts. Clicking the button
changes this.state.isFiltered
. The render
function renders appropriately based on the current state.
class Posts extends Component { state = { posts: [], isFiltered: false, isError: false }; async componentDidMount() { // do your API fetch and set the state for `posts` and `isError` try { const fetchedPosts = someApiFunction(); this.setState({ posts: fetchedPosts }); } catch (error) { this.setState({ isError: true }); } } onClickFilter = () => { // toggles filter on and off this.setState((prevState) => ({ isFiltered: !prevState.isFiltered })); }; render() { if (this.state.isError) { return <p style={{ textAlign: "center" }}>Something went wrong!</p>; } // show only answered posts if isFiltered is true, or all posts if false const visiblePosts = this.state.isFiltered ? this.state.posts.filter((post) => post.isAnswered) : this.state.posts; return ( <> <button onClick={this.onClickFilter}>Filter</button> {visiblePosts.map((post) => { return ( <Post key={post.key} id={post.key} title={post.title} type={post.type} body={post.body} answer={post.answer} onChange={(value, id) => this.postAnswerHandler(value, id)} clicked={(body) => this.displayAnswerHandler(body)} /> ); })} </> ); } }