2

I am struggling with figuring out how to implement conditional rendering in React. Basically, what I want to do is this: if there is a reviewResponse in the reviewResponses array, I no longer want to render the reviewResponseForm. I only want to render that ReviewResponse. In other words, each review can only have one response in this app. I am not sure what I am doing wrong when trying to implement this logic. I know I need to implement some kind of conditional statement saying if the length of my reviewResponses array is greater than 0, I need to render the form. Otherwise, I need to render that reviwResponse. Every statement I have written has not worked here. Does anybody have a suggestion? Here is my code so far:

My review cardDetails component renders my ReviewResponseBox component and passed the specific reviewId as props:

import React from "react";
import { useLocation } from "react-router-dom";
import StarRatings from "react-star-ratings";
import ReviewResponseBox from "../ReviewResponse/ReviewResponseBox";

const ReviewCardDetails = () => {
  const location = useLocation();
  const { review } = location?.state; // ? - optional chaining

  console.log("history location details: ", location);
  return (
    <div key={review.id} className="card-deck">
      <div className="card">
        <div>
          <h4 className="card-title">{review.place}</h4>
          <StarRatings
            rating={review.rating}
            starRatedColor="gold"
            starDimension="20px"
          />
          <div className="card-body">{review.content}</div>
          <div className="card-footer">
            {review.author} - {review.published_at}
          </div>
        </div>
      </div>
      <br></br>
       {/*add in conditional logic to render form if there is not a response and response if there is one*/} 
    <ReviewResponseBox review_id={review.id}/>

    </div>
  );
};

export default ReviewCardDetails;

Then eventually I want this component, ReviewResponseBox, to determine whether to render the responseform or the reviewresponse itself, if it exists already.

import React from 'react';
import ReviewResponse from './ReviewResponse';
import ReviewResponseForm from './ReviewResponseForm';

class ReviewResponseBox extends React.Component {
  constructor() {
    super()

    this.state = {
      reviewResponses: []
    };
  }
  
  render () {
    const reviewResponses = this.getResponses();
    const reviewResponseNodes = <div className="reviewResponse-list">{reviewResponses}</div>;
   

    return(
      <div className="reviewResponse-box">
        <ReviewResponseForm addResponse={this.addResponse.bind(this)}/>
       
        <h3>Response</h3>
        {reviewResponseNodes}
        
      </div>  
        
    );
  } 
  

  addResponse(review_id, author, body) {
    const reviewResponse = {
      review_id,
      author,
      body
    };
    this.setState({ reviewResponses: this.state.reviewResponses.concat([reviewResponse]) }); // *new array references help React stay fast, so concat works better than push here.
  }
   
  
  
  getResponses() {    
    return this.state.reviewResponses.map((reviewResponse) => { 
      return (
        <ReviewResponse 
          author={reviewResponse.author} 
          body={reviewResponse.body} 
          review_id={this.state.review_id} />
      ); 
    });
  }

  
  
}
export default ReviewResponseBox;

Here are the ReviewResponseForm and ReviewResponse components:

import React from "react";

class ReviewResponseForm extends React.Component {
  render() {
    return (
      <form className="response-form" onSubmit={this.handleSubmit.bind(this)}>
        <div className="response-form-fields">
          <input placeholder="Name" required ref={(input) => this.author = input}></input><br />
          <textarea placeholder="Response" rows="4" required ref={(textarea) => this.body = textarea}></textarea>
        </div>
        <div className="response-form-actions">
          <button type="submit">Post Response</button>
        </div>
      </form>
    );
  }
  
  handleSubmit(event) { 
    event.preventDefault();   // prevents page from reloading on submit
    let review_id = this.review_id
    let author = this.author;
    let body = this.body;
    this.props.addResponse(review_id, author.value, body.value);
  }
} 

export default ReviewResponseForm;
import React from 'react';

class ReviewResponse extends React.Component {
    render () {
      return(
        <div className="response">
          <p className="response-header">{this.props.author}</p>
          <p className="response-body">- {this.props.body}</p>
          <div className="response-footer">
            </div>
        </div>
      );
    }
  
  }
  export default ReviewResponse;
  

Any advice would be helpful, thank you.

1 Answers1

0

If I understand your question correctly, you want to render ReviewResponseForm if the this.state.reviewResponses state array is empty.

Use the truthy (non-zero)/falsey (zero) array length property to conditionally render either UI element.

render () {
  const reviewResponses = this.getResponses();
  const reviewResponseNodes = <div className="reviewResponse-list">{reviewResponses}</div>;


  return(
    <div className="reviewResponse-box">
      {reviewResponses.length 
        ? (
          <>
            <h3>Response</h3>
            {reviewResponseNodes}
          </>
        )
        : (
          <ReviewResponseForm addResponse={this.addResponse.bind(this)}/>
        )}
    </div>  
    
  );
} 
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • Excellent, that worked- thank you! But when I refresh the page, my review response is gone and the form shows back up again. Any idea why the response isn't sticking around? New to react and to coding in general....any insight would be helpful if you have time. Thank you so much –  Aug 20 '21 at 16:35
  • @bab React state is ephemeral and lives in memory, each time the `ReviewResponseBox` mounts (as when the page & app is reloaded) the `reviewResponses` state is initially set to `[]`. You would need to persist state changes to longer term storage, i.e. localStorage when it's updated, and when the component mounts, read from localStorage and set/initialize the state. – Drew Reese Aug 20 '21 at 16:49