redux-storage with redux-saga - not saving - redux

I am trying to use both the redux-storage and redux-saga middleware for redux. Each middleware works great on its own, but when I apply redux-storage with redux-saga, the store does not load the prior state. I am adding the middleware as follows:
const combinedReducers = storage.reducer(combineReducers(reducers));
import createEngine from 'redux-storage-engine-reactnativeasyncstorage';
const engine = createEngine('storage-key');
const storageMiddleWare = storage.createMiddleware(engine);
const sagaMiddleWare = createSagaMiddleware(mySagas);
export const store = createStore(combinedReducers, applyMiddleware( sagaMiddleWare, storageMiddleWare));
const load = storage.createLoader(engine);
load(store).then(() =>
{
console.log("Loaded State");
console.log(store.getState());
}).done();

Middlewares execute in the order they're passed into applyMiddleware. Loading from storage should be before the saga middleware, eg:
const finalCreateStore = compose(
applyMiddleware(
storage.createMiddleware(engine),
createSagaMiddleware(sagas)
)
)(createStore)

D'oh! A bunch of 'EFFECT_TRIGGERED' Actions are emitted right away from redux-saga. These trigger in redux-storage a save event of an empty store, before the store has loaded. The solution is to add EFFECT_TRIGGERED to the blacklist when creating the redux-storage middleware like so:
const storageMiddleWare = storage.createMiddleware(engine, [= 'EFFECT_TRIGGERED']);

Related

Does useQuery run on server-side rendering?

I'm new to Nextjs and have some questions about client-side rendering and server-side rendering in Nextjs
I see there are two ways to fetched data on Nextjs. One of them is to use useQuery hook but only callable on React component function. Does it mean that it only running when rendering the page from the client-side
I read a post about how to connect apolloClient to Nextjs. It said that
always create a new instance of apolloClient for SSR and only create one instance of apolloClient for CSR
Here is the example code
export function initializeApollo(initialState = null) {
const _apolloClient = apolloClient ?? createApolloClient();
// If your page has Next.js data fetching methods that use Apollo Client,
// the initial state gets hydrated here
if (initialState) {
// Get existing cache, loaded during client side data fetching
const existingCache = _apolloClient.extract();
// Restore the cache using the data passed from
// getStaticProps/getServerSideProps combined with the existing cached data
_apolloClient.cache.restore({ ...existingCache, ...initialState });
}
// For SSG and SSR always create a new Apollo Client
if (typeof window === "undefined") return _apolloClient;
// Create the Apollo Client once in the client
if (!apolloClient) apolloClient = _apolloClient;
return _apolloClient;
}
Can anyone explain that? I'm sorry if the question are silly
In Next JS:
SSR - Server side rendering - getServerSideProps
SSG - Static site generated - getStaticPaths & getStaticProps
CSR - Client side rendering - everything else
It is important to note that SSG functions are run server-side.
On the client, you only want to create a single global instance of Apollo Client. Creating multiple instances of Apollo Client will make it challenging to keep things in sync with the client. This difficulty is because the Apollo Cache, Apollo Link, etc., will all be stored in different instances of Apollo Client.
In Next, it is common to place the global instance of Apollo Client on the page _app.js and use the Apollo Provider. On the other client-side pages, you'd use the useQuery hook that calls your single global instance.
The server-side (SSR) functions getStaticProps or getServerSideProps do not have access to the client instance of Apollo, client instance of Next, or other server-side functions. Because of this, you must define your Apollo connection on every page that uses getStaticPaths, getStaticProps, or getServerSideProps and needs access to the Apollo client or it will not be available to the server-side calls.
Since the first rule of hooks is that they must only be called at the top level (client-side), you cannot use them in server-side functions. No, you cannot run useQuery in the Next SSR or SSG functions.
The example you provide is keeping the cache in sync and is outdated in how it is defining the client. Here is a simplified example more along the lines of the official docs.
graphqlClient.js
import { ApolloClient, HttpLink, InMemoryCache } from '#apollo/client';
// Used server and client side - can't use react hooks
export const graphqlClient = new ApolloClient({
cache: new InMemoryCache(),
link: new HttpLink({
uri: 'YOUR_GQL_ENDPOINT',
}),
ssrMode: typeof window === 'undefined',
});
_app.js - a single instance that all client pages use because it wraps the whole app
import graphqlClient from 'my/path/graphqlClient';
const App = ({ Component, pageProps }) => {
const client = graphqlClient();
return (
<ApolloProvider client={client}>
<Component {...pageProps} />
</ApolloProvider>
);
};
Every page/component that is client-side that can use the useQuery hook because Apollo Client is wrapping the app in _app.js
Client-side query
import { gql, useQuery } from '#apollo/client';
const About = () => {
const { data } = useQuery(YOUR_QUERY); // uses your single instance defined in _app.js
return (
...
)
}
Every page that uses SSR or SSG functions and needs access to Apollo must instantiate a new instance of Apollo.
SSG
import graphqlClient from 'my/path/graphqlClient';
//does not have access to _app.js or client and must define new Apollo Client instance
export const getStaticProps = async () => {
const client = graphqlClient();//
const { data } = await client.query({query: YOUR_QUERY});
};
export const getStaticPaths = async () => {
const client = graphqlClient();
const { data } = await client.query({query: YOUR_QUERY});
};
SSR
import graphqlClient from 'my/path/graphqlClient';
//does not have access to _app.js or client and must define new Apollo Client instance
export const getServerSideProps = async () => {
const client = graphqlClient();
const { data } = await client.query({query: YOUR_QUERY});
};
Lastly, to simplify things you can use graphql-code-generator to auto-generate Apollo query, mutation, etc. hooks (and types for TS users) as well as server-side compatible query and mutation functions for Next.js.

