React: Persisting State Using Local Storage

Viewed 140

I am coding an app in which there is a collection of reviews and a person can respond to a review, but each review can only have one response. So far, I am doing this by rendering a ReviewResponseBox component in my ReviewCardDetails component and passing the review_id as props. I have implemented the logic so that once there is one ReviewResponse, the form to write another will no longer appear. However, before I was initializing the state in this component with an empty array, so when I refreshed my page the response went away and the form came back up. (This is now commented out)

I am trying to resolve this by persisting my state using React LocalStorage but am having trouble writing my method to do this. Here is what I have so far:

Component that renders ReviewResponseBox and passes review_id as props:

import React from "react";
import './Review.css';
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>
        <div className='card-container'>
          <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>
      </div>
      <br></br>
    <ReviewResponseBox review_id={review.id}/>
    </div>
  );
};

export default ReviewCardDetails;

component that I want to keep track of the state so that it can render the form or response:

import React from 'react';
import ReviewResponse from './ReviewResponse';
import ReviewResponseForm from './ReviewResponseForm';
import { reactLocalStorage } from "reactjs-localstorage";

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

 //   this.state = {
  //    reviewResponses: []
  //  };
//  }

 fetchResponses = () => {
  let reviewResponses = [];
  localStorage.setResponses
  reviewResponses.push(reviewResponse);
}
  
  render () {
    const reviewResponses = this.getResponses();
    const reviewResponseNodes = <div className="reviewResponse-list">{reviewResponses}</div>;
   
    return(
      <div className="reviewResponse-box">
        {reviewResponses.length 
          ? (
            <>
              {reviewResponseNodes}
            </>
          )
          : (
            <ReviewResponseForm addResponse={this.addResponse.bind(this)}/>
          )}
      </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;

Any guidance would be appreciated

1 Answers

You would persist the responses to localStorage when they are updated in state using the componentDidUpdate lifecycle method. Use the componentDidMount lifecycle method to read in the localStorage value and set the local component state, or since reading from localStorage is synchronous directly set the initial state.

I don't think you need a separate package to handle this either, you can use the localStorage API easily.

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

class ReviewResponseBox extends React.Component {
  state = {
    reviewResponses: JSON.parse(localStorage.getItem(`reviewResponses-${this.props.review_id}`)) || []
  };

  storageKey = () => `reviewResponses-${this.props.review_id}`;

  componentDidUpdate(prevProps, prevState) {
    if (prevState.reviewResponses !== this.state.reviewResponses) {
      localStorage.setItem(
        `reviewResponses-${this.props.review_id}`,
        JSON.stringify(this.state.reviewResponses)
      );
    }
  }

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

    return (
      <div className="reviewResponse-box">
        {reviewResponses.length ? (
          <>{reviewResponseNodes}</>
        ) : (
          <ReviewResponseForm addResponse={this.addResponse.bind(this)} />
        )}
      </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}
        />
      );
    });
  }
}
Related