In react check if page has refreshed then set localstorage off of graphql query otherwise keep current local storage for state

寵の児 提交于 2020-05-17 07:08:04

问题


I am attempting to build a hit counter that is based off of a user click. It increments the counter each time the user clicks. I am running GatsbyJS with react.

I am using a lambda function to store the count in faunadb, so it increments each time the button is clicked. This is working.

For the client side I am updating the state of the component hitCounter number by using localStorage, so each time the button is clicked I update localStorage.

All of this works pretty well. However if I change the state of StoryPopFull (seen in code below) by clicking the close button in StoryBlock and reopen by clicking the more button the state keeps the localStorage state which is good. However If I change the state of StoryPop closing the StoryGrid reopen everything the state of SvgAnimation(component where the hit counter lives) is taking it from the graphQL query, now this would be fine if the query came fast enough, but there is about a 20 second delay between the lambda, faunadb and hitting refresh on the page, because I am not using graphql subscriptions so there has to be a refresh of a kind.

What is a good solution so that while the user is using the site localstorage is used to track the counter, but when they return or refresh the page (not a component reload) I can update localstorage with the graphql query.

Here is my code thus far, thanks ahead of time.

import React, { useState, useEffect } from 'react';
import { Link } from "gatsby"
import Layout from "../components/layout"
import { gql } from "apollo-boost"
import { useQuery } from "@apollo/react-hooks"
import rabbit from "../images/rabbit.svg"
import { StoryBlock, StoryGrid, Svg } from "../components/indexStyles"

class SvgAnimation extends React.Component {
  constructor(props) {
    super(props)
    let initialCount = Number(window.localStorage.getItem('count') || 0)
    this.state = {
      buttonDisabled: false,
      hitCounter: initialCount,
    }
    this.handleCLick = this.handleCLick.bind(this);
  }
  componentDidMount(){
    const {data} =this.props
      window.localStorage.setItem('count', data.findHitCounterByID.amount)
  }
  async handleCLick(e) {
    const {count, loading, data} =this.props
    if(this.state.fillAnimate > 0){

      try{
        const response = await fetch("/.netlify/functions/hitCounter", {
          method: "POST",
          body: JSON.stringify({test:"test"}),
        }) 

        if (!response.ok) {
          console.log(response);
        }
        else{
          console.log("succes");
        }
      } catch(e){ console.log(e)}


      let initialCount1 = Number(window.localStorage.getItem('count') || 0)
      window.localStorage.setItem('count', initialCount1 +1)
      let initialCount = Number(window.localStorage.getItem('count') || 0)
      this.setState({ hitCounter: initialCount })
    }

  }
  render() {
    const { buttonDisabled, hitCounter } = this.state;
    return (
      <>
        <button disabled={buttonDisabled}  onClick={e => { this.handleCLick(e) }}>I want this</button>
        <Svg aria-labelledby="title" id="svg" viewBox="0 0 100 125" xmlns="http://www.w3.org/2000/svg">
                ...
        </Svg>
        <span>{hitCounter}</span>
      </>
    )
  }
}


const HIT_COUNTER = gql`
  query HitCounter{
      findHitCounterByID(id: "264173443862233609") {
        amount
      }
  }
`

const IndexPage = () => {
  const [StoryPop, setStoryPop] = useState(false);
  const [StoryPopFull, setStoryPopFull] = useState(false);
  const { loading, data } = useQuery(HIT_COUNTER);

  const rabbitClick = (e) => {
    setStoryPop(true)
  }
  const storyClick = (e) => {
    setStoryPopFull(true)
  }
  const clickClose = (e) => {
    setStoryPopFull(false)
  }
  const clickCloseStoryGrid = (e) => {
    setStoryPop(false)
    setStoryPopFull(false)
  }

  return (

    <Layout>
    <img onClick={(e => { rabbitClick(e) })} onMouseOut={e => { imageHoverOut(e) }} src={rabbit} />
      {StoryPop &&
        <StoryGrid >
          <button onClick={e => { clickCloseStoryGrid(e) }}>Close</button>
            <button onClick={e => { storyClick(e) }}>More</button>
        </StoryGrid>
      }
      {StoryPopFull &&
        <StoryBlock>
          <button onClick={e => { clickClose(e) }}>Close</button>         
            <SvgAnimation count={data.findHitCounterByID.amount} data={data} loading={loading} /> 
        </StoryBlock>
      }
    </Layout>
  )

}

export default IndexPage

回答1:


Have you tried setting a function for after the query completes?

const { loading, data } = useQuery(HIT_COUNTER, {
  onCompleted({ findHitCounterByID }) {
    window.localStorage.setItem('count', findHitCounterByID.amount);
  }
});

This should set the localStorage data to whatever the API response is after it is fetched. You may need to tweak it and other things in your code to make sure it only happens when you want it to. For that you can look at leveraging useLazyQuery and useEffect to call this query only when your component first mounts.

Something like this

const fetchHitCounter = useLazyQuery(HIT_COUNTER, {
  onCompleted({ findHitCounterByID }) {
    window.localStorage.setItem('count', findHitCounterByID.amount);
  }
});

useEffect(() => {
    fetchHitCounter();
}, []);



回答2:


I think the only problem you are having right now is detecting weather or not the page has been refreshed. So if you were able to detect a refresh you could take the appropriate action on the refresh for that i recommend you look at this question :

Detecting Refresh

So once you are able to detect the refresh you could then possibly add an indice in your storage that could indicate that and take the actions you want.



来源:https://stackoverflow.com/questions/61527973/in-react-check-if-page-has-refreshed-then-set-localstorage-off-of-graphql-query

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!