Apollo Cache not updating when changing Query variables - graphql

I'm following this tutorial https://www.howtographql.com/react-apollo/0-introduction/ and I discovered an issue.
I have a component named LinkLists which displays a list of links and it's paginated.
import React, { useEffect } from "react";
import Link from "./Link";
import { useQuery } from "#apollo/client";
import { useNavigate } from "react-router-dom";
import { LINKS_PER_PAGE } from "../utilities/constants";
import FEED_QUERY from "../graphql/queries/feedLinks";
import NEW_LINKS_SUBSCRIPTION from "../graphql/subscriptions/newLinks";
import NEW_VOTES_SUBSCRIPTION from "../graphql/subscriptions/newVotes";
import getQueryVariables from "../utilities/getQueryVariables";
import getLinksToRender from "../utilities/getLinksToRender";
const LinkList = () => {
const navigate = useNavigate();
const isNewPage = window.location.pathname.includes("new");
const pageIndexParams = window.location.pathname.split("/");
const page = parseInt(pageIndexParams[pageIndexParams.length - 1]);
const pageIndex = page ? (page - 1) * LINKS_PER_PAGE : 0;
const { data, loading, error} = useQuery(FEED_QUERY, {
variables: getQueryVariables(isNewPage, page),
});
return (
<>
{loading && <p>Loading...</p>}
{error && <pre>{JSON.stringify(error, null, 2)}</pre>}
{data && (
<>
{getLinksToRender(isNewPage, data).map((link, index) => (
<Link key={link.id} link={link} index={index + pageIndex} />
))}
{isNewPage && (
<div className="flex ml4 mv3 gray">
<div
className="pointer mr2"
onClick={() => {
if (page > 1) {
navigate(`/new/${page - 1}`);
}
}}
>
Previous
</div>
<div
className="pointer"
onClick={() => {
if (page < data.feed.count / LINKS_PER_PAGE) {
const nextPage = page + 1;
navigate(`/new/${nextPage}`);
}
}}
>
Next
</div>
</div>
)}
</>
)}
</>
);
};
export default LinkList;
When next is clicked, it navigates to the next page and renders the next set of links correctly. But after the links are loaded and I try to go to a previous page with new variables, the data is not being updated. I get the recent data but on a different page which is supposed to render older data. I think it has to do something with the cache but I'm not really sure. I'm still learning about Apollo Client and I'm not sure what is going wrong especially that I followed a tutorial.

Related

react-leaflet map.locate() overrides state when updated through <select> in a different component

I am trying to build an app with a map that takes the user to their location when loaded , but once that is done it should move to any country through a select box. I am using a geoJSON file to update the state both in the and the component.
This works when on load, but when I try to change the country borders through the select box the state immediately re-updates to the current location country .
ex. current location is Ireland so when the map is loaded console.log(countryIso_a3) is IRL . When selecting Albania, console.log(countryIso_a3) is ALB but immediately updates to IRL.
Here is my code :
App.js :
import "./App.css";
import { Fragment, useEffect, useState } from "react";
import NavBar from "./components/navbar/Navbar";
import BasicMap from "./components/leaflet/BasicMap";
function App() {
const [countryIso_a3, setCountryIso_a3] = useState("");
const [countryBorders, setCountryBorders] = useState({});
return (
<Fragment>
<NavBar
setCountryIso_a3={setCountryIso_a3}
setCountryBorders={setCountryBorders}
/>
<BasicMap
setCountryIso_a3={setCountryIso_a3}
setCountryBorders={setCountryBorders}
countryBorders={countryBorders}
/>
</Fragment>
);
}
export default App;
BasicMap.js
import React, { useEffect, useState } from "react";
import { MapContainer, TileLayer, Marker, Popup, useMap } from "react-leaflet";
import osm from "./osm_providers";
import "leaflet/dist/leaflet.css";
import icon from "./icons";
import L from "leaflet";
import countryData from "../../countryData.json";
function BasicMap({ countryBorders,setCountryIso_a3, setCountryBorders }) {
function LocationMarker() {
const [center, setCenter] = useState(null);
const [bbox, setBbox] = useState([]);
const map = useMap();
useEffect(() => {
map.locate().on("locationfound", function (e) {
setCenter(e.latlng);
map.flyTo(e.latlng, map.getZoom());
const radius = e.accuracy;
const circle = L.circle(e.latlng, radius);
circle.addTo(map);
setBbox(e.bounds.toBBoxString().split(","));
map.fitBounds(L.geoJson(countryBorders).addTo(map).getBounds());
for (let details of countryData) {
for (let coordinateSet of details.geometry.coordinates) {
for (let coordinates of coordinateSet) {
if (
e.latlng.lat < coordinates[1] &&
e.latlng.lng > coordinates[0]
) {
setCountryIso_a3(details.properties.iso_a3);
setCountryBorders(details.geometry);
}
}
}
}
})
return function cleanup() {
map.stopLocate();
};
}, [map]);
return center === null ? null : (
<Marker position={center} icon={icon}>
<Popup>
You are here. <br />
Map bbox: <br />
<b>Southwest lng</b>: {bbox[0]} <br />
<b>Southwest lat</b>: {bbox[1]} <br />
<b>Northeast lng</b>: {bbox[2]} <br />
<b>Northeast lat</b>: {bbox[3]}
</Popup>
</Marker>
);
}
return (
<MapContainer
center={[49.1951, 16.6068]}
zoom={13}
scrollWheelZoom
style={{ height: "100vh" }}
>
<TileLayer
url={osm.maptiler.url}
attribution={osm.maptiler.attribution}
/>
<LocationMarker />
</MapContainer>
);
}
export default BasicMap;
I have tried wrapping BasicMap in React.memo (React.memo(BasicMap)) and it works but I am not sure this is the right solution.

