Skip to content

Rendering Content Conditionally in React JS Based on the state

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:

enter image description here

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} 
               )

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)}
            />
          );
        })}
      </>
    );
  }
}