问题
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