Redux tookit access store values outside of component

How can I access redux-toolkit store variables outside of React component? In React components I could access it with
import { useSelector } from 'react-redux';
const isAuthenticated: boolean = useSelector(
(state: RootState) => state.user.isAuthenticated,
);
Since useSelector isn't allowed to access outside of React. Or should I pass variables when I dispatch in React Component? I need access variable in createAsyncThunk API call
createAsyncThunk will pass in a thunkApi object as the second argument to the payload creator callback and on that object, you can call the getState method.
const fetchUserById = createAsyncThunk(
'users/fetchByIdStatus',
async (someArg, thunkApi) => {
const state = thunkApi.getState()
// whatever logic you need.
const response = await userAPI.fetchById(someArg)
return response.data
}
)

Redux - how to access the store "dispatch" after providing it with Provider?

I have the following files:
index.js
const store = createStore(...)
ReactDOM.render(<Provider store={store}><BrowserRouter><App/></BrowserRouter></Provider>, document.getElementById('root'));
The App component: (App.js)
const App = withRouter(connect(mapStateToProps, mapDispatchToProps)(Main))
export default App
So then how i can access store.dispatch inside of the Main component?
If i try to do it by store.dispatch({...}) i get:
'store' is not defined no-undef
If mapDispatchToProps looks like:
const mapDispatchToProps = dispatch => ({
myAction1: () => dispatch(myAction1())
});
connect(mapStateToProps, mapDispatchToProps)(Main)
...then in the component, you can call this.props.myAction1()
If mapDispatchToProps uses bindActionCreators:
const actions = { myAction1, myAction2 };
const mapDispatchToProps = dispatch => bindActionCreators(actions, dispatch);
...then in the component, you can call this.props.myAction1() and this.props.myAction2()
If mapDispatchToProps is undefined:
connect(mapStateToProps)(Main)
then in the component, you can access this.props.dispatch
dispatch function should be available through this.props
this.props.dispatch()
Your component shouldn't access the store directly - connect abstracts that away.
Please see our new React-Redux docs page on connect: Dispatching Actions with mapDispatchToProps for a complete description of how to handle dispatching actions.

redux-offline ignore middlewares when executing commit or rollback actions

As shown at https://github.com/redux-offline/redux-offline/pull/178#issuecomment-408795302, we are trying to use with redux-offline a middleware that can dispatch new actions after their counter-parts commit or rollback are executed. Point is that these ones are not dispatched, and after debugging it, we have found that when dispatching the initial action the middleware is being used as the dispatch() function (probably due to how redux composeEnhancers() and applyMiddleware() function works, since they are chaining the functions), but when the commit action is dispatched, it's done using directly the store dispatch() method, so no middleware is being executed at all.
We are not fully sure if it's a fault on our side regarding redux-offline configuration, or a bug in redux-offline itself... Our store configuration is like this:
import { applyMiddleware, compose, createStore } from 'redux'
import reduxOfflineThunkMiddleware from './thunk-middleware'
import rootReducer from '../../reducers'
import { createUser } from '../../actions/user'
const initialState = {}
const windowCompose = (typeof window !== 'undefined') && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
const composeEnhancers = windowCompose || compose
const store = createStore(
rootReducer,
initialState,
composeEnhancers(
applyMiddleware(reduxOfflineThunkMiddleware({ createUser })),
offline()
)
)
Yes, both offline and applyMiddleware are "store enhancers". When you call store.dispatch, the action sequence will be:
Processed by all the middlewares in the middleware pipeline
Processed by offline
Handled by the store itself
Because the offline enhancer is after the applyMiddleware enhancer, any actions that it dispatches internally will never go through the middleware pipeline.

Configure react redux middleware from fetch

I have a Redux middleware that requires some data to be configured via a server call (eg, fetch) which is async / requires promises.
However, every example of createStoresuch as this one seems to use a Singleton pattern for the store. This means that the store gets initialized before my fetch is complete. Because it's middleware, I can't "reconfigure" it after createStore is called.
How can i configure createStore without using a singleton pattern to configure middleware?
How do you fetch those data? If it's just a simple API call. You can easily wait for the data to be returned then pass the data to createStore. Something like this:
const fetchData = () => {
return Promise.resolve({
data: 'Your data here'
});
}
const initialState = window.__INITIAL_STATE__;
fetchData()
.then((data) => {
initialState.somethingYouNeed = data;
const store = createStore(initialState);
// Do the rest here
});

Resources