How to set initial redux state from asynchronous source in react native? - reactjs

I trying to load all the asynchronous data like user objects from asynchronous storage when the store first-time initialize. But I do not have any idea how to do that. I got one of the suggestions to create a middleware to do this but I don't know-how.
Current Store and its initialization in app
const store = createStore(rootReducer, {}, applyMiddleware(ReduxThunk,
//actionCallbackOnceMiddleware(INITIAL_AJAX_END, AppSafeAreaProvider)
))
const AppSafeAreaProvider = () => {
return <Provider store={store}>
<PaperProvider>
<SafeAreaProvider>
<App />
</SafeAreaProvider>
</PaperProvider>
</Provider>
}
AppRegistry.registerComponent(appName, () => AppSafeAreaProvider);

I'm not sure about the method for setting up redux on React Native since I only use React.js but if you are going to setup your store to handle asynchronous call handlers, your best bet would be to look into middlewares.
https://redux.js.org/api/applymiddleware
Here is an excellent example of how the async requests can be called using the middleware and redux-thunk
https://redux.js.org/advanced/async-actions
At the end of the example, it shows how the redux-thunk can be used to initialize your store with an async API call.

You don't need default state globally because reducers already have a default state parameter on redux
For example
const someReducer = (defaultState = {foo: 'bar'}, action) => {
switch(action.parameter)
{
...
default return defaultState
}
}
Your state
{
someReducer: { foo: 'bar' }
}

Related

React app state patterns: is wrong to mix it with React Context API, and why if yes?

