According to the documentation, GraphQl provides state management like Redux. I have 2 components. In the Component1 i get data from the server using AppoloClient, it works ok, and in the Component2 i want to read data from the cache(store).
//Component1
import React from "react";
import { gql, useQuery } from "#apollo/client";
const EXCHANGE_RATES = gql`
query GetExchangeRates {
rates(currency: "EUR") {
currency
}
}
`;
const Component1 = () => {
const { loading, error, data } = useQuery(EXCHANGE_RATES);
console.log("component1", data);
return <div>Component1</div>;
};
export default Component1;
//Component2
import React from 'react';
import {gql} from "#apollo/client";
import {client} from "./App";
const Component2 = () => {
const info = client.readQuery({
query: gql`
query EXCHANGE_RATES {
rates(currency: "EUR") {
currency
}
}
`,
});
console.log('component2',info);
return (
<div>
component2
</div>
);
};
export default Component2;
Issue: I can get data in component 1, but when I try to read data from component 2, I get undefined.
Question: How to solve this to be able to read data that is fetched in component 1, in component 2? Also how in GraphQl and Apollo client to pass an object for example in the cache, and to read this in component 1(like redux functionality)?
demo: https://codesandbox.io/s/empty-sun-symv6?file=/src/App.js
When the App mounts, both of your component's data are empty.
Then apollo fetches the data with useQuery. And your component1's state got changed. Because of that, component1 re-render and log the new data.
But there is no state on your component2 that changed. So, component2 does not re-render.
To solve this, you can run useQuery hook with the same query on the component2 again, by default apollo will provide you the data from the Cache.
Apollo provides client-side state handling which can be set up to handle your client site state in the same we you do it with your server-side state.
In your example this is not what you want. Recently there is a noticeable shift in the react community that server side data should not be stored in your global state handling tool of choice. Your fetching tool (in your case apollo) should handle this by caching the responses.
To solve your problem, where both components are using the exact same query, you should just do that. Run the query twice and let apollo handle the caching. So you could pull out the query to a query file or just create a useRates hook and import that in your component to even better share the logic between your components.
To answer why your approach is not working you have to understand that your lookup in the cache is happening at a time before your request has even finished and that this cache look up is not "reactive".
Edit: I just got this out fast to provide a starting point and can clean this up later if things got cleared up.
Related
I'm building a small chat app that will use sockets.
Thus I need to use both Context API and a global socket variable. This is my first project using Next.js and I'm not sure how the data flows here.
Beforehand, I used to pass props to the component with React router, however I came to realize that it's not possible with Next.js.
so, my question is whether I'm sharing the socket object correctly.
if that is not the case I would highly appreciate it if you can share with me a code snippet of the right implementation of Sockets utilizing Next.js
My _app.js file:
import { createContext } from 'react';
import '../styles/globals.css'
import UserRoom from '../context/state';
import { useState } from 'react';
function MyApp({ Component, pageProps }) {
const [userName, setUserName] = useState('');
const [userRoom, setUserRoom] = useState('');
const socket = io.connect('http://localhost:4000', { transports : ['websocket'] });
return(
<UserRoom.Provider value={{userName, userRoom, setUserName, setUserRoom}}>
<Component {...pageProps} />
</UserRoom.Provider>
)
}
export default MyApp
The answer is Yes you can, you can share all global states inside _app file, but it will affect to all the components, including the server-side component and the client side, if something depends on the client it will throw an error because window is undefined.
and for the information, this is how next js Data-fetching props flow all work :
Data Fetching > _app > components inside pages
read more here
I'm totally new to Redux.
My understanding is that redux acts like a react hook, for state, and that it is globally available.
I think I should be able to do this (in one component):
import { useStore } from 'react-redux';
function addToStore() { const [user_name, setUser_name] = useStore("jimmy") };
And then recall the variable (in another component) like this:
import { useStore } from 'react-redux';
function getFromStore() { const id = useStore.user_name }
However, this obviously doesn't work. What am I doing wrong? I've tried reading the documentation, but it is too complicated for me, doesn't deal with just a single variable storage.
Easiest Answer:
Turns out this can be done very simply using window.sessionStorage (Mozilla)
So far I've been able to easily store and retrieve variables across the app, without using Redux.
Best Answer:
#liambgs recommended React Context. Turns out this is quite a bit simpler than Redux, but offers much more out of the box than sessionStorage.
To implement Context:
Requires no new installation
Just import { reactContext } from 'react'
Create new component: class UserContextProvider extends Component {}
Wrap all other components in this new component (in App.js)
Move all user api's into UserContextProvider and store data in local state
render() { return ()} the component with value={{this.state}}
This gave all children components access to the UserContext state.
I have a React/Electron application I'm working on in which I want to use data from my Redux store to initialize my Axios client. The use case is, for example, on first load of the app the user enters some information, like their username. This is pushed to the Redux store (and persisted in localStorage for future use), then used in the baseURL of the axios client for subsequent network requests.
The problem is, I can't get axios to work with react-redux and the connect() function. Axios' function exports seem to be hidden by the exported HOC, and any time I call one of its functions I get the following error:
TypeError: _Client2.default.get is not a function
My client looks something like this:
import axios from "axios";
import { connect } from "react-redux";
const Client = ({ init }) => {
return axios.create({
baseURL: `http://${init.ip}/api/${init.username}`
});
};
const mapStateToProps = state => {
return { init: state.init };
};
export default connect(
mapStateToProps,
{}
)(Client);
What am I doing wrong here?
Here in react-redux documentation https://react-redux.js.org/api/connect#connect-returns it says that The return of connect() is a wrapper function that takes your component and returns a wrapper component with the additional props it injects. So it returns react component that wraps react component. Your function returns axios client, it doesn't render anything.
I prefer to use action creators and make api calls there(Therefore I don't pass axios client or whatever). But if I decided to that I would initialize axios client inside reducer and keep in the store. And then pass it to clients as props.
const mapStateToProps = state => {
return { axios: state.axios };
};
On top of #Ozan's answer, In this case what you can do is create a main component, connect it with redux and dispatch an action on mount to initialize axios client.
You should initiate AXIOS client before you load App.js. I recommend you can use redux-axios as redux middleware and use action to call api.
https://github.com/svrcekmichal/redux-axios-middleware
I am currently building an app with React, React Router and React Redux
Versions:
React - v15.5.4
React Router - v4.0
React Redux - v.5.0.6
I am new to React and even newer to Redux and right when I got my head around the connect HOC I started to have this error that I cant seem to figure out.
When I connect a component to my redux store after a <switch> element and some <Route> elements. My connect within that returns my props as false boolean values where as the component within the connect has the correct props.
See code and error below for example.
Component
UserDashboardPage = connect(state => {
console.log("STATE", state);
return {
user: state.user.user,
userAuth: state.user.userAuth,
userFetched: state.user.fetched
};
})(UserDashboardPage);
UserDashboardPage.propTypes = {
user: PropTypes.shape(),
userAuth: PropTypes.shape(),
userFetched: PropTypes.boolean,
dispatch: PropTypes.func
};
CONSOLE LOG STATE
Connect with boolean prop values
Component with correct props
ERROR:
You are overwriting the local UserDashboardPage variable with the result of calling connect(). You then set PropTypes on the component returned by connect().
While you can do that, what you want in this case is to set the PropTypes of the wrapped component, not the wrapper component. Just swapping the order of execution will do it:
UserDashboardPage.propTypes = {
};
UserDashboardPage = connect(state => {
...
})(UserDashboardPage);
But you may want to consider using a different variable name for one component or the other, e.g.
UserDashboardPage.propTypes = {
};
const ConnectedUserDashboardPage = connect(state => {
...
})(UserDashboardPage);
This is usually not a problem since most people just immediately export the connected component as the default export:
export default connect(...)
The false values you're seeing are from React assigning default values to those props that failed validation. And they will always fail validation since those props are pulled from context, not passed down as normal props.
why are you passing UserDashboardPage into connect? This should be your non connected component
I try to learn React.js but I have some difficulties to load data from my server with redux. For the moment my global state is loaded with static json like this:
My store.js
import ...
// Load my static json data
import tickets from '../data/tickets.js'
import interventions from '../data/interventions.js'
const defaultState = {
tickets,
interventions
}
const store = createStore(rootReducer, defaultState);
export const history = syncHistoryWithStore(browserHistory, store);
export default store;
My application works perfectly. Now I want to fill my components with the data returned from my node.js server. However I have no idea how to corretly do it.
I have to fill my defaultState only once ? After the reducers will change the values ?
Is isomorphic-fetch is a good way to call a url from my server ? (I use react-router on my client)
Thanks for your answers.
if you try to do this on client side you would be asynchronous.
On your entry point, where you are importing the store, you can fetch your data then dispatch an action that will init your tickets or interventions.
You can also use redux-async-connect to give your app all required data before it's will be mounted, this can be use also on server rendering side