Create react functional component with hook and publish him in npm package

I try to create own npm pack.
I created and published my component, it is working, but when I add UseEffect in my component I have errors.
What is goin on?
import React, { FC, useEffect, useState } from 'react';
import './Button.scss';
export interface ButtonProps {
children: any;
styles?: Array<string>;
}
const Button: FC<ButtonProps> = (
{
children,
styles,
...props
}) => {
const [active, setActive] = useState(null);
const root_styles = ['pef_button'];
useEffect(()=>{
console.log('JK:DAHJS:JDKHA:SKJhd');
},[])
if(styles){
for (let i = 0; i < styles.length; i++){
root_styles.push(styles[i]);
}
}
return(
<button {...props} className={root_styles.join(' ')} >
{children}
</button>
);
};
export default Button;
I do import this component in my app and have error
import React, {useContext, useState, useEffect} from 'react';
import {Button, Input} from 'My[![enter image description here][1]][1]-react-library'
const MainPage: React.FunctionComponent = () => {
return (
<div>
<div>
<Button >
zxc
</Button>
</div>
</div>
);
};
export default MainPage;
Maybe I should use component classes instead of functional-components
What are you using to package it? I had the same issue while using Rollup. I solved it by adding react and react-dom to external in rollup.config.js.
export default {
...,
external: [
'react',
'react-dom',
]
}

React how to use a state from another component?

I am new to programming and try to learn to react and hands-on some real projects. I am trying to sort out a FreeCodeCamp drum-machine program and I am stuck at trying to use a state from one component to another one. Thank you for any help from you guys.
Please see the source component below:
import React,{useState} from 'react';
import './DrumControl.scss';
import { Switch } from 'antd';
import 'antd/dist/antd.css';
const DrumControl = () => {
const [isOn,setIsOn] = useState(false);
const [bankName,setBankName] = useState("Heater 1");
const [info,setInfo] = useState("");
const onOff = () =>{
isOn ? setIsOn(false) : setIsOn(true);
setInfo("");
}
const changeBankName = ()=>{
bankName === "Heater 1" ? setBankName("Piano") : setBankName("Heater 1");
setInfo(bankName);
}
console.log(info);
return (
<div className="drum-control">
<header>
<a className="navbar-brand text-dark" href="#">
ABC
</a>
</header>
<p className="text-dark fw-bold">Power</p>
<Switch onClick={onOff}/>
<p id="display">{info}</p>
<input type="range" className="form-range" min="0" max="100" id="volume-control"/>
<p className="text-dark fw-bold">Bank</p>
<Switch onClick={changeBankName}/>
</div>
);
}
export default DrumControl;
I am trying to use the bankName from the codes above in following component
import React,{useState} from 'react';
import {bankOne,bankTwo} from './Bank.js';
import {bankName} from './DrumControl';
const Drumpad = () =>{
const [bank,setBank] = useState(bankOne);
const changeBank = () =>{
bankName === "Heater 1" ? setBank(bankOne) : setBank(bankTwo);
}
return(
<h1 className="drum-pad">{bankOne[0].id}</h1>
);
}
export default Drumpad;
Anyway or alternative to sort this out ?
Ok all sorted now, import pubhub.js
https://www.npmjs.com/package/pubsub-js
publisher from drumcontrol
const pubsub = () =>{
PubSub.publish('changeBank',{bankName})
}
subscribe in Drumpad
useEffect(
()=>{
const mySub = PubSub.subscribe('changeBank',chooseBank);
return () => {
PubSub.unsubscribe(mySub);
};
},[]
);
const chooseBank = (changeBank,{bankName}) => {
bankName === 'Heater 1' ? setBank(bankOne) : setBank(bankTwo);
}

Redux store error: <Provider> does not support changing `store` on the fly

I am trying to setup my first react/redux/rails app. I am using react_on_rails gem to pass in my current_user and gyms props.
Everything appears to work ok so far except my console shows error:
<Provider> does not support changing `store` on the fly. It is most likely that you see this error because you updated to Redux 2.x and React Redux 2.x which no longer hot reload reducers automatically. See https://github.com/reactjs/react-redux/releases/tag/v2.0.0 for the migration instructions.
Googling gives me hints that this can happen if you try to create a store within a render method, which causes store to get recreated. I don't see that issue here. Where am I going wrong?
//App.js
import React from 'react';
import { Provider } from 'react-redux';
import configureStore from '../store/gymStore';
import Gym from '../components/Gym';
const App = props => (
<Provider store={configureStore(props)}>
<Gym />
</Provider>
);
export default App;
../store/gymStore.jsx
//the store creation.
/*
// my original way
import { createStore } from 'redux';
import gymReducer from '../reducers/';
const configureStore = railsProps => createStore(gymReducer, railsProps);
export default configureStore;
*/
/* possible fix: https://github.com/reactjs/react-redux/releases/tag/v2.0.0 */
/* but adding below does not resolve error */
import { createStore } from 'redux';
import rootReducer from '../reducers/index';
export default function configureStore(railsProps) {
const store = createStore(rootReducer, railsProps);
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept(() => {
const nextRootReducer = require('../reducers').default;
store.replaceReducer(nextRootReducer);
});
}
return store;
}
I am not sure my rendered component is necessary but in case it is:
//compenents/Gym.jsx
import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
import LeftMenu from './LeftMenu';
class Gym extends React.Component {
static propTypes = {
//name: PropTypes.string.isRequired // this is passed from the Rails view
};
/**
* #param props - Comes from your rails view.
*/
constructor(props) {
super(props);
this.state = {
current_user: this.props.current_user,
gyms: JSON.parse(this.props.gyms),
active_gym: 1, //JSON.parse(this.props.gyms)[0],
name: 'sean',
title: 'Gym Overview'
};
}
updateName = name => {
this.setState({ name });
};
isLoggedIn = () => {
if (this.state.current_user.id != '0') {
return <span className="text-success"> Logged In!</span>;
} else {
return <span className="text-danger"> Must Log In</span>;
}
};
isActive = id => {
if (this.state.active_gym == id) {
return 'text-success';
}
};
render() {
return (
<div className="content">
<h2 className="content-header">{this.state.title}</h2>
{LeftMenu()}
{this.state.current_user.id != '0' ? <span>Welcome </span> : ''}
{this.state.current_user.first_name}
<h3 className="content-header">Your Gyms</h3>
<ul>
{this.state.gyms.map((gym, key) => (
<li key={key} className={this.isActive(gym.id)}>
{gym.name}
</li>
))}
</ul>
{this.isLoggedIn()}
<hr />
{/*
<form>
<label htmlFor="name">Say hello to:</label>
<input
id="name"
type="text"
value={this.state.name}
onChange={e => this.updateName(e.target.value)}
/>
</form>
*/}
</div>
);
}
}
function mapStateToProps(state) {
return {
current_user: state.current_user,
gyms: state.gyms,
active_gym: state.active_gym
};
}
export default connect(mapStateToProps)(Gym);

Set state like this props in react

Hello i have this code
import React from 'react'
import Link from 'react-router/lib/Link'
import { connect } from "react-redux"
import { load } from '../../actions/customerActions'
import List from './list';
import MdAdd from 'react-icons/lib/md/add'
#connect(store => {
return {
customers: store.customer.customers
}
})
export default class Customer extends React.Component {
componentDidMount() {
this.props.dispatch(load({location:localStorage.getItem('locationb')}));
}
render() {
const { customers } = this.props;
const tea = customers.customers && customers.customers.map(customer => <List key={customer.profile} customer={customer} />) || [];
return(
<div class="container">
{ customers.customers ?
<div class="columns is-multiline mb-100">
{ tea }
</div>
: 'Não exitem Clientes'}
<Link to="/customer/create" class="button is-info btn-rounded" title="Novo"><MdAdd /></Link>
</div>
)
}
}
But i only have access to customers in render passing this props.
How can i pass customer to a state variable in component did mount or else ?
i mean customers const { customers } = this.props; how i make like this.setState({customers: customers}) Having in the beginning this.state(customers: [])
YOu can use
componentWillReceiveProps(newProps){
console.log(newProps.customers.customers)
this.setState({customers: newProps.customers.customers})
}
it works for me
thanks
Carlos Vieira

Resources