Why? One could have multiple components that needs to access a slice of the global app state (here provided by the hook useGlobalStore), for example:
// Component 1
export cost NavbarUserInfo = () => {
// Retrieve the current auth state
const auth = useGlobalStore(state => state.auth);
return (<p>{auth.username}</p>);
}
// Component 2
export cost UserDetails = () => {
// Retrieve the current auth state AGAIN!
const auth = useGlobalStore(state => state.auth);
return ({ /* show user info */ });
}
// Component 3 using the same state slice
Passing down props is not an option. The three (at least) problems of this approach:
Repeat access to useGlobalStore to retrieve the same state slice over and over
Coupling two components to a given state pattern library
Need to change many components for refactoring the state slice design
Is possible (or it's considered a bad practice) to mix the state pattern with context API? That is, inside my root component, expose a context with all my global state?
import { initialState, useGlobalStore} from './store.ts';
export cost App = () => {
const GlobalContext = React.createContext(initialState);
const state = useGlobalStore(state => state);
return (
<GlobalContext.Provider value={state}>
{/* now global state is available with the context API */}
</GlobalContext.Provider>
);
}

React: Accessing context from within a Redux store

I am a seasoned Angular developer who has now decided to learn React. To this end, I am rewriting one of my web apps in React. I have been reading about replacing Redux with the Context API and the useReducer() hook, but in my case I don't think this would be a good idea since my state is quite large and I don't yet have the React skill to mitigate any performance problems that might arise.
So I have decided to stick with Redux.
I now have the problem that I have a context that I need to make use of in my Redux store. My SocketProvider looks like this:
export const SocketProvider = ({
children,
endpoint
}) => {
const [socket, setSocket] = useState();
const [connected, setConnected] = useState(false);
useEffect(() => {
// set socket and init
}, [isAuthenticated]);
return (
<SocketContext.Provider
value={{
socket,
connected
}}
>
{children}
</SocketContext.Provider>
)
}
I would like to use the socket value from the SocketContext within my Redux store, like in this async action:
export const fetchComments = (issue: Issue): AppThunk => async dispatch => {
try {
dispatch(getCommentsStart())
const comments = socket.emit('getComment', issue.comments_url) // how can I get this socket?
dispatch(getCommentsSuccess({ issueId: issue.number, comments }))
} catch (err) {
dispatch(getCommentsFailure(err))
}
}
const App = () => {
return (
<SocketProvider endpoint={process.env.REACT_APP_API_SOCKET_ENDPOINT}>
<Provider store={store}>
{ /* my components */}
</Provider>
</SocketProvider>
);
}
I receive errors like this when I try to grab the socket context in my store code:
Unhandled Rejection (Error): Invalid hook call. Hooks can only be called inside of the body of a function component.
I can understand why this is happening, but I don't know the best way to solve it. I could keep all the thunks local to my components, or pass the context as a parameter in the async actions, but neither of these is really satisfactory.
Suggestions?

Is using Redux with Next.js an anti-pattern?

I'm building a Next.js app and it currently is using Redux. As I am building it I am wondering if the use of Redux is really necessary and if its use is actually an anti-pattern. Here is my reasoning:
In order to properly initialize the Redux Store in Next.js, you must create a custom App component with a getInitialProps method. By doing this you are disabling the Automatic Static Optimization that Next.js provides.
By contrast, if I were to include Redux on the client-side, only after the App has mounted, then the Redux store will reset after every server-side navigation. For instance, I have a Next.js app that initializes the Redux store on the client-side, but when routing to a dynamic route such as pages/projects/[id], the page is server-side rendered, and I have to re-fetch any information that was in the store.
My questions are:
What are the benefits of a Redux store in this circumstance?
Should I initialize the store in the root App component and forego the Automatic Static Optimization?
Is there a better way to do to manage state in Next.js 9.3 with getStaticProps and the other data fetching methods
Am I missing something?
If you have a custom App with getInitialProps then the Automatic
Static Optimization that Next.js provides will be disabled for all
pages.
True, if you follow this approach.
Is there a better way ?
Yes, you can create a Redux Provider as a wrapper and wrap the component you need, the redux context will be automatically initialized and provided within that component.
Example:
const IndexPage = () => {
// Implementation
const dispatch = useDispatch()
// ...
// ...
return <Something />;
}
IndexPage.getInitialProps = ({ reduxStore }) => {
// Implementation
const { dispatch } = reduxStore;
// ...
// ...
}
export default withRedux(IndexPage)
You have now the possibility to use Redux only for the pages which need state management without disabling the optimization for the entire App.
Answering you question "Is using Redux with Next.js an anti-pattern?"
No, but it needs to be used properly.
More info on how is done here: https://github.com/vercel/next.js/tree/canary/examples/with-redux
I hope this helps
we use Redux mainly for 2 reasons.
1- pass data between components.
if you do not use redux, then you need to do prop drilling. To decide if user logged in or not, we fetch the data and then store it in redux store and then Header components connects to the store and gets the authentication info. If you are not using redux, then you need to fetch the user in each page and then pass it to the Header component.
Next.js pre-renders every page. This means that Next.js generates HTML for each page in advance, instead of having it all done by client-side JavaScript. Pre-rendering can result in better performance and SEO. next-redux-wrapper package allows you to use the redux with automatic-static-optimization. If you click on the link, there is a note saying: "Next.js provides generic getInitialProps when using class MyApp extends App which will be picked up by wrapper, so you must not extend App as you'll be opted out of Automatic Static Optimization:". I set up this package for my project and it is easy to setup.
But downside of using redux, it is not caching. You store the data and then you refetch it periodically to make sure it is up to date. and this is an extra expensive work. To achieve caching in redux, we use reselect library. This means extra dependency for your project on top of redux and will make you write more code.
There is a nice package swr which is created by next.js. Stale-While-Revalidate. it first returns the data from cache(stale), then sends the fetch request, and finally comes with the updated data again. I choose the use this in each page.
import useSWR from "swr";
export const useGetUser = () => {
// fetcher can be any asynchronous function which returns the data. useSwr will pass "/api/v1/me" to fetcher
const { data, error, ...rest } = useSWR("/api/v1/me", fetcher);
// !data && !error if both true, loading:true, data=null=>!data=true, error=null => !error=true
return { data, error, loading: !data && !error, ...rest };
};
here is resuable fetcher
export const fetcher = (url: string) =>
fetch(url).then(
async (res: Response): Promise<any> => {
const result = await res.json();
if (res.status !== 200) {
return Promise.reject(result);
} else {
return result;
}
}
);
2- Making api requests.
I set up redux store for my project and it was conflicting with the text-editor that I set up. Redux was somehow blocking the editor and i could not populate the store with the text that i wrote on the editor. So I used reusable hooks for fetching api. it looks intimating in the beginning but if you analyze it, it will make sense.
export function useApiHandler(apiCall) {
// fetching might have one those 3 states. you get error, you fetch the data, and you start with the loading state
const [reqState, setReqState] = useState({
error:null,
data:null,
loading:true, // initially we are loading
});
const handler = async (...data) => {
setReqState({ error: null, data: null, loading: true });
try {
// apiCall is a separate function to fetch the data
const res = await apiCall(...data);
setReqState({ error: null, data: res.data, loading: false });
alert(res.data);// just to check it
return res.data;
} catch (e) {
// short circuting in or. if first expression is true, we dont evaluate the second.
// short circuting in and. if first expression is true, result is the second expression
const message =
(e.response && e.response.data) || "Ooops, something went wrong...";
setReqState({ error: message, data: null, loading: false });
return Promise.reject(message);
}
};
return [handler, { ...reqState }];
}
A simple apiCall function
const createBlog = (data) => axios.post("/api/v1/blogs", data);
and then this is how we use it :
export const useCreateBlog = () => useApiHandler(createBlog);
Setting redux is easy since it is easy people are not worried about the performance of their app, they just set it up. In my opinion, if you have a large app you need to set up redux or if you are familiar with graphql you can use Apollo. Here is a good article to get an idea about using apollo as state management. apollo as state management. I built a large ecommerce website and I used redux, my in my new app, since it is relatively small I do not use next js and make it more complicated.
Redux Toolkit Query
I think redux toolkit query (RTK query) is the biggest improvement in the redux ecosystem. It is actually built on top of redux-toolkit library. redux-toolkit helped us to write our redux code much simpler and update the state easier by using immer.js behind the scene.
With "RTK Query" we can handle data fetching and state management together. All the data fetching is combined under one API and we can cache the data, invalidate the cache or refetch the query. It is actually doing what the combination of swr and context Api is doing. state management with swr and context api
If you are using Redux, you do not need to have getInitialProps on _app.js.
You can use next-redux-wrapper, and just wrap _app.js export with it.
Store example, with next-redux-wrapper and thunk:
import { createStore, applyMiddleware } from 'redux';
import { createWrapper } from 'next-redux-wrapper';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunkMiddleware from 'redux-thunk';
import rootReducer from './rootReducer';
const bindMiddleware = middleware => {
return composeWithDevTools(applyMiddleware(...middleware));
};
const initStore = (initialState = {}) => {
return createStore(rootReducer, initialState, bindMiddleware([thunkMiddleware]));
};
export const wrapper = createWrapper(initStore, { debug: true });
Then inside your _app.js, you are exporting it as functional component with
const App = ({ Component, pageProps }) => {
return (
<Component {...pageProps} />
)
}
export default wrapper.withRedux(App);
Works like a charm. Just make sure you are doing hydration ssr -> csr.
Personally I think using the Redux is not a good idea at any case. It would be better to use, for example, useContext, or in case of extreme need for centralized storage look towards mobx. But in fact, there is a simple way to use Redux with SSR without using getInitialProps.
There is an important point here - the solution I gave is applicable only if you DO NOT use the rendering of literally every page on the server - when following the route after the first render, the application renders the next page on its own. In this solution it is assumed that the store will be initialized on the server side once and then the rendering result will be transferred to the client. If you need to render the page on the server absolutely every time you navigate the route and you need to save the state of store, then perhaps you really better still look towards the next-redux-wrapper.
So to initialize store at getServerSideProps first you will need to change your storage initialization file as follows (perhaps you will have other imports):
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension/developmentOnly';
let storeInstance: any;
export const makeStore = (initialState: {}) => {
storeInstance = createStore(
Reducers,
initialState,
composeWithDevTools(applyMiddleware(thunkMiddleware)) // Optional, but is a handy thing
);
return storeInstance;
};
// initializeStore used for pages that need access to store at getServerSideProps
export const initializeStore = (preloadedState) => {
let reInitiatedStore = storeInstance ?? makeStore(preloadedState)
// After navigating to a page with an initial Redux state, merge that state
// with the current state in the store, and create a new store
if (preloadedState && storeInstance) {
reInitiatedStore = makeStore({ ...storeInstance.getState(), ...preloadedState});
// Reset the current store
storeInstance = undefined;
}
// Keep in mind that in some cases this can cause strange
// and difficult to track errors, so whether or not
// to uncomment next lines depends on the architecture of your application.
// if (typeof(window) === 'undefined') {
// return reInitiatedStore; // For SSG and SSR always create a new store
// }
// Create the store once in the client
if (!storeInstance) {
storeInstance = reInitiatedStore;
}
return reInitiatedStore;
}
After that, in the page, where you need store on server side in the getServerSideProps, you can simple use initializeStore:
import { initializeStore } from '#Redux';
// Compnent code here...
export const getServerSideProps(context: any) {
const reduxStore = initializeStore();
// reduxStore = {
// dispatch: [Function (anonymous)],
// subscribe: [Function: subscribe],
// getState: [Function: getState],
// }
// Doing something with the storage...
const initialReduxState = storeInstance.getState(); // and get it state
return { props: { initialReduxState, ...someProps } };
}
Also don't forget that if you need to access the store in your _app.js, you must define store as:
const store = initializeStore(pageProps.initialReduxState);
Next.js is just a framework on top of React which simplifies Server Side Rendering setup, but it is still React. And React/Redux combo is very popular and still often used, also by me, so the answer is - it is not necessary, but totally possible! The bigger the app and the more you like functional programming, the better chance Redux will be a good option!

Dispatching an action from an asynch call in Redux

I am converting my project from flux to redux.
The root component is very straight forward redux :
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from 'react-redux'
import AppReducer from './AppReducer'
import { createStore } from 'redux'
const store = createStore(AppReducer)
ReactDOM.render(
<Provider store={store}>
//rest of application goes here
</Provider>,
app);
Using mapToProps, mapToDispatch I can deal with behaviour at the compoment level just fine.
Now I am coming to server responses, ie asynch calls.
In my flux implementation I have a file a call 'Server' I have my AJAX call and a promise is returned which then calls AppDispatcher.dispatch :
function handleResponse(endpoint) {
return function (err, res) {
if (res && res.ok) {
receiveData(endpoint, res);
}
};
}
function receiveData(endpoint, responseData) {
AppDispatcher.dispatch(
{
type: ActionTypes.SERVER_RESPONSE,
endpoint: endpoint,
state: "ReponseIsOK",
payload: responseData.body
}
);
dispatch(endpoint, "CommunicationState.RESPONSE_OK", responseData.body);
}
How should I convert this into redux?
I need to somehow get to the dispatcher, and I was hoping something like this would work :
function receiveData(endpoint, responseData) {
let store = Provider.getStore();
store.dispatch(
{
type: ActionTypes.SERVER_RESPONSE,
endpoint: endpoint,
state: "ReponseIsOK",
payload: responseData.body
}
);
}
I have tried provider.store, provider.context.store and they also seem to not work.
I really just need access to the store so that I can fire events, but not sure how to access it.
I'm pretty sure this is a situation to use Redux-Thunk, which allows you to dispatch actions asynchronously.
Here is the explanation from the docs:
Redux Thunk middleware allows you to write action creators that return a function instead of an action. The thunk can be used to delay the dispatch of an action, or to dispatch only if a certain condition is met. The inner function receives the store methods dispatch and getState as parameters.
I think you would first need to modify your creation of the store and pass in redux-thunk as a middleware and then you can modify your receiveData function to be something like this:
function receiveData(endpoint, responseData) {
return (dispatch, getState) => {
// DISPATCH YOUR ACTION WITH RESPONSE DATA
// AND ACCESS THE REDUX STORE
}
}
You just need to save a reference to the store when you create it with configureStore. You could just set up a getter if you'd like to access this from outside.
Pseudo code:
var store = configureStore()
// ...
export function getStore() {
return store
}
// ...
<Provider store={ store } />

Accessing redux state when dispatch called in NextJS getInitialProps

I am using Next.js and would like to prefetch an API item using redux. Here's some code that almost works:
class Thing extends Component {
static getInitialProps ({ store }) {
store.dispatch(fetchProduct())
}
}
const mapStateToProps = (state, ownProps) => {
return {
product: getProduct()
}
}
I am running into the issue that the component renders before getProduct has info. How can I shape things so that I don't render until dispatch has fetched the item? I am using redux-api-middleware in case that matters. I can check for the existence/validity of 'product' in render but that sort of defeats the purpose of getInitialProps. Is there a way to achieve the equivalent of an async/await fetch with redux?
Update Ok this might not work. redux-api-middlewar seems to return an empty object as an action on SSR - so its not a timing issue.
Ok. Reading up some GH issues I found that using redux-api-middleware in SSR is somewhat problematic. I believe that part of the issue is that it doesn't appear to return a promise from dispatch that can be used with async/await. Additionally one should ideally use getInitialProps for its intended purpose so let's return the props there directly.
I've switched to redux-axios-middleware and it works like a charm.
static async getInitialProps ({ store }) {
await store.dispatch(fetchProducts())
return { products: getProducts(store.getState()) }
}
If you are using 'next-redux-wrapper' you must accept initialState argument in makeStore and pass it down to createStore so that after dispatching the store the new state will be accesible in the server and client side in the object methods.
const makestore = initialState => {
return createStore(reducer, initialState);
};

Resources