How to cache API response and use it later in react & redux? - reactjs

In my React based application, there is a rest API call which fetches all the data in one shot which is needed for the whole page. The response has data which can be used in a population of dropdowns as well. But I am not sure how this can be achieved. I am currently making a new request whenever the drop-down value is selected. Please do suggest me how to do implement it efficiently without making multiple unwanted rest calls.

you can cache in HDD or RAM.
HDD = e.g. localStorage
RAM = application state, e.g. redux store.
For localStorage you can use my little plugin for this:
https://www.npmjs.com/package/localstorage-ttl
In app state (RAM) - fire action to fetch data, use redux-thunk, redux-saga or similar to make a call and with reducer save data in the store. Retrieve data from store.
https://github.com/gaearon/redux-thunk
https://github.com/redux-saga/redux-saga

You could choose a redux-cached-api-middleware (disclaimer: I'm the author of this library), that's especially built for such cases.
Here's an example that might fit your case:
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import api from 'redux-cached-api-middleware';
import Dropdown from './Dropdown';
import Error from './Error';
class ExampleApp extends React.Component {
componentDidMount() {
this.props.fetchData();
}
render() {
const { result } = this.props;
if (!result) return null;
if (result.fetching) return <div>Loading...</div>;
if (result.error) return <Error data={result.errorPayload} />;
if (result.successPayload) return <Dropdown items={result.successPayload} />;
return <div>No items</div>;
}
}
ExampleApp.propTypes = {
fetchData: PropTypes.func.isRequired,
result: PropTypes.shape({}),
};
const CACHE_KEY = 'GET/items';
const enhance = connect(
state => ({
result: api.selectors.getResult(state, CACHE_KEY),
}),
dispatch => ({
fetchData() {
return dispatch(
api.actions.invoke({
method: 'GET',
headers: { Accept: 'application/json' },
endpoint: 'https://my-api.com/items/',
cache: {
key: CACHE_KEY,
strategy: api.cache
.get(api.constants.SIMPLE_SUCCESS)
.buildStrategy(),
},
})
);
},
})
);
export default enhance(ExampleApp);
It will fetch from API only once, and whenever you call fetchData it will return data from the cache, even if the call is from another component.
The setup for this library is as follows:
import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { apiMiddleware } from 'redux-api-middleware';
import api from 'redux-cached-api-middleware';
import reducers from './reducers';
const store = createStore(
combineReducers({
...reducers,
[api.constants.NAME]: api.reducer,
}),
applyMiddleware(thunk, apiMiddleware)
);
With this approach after user re-loads the page all redux state will be lost, if you'd like to save that state you could choose a redux-persist library to sync the state to localStorage and use would probably use different caching strategy for redux-cached-api-middleware.

As I guessed you are using redux for state management. And You want to cache api calls for efficient controll state changes for avoiding rerendering Components, unnecessarry API calls and being friendly for Mobile Users (with avoiding fetchig same data(not changed) multiple time). If so -> then you can use ready solution for it.
redux-cache. please visit below link for additional information and investigate caching mechanizm briefly -> when you need cache data and when you need evict cache.
https://github.com/JumboInteractiveLimited/redux-cache
How do I implement caching in Redux?

If you have control over API you can tweak it so responses will be cached.
https://web.dev/http-cache/

Related

How to configureStore Mutiple middleware in RTK query

redux-toolkit given original doc--
import { configureStore } from '#reduxjs/toolkit'
// Or from '#reduxjs/toolkit/query/react'
import { setupListeners } from '#reduxjs/toolkit/query'
import { pokemonApi } from './services/pokemon'
export const store = configureStore({
reducer: {
// Add the generated reducer as a specific top-level slice
[pokemonApi.reducerPath]: pokemonApi.reducer,
},
// Adding the api middleware enables caching, invalidation, polling,
// and other useful features of `rtk-query`.
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(pokemonApi.middleware),
})
// optional, but required for refetchOnFocus/refetchOnReconnect behaviors
// see `setupListeners` docs - takes an optional callback as the 2nd arg for customization
setupListeners(store.dispatch)
my code
import { configureStore } from '#reduxjs/toolkit'
import newletterReducer from '../slicers/newletter/newsletterSlicer'
import { setupListeners } from '#reduxjs/toolkit/query'
import { getmoviesdetails } from '../slicers/service/getmoviedetails'
import { authService } from '../slicers/service/auth/authService'
export const store = configureStore({
reducer: {
newsletter: newletterReducer,
[getmoviesdetails.reducerPath]: getmoviesdetails.reducer,
[authService.reducerPath]: authService.reducer,
},
middleware:
(getdefaultMiddleware) =>
{return getdefaultMiddleware().concat(getmoviesdetails.middleware)
,getdefaultMiddleware().concat(authService.middleware)}
})
setupListeners(store.dispatch)
they given error
buildInitiate.ts:248 Warning: Middleware for RTK-Query API at reducerPath "getmoviedetails" has not been added to the store.
Features like automatic cache collection, automatic refetching etc. will not be available.
You can do it like this:
middleware:
(getdefaultMiddleware) =>
getdefaultMiddleware()
.concat([
getmoviesdetails.middleware,
authService.middleware
])
Basically you can pass all your middleware in a array.
getdefaultMiddleware().concat(getmoviesdetails.middleware).concat(authService.middleware)
But you really should only have one api unless those are completely independent apis that will never have any overlapping data ever.
Quoting from the docs:
Typically, you should only have one API slice per base URL that your application needs to communicate with. For example, if your site fetches data from both /api/posts and /api/users, you would have a single API slice with /api/ as the base URL, and separate endpoint definitions for posts and users. This allows you to effectively take advantage of automated re-fetching by defining tag relationships across endpoints.
For maintainability purposes, you may wish to split up endpoint definitions across multiple files, while still maintaining a single API slice which includes all of these endpoints. See code splitting for how you can use the injectEndpoints property to inject API endpoints from other files into a single API slice definition.

Every time when my redux state is updating my whole page is loading. Not exactly loading resources back from server but

Every time when my redux state is updating my whole page is loading. Not exactly loading resources back from server but refreshing the components inside the page?
The following is my store code.
P.S : I am using multiple stores and combining them using combine reducers.
import { createStore, combineReducers, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import logger from "redux-logger";
import { User } from "./User";
import { Restaraunt } from "./Restaraunt";
import { Dish } from "./Dishes";
import { Cart } from "./Cart";
import { createForms } from "react-redux-form";
import { InitialFeedback, RegisterUserDetails, RegisterRestarauntDetails, addDishDetails } from "./forms";
export const storeConfig = () => {
const store = createStore(
combineReducers({
user: User,
restaraunts: Restaraunt,
dishes: Dish,
cart: Cart,
...createForms({ feedback: InitialFeedback }),
...createForms({ registeruser: RegisterUserDetails }),
...createForms({ registerres: RegisterRestarauntDetails }),
...createForms({ addDish: addDishDetails })
}),
applyMiddleware(thunk, logger)
);
return store;
};
I So I am using the cart that I mentioned a particular page. So when ever my cart is updating then my whole page is loading again.
This a bit too broad to be honest.
You should take a look at every component in your react app and make sure that they only re-render when needed.
Things you can do:
You can use shouldComponentUpdate lifecycle if you have class-based components that should update only when few of the props change.
You can use React.memo to memoize a component and prevent unnecessary rerender.
useCallback hook on functions for memorizing them.

Storing to Redux on page load

I am quite new to React and Redux framework. What I am trying to do is reading a propery say zipCode from an API call on componentDidMount. And now I have to save it to redux store.
I am really confused reducers and actions etc. And not sure how and where to configure those.
Help much appreciated.
Redux can seem confusing at first glance, as I have experienced. But like everything, you will start to understand it quickly, especially if you practise making your own redux application(with the support of this community) and succeed by trial and error.
What you must understand about Redux are the following (I wont go into detail, ill keep it simple and basic, as well as relate it to your question):
1. Actions: These are as the name suggests, actions fired by the view/component. E.g. when you request to fetch the zipcode, this will be dispatched to the action.
componentDidMount(){
this.props.fetchZipCode(85001);
}
action.js
export const fetchZipCode= (zip) => ({
type: 'FETCH_ZIPCODE',
zip,
});
2. Reducers: These handle the actions request. As described in Redux docs 'The reducer is a pure function that takes the previous state and an action, and returns the next state'
reducer.js
const ZipcodeInfo = (state = 0, action) => {
switch (action.type) {
case 'REQUEST_ZIPCODE':
return {
...state
};
case 'RECEIVE_ZIPCODE':
return {
...state
zip: action.data.zipcode
};
case 'RECEIVE_ZIPCODE_FAILED':
return {
...state
}
default:
return state;
}
};
3. Store: This is what brings everything together. It holds application state, allows access to the store.
index.js (note: this is the root file of the application, so all states can be passed from the store to all components in your app)
import App from './App';
import { Provider } from 'react-redux';
import { createStore } from 'redux'
import ZipcodeInfo from './reducers'
const store = createStore(ZipcodeInfo)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>, document.getElementById('root'));
registerServiceWorker();
4. Middleware: Now the middleware is not necessary, but if you are making async calls you will need to use a middleware to pass information to the API etc... There a many middlewars to use for redux.
Redux-saga middleware example
import {call, put, takeEvery, fork} from 'redux-saga/effects';
import * as service from '../Services/api';
import * as actions from '../actions/zipcode';
//The worker: will handle the action and terminate
function* fetchZipcode(action){
try{
yield put(actions.requestZipcode());
const [zipcode] = yield [call(service.getZipcodeAPI, action.zip)];
yield put(actions.receiveZipcode(zipcode));
} catch(e){
yield put(actions.receiveZipcodeFailed(e));
}
}
//The watcher: will watch for dispatched actions and fork a worker on every action
function* watchfetchZipcode(){
yield takeEvery('FETCH_ZIPCODE', fetchZipcode);
}
export default function* zipcodeSaga(){
yield fork(watchfetchZipcode);
}
I personally prefer Redux-Saga as a middleware, but it may be confusing as it uses generators(from es6) and more unknown terms.
Watch the below tutorial. It's a simple example to understand.
https://www.youtube.com/watch?v=OSSpVLpuVWA

Which is the best way to make API call in react web application should i use react fetch or is there available any other best tool

Hi all i wanna make API/Server call for retrieve the data into my react web application how to do that . and which is the best tool for make API call should i use AJAX or react fetch
There is no specific way in react, you free to choose.
I would recommend Fetch api, it works beautifully. So if you are familiar with ES6 promises etc then go for it.
Your other options are XMLHttpRequest, axios
React only deals with the view layer, so ideally it is not supposed to make api calls.
Hook react up with other patterns like flux or redux etc and see the magic.
There are couple of other ways to make api calls:
XMLHttpRequest
Fetch
Superagent
I prefer Fetch as it is designed to be more extensible and efficient
Also, you can see the comparison table below for more options:
https://npmcompare.com/compare/axios,isomorphic-fetch,mappersmith,superagent-bluebird-promise,then-request.
Updated on 13 Feb 17:
#Ramusesan : Below is my working code.
dataAction.js
import * as types from '../constants/ActionTypes';
export function loadData(data) {
return {type: types.LOAD_DATA, data};
}
export function getDataById(id) {
return {type: types.GET_DATA_BY_ID, id};
}
export function loadCompleteData() {
return function(dispatch, getState) {
return fetch("localhost:9090/data/", {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then ((response) => {
dispatch(loadProfiles(response));
})
.catch(error => console.log(error));
}
}
configure-store.js
import {createStore, applyMiddleware} from 'redux';
import thunkMiddleware from 'redux-thunk';
import rootReducer from '../reducers/index';
const createStoreWithMiddleware = applyMiddleware(thunkMiddleware)(createStore);
export default function configureStore(initialState) {
const store = createStoreWithMiddleware(rootReducer, initialState);
return store;
}

React Redux Refresh issue

I am using react-redux and I am having a issue where I loose my redux state when the page is refreshed.
Now before I go further on that this is the scenario, which I may be messing up.
Question one: Can I connect to more than one layout?
I have a dashboard and a "app" layout. Both have separate layouts. I connect both in the same manner:
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as actionCreators from '../actions/actionCreators';
function mapStateToProps(state) {
return {
profile: state.profile,
child: state.child,
}
}
function mapDispachToProps(dispatch) {
return bindActionCreators(actionCreators, dispatch);
}
const LayoutApp = connect(mapStateToProps, mapDispachToProps) (DashboardLayout);
export default LayoutApp;
The dashboard connects just fine. I am able to hit reducers when i need to and update the store, however the dashboard links to the app for certain parts of data manipulation for you to play with. When it links I get the data in props as expected, however as soon as any page refreshes within the app layouts I loose the props being sent by maptoprops.
I have tried to combine into one master layout however that seems to have the same affect. I have also tried to save to state immediately when i first receive data but that seems to be lost as well upon refresh which makes me think it is resetting it.
Summary:
- DashboardLayout (connects to redux)
- AppLayout (connects to redux) however after a page refresh it looses props to the Applayout and needed data is gone.
Get to know redux-persist
https://github.com/rt2zz/redux-persist
You can install it using
npm i --save redux-persist
persistStore is the function that allows you to persist store.
import {persistStore} from 'redux-persist'
and autoRehydrate is the action that is performed whenever the state needs to be rehydrated
import {autoRehydrate} from 'redux-persist'
following is the structure that may be useful.
import {compose, applyMiddleware, createStore} from 'redux'
import {persistStore, autoRehydrate} from 'redux-persist'
// add `autoRehydrate` as an enhancer to your store (note: `autoRehydrate` is not a middleware)
const store = createStore(
reducer,
undefined,
compose(
applyMiddleware(...),
autoRehydrate()
)
)
// begin periodically persisting the store
persistStore(store)
and then for your reducer
import {REHYDRATE} from 'redux-persist/constants'
//...
case REHYDRATE:
var incoming = action.payload.myReducer
if (incoming) return {...state, ...incoming, specialKey:
processSpecial(incoming.specialKey)}
return state
following are the methods which can be used to work out of redux-persist
persistor.pause() - pauses redux persist
persistor.resume() - resumes redux persist
persistor.purge() - deletes all persisted data
persistor.rehydrate() - calls reducer to rehydrate store
types of storage redux persist allows
// sessionStorage
import { persistStore } from 'redux-persist'
import { asyncSessionStorage } from 'redux-persist/storages'
persistStore(store, {storage: asyncSessionStorage})
// react-native
import {AsyncStorage} from 'react-native'
persistStore(store, {storage: AsyncStorage})
// web with recommended localForage
import localForage from 'localforage'
persistStore(store, {storage: localForage})
it is the most basic use redux-persist hope it helps.
You can connect to as many components as you want to same store properties - that's not issue. Just to note here, connect is listening for changes so when action changes store it would cause all components listening to changed store parts to re-render (or at least do computation and then compare shadowdom).
Store is stored only when running application - after page close/resfresh it's cleared.
There is good thread about persistence of store (eg. to local storage): Where to write to localStorage in a Redux app?
As mentioned in the previous answers storing the state in local storage is one possible solution however some other possible solutions are
Make sure your links use the Push API ie <Link to={"route"}/> (react-router) this will ensure the store is not removed between route changes
Set your initial state when you create your store
const rootReducer = combineReducers({
todos: todos,
visibilityFilter: visibilityFilter
});
const initialState = {
todos: [{id:123, text:'hello', completed: false}]
};
const store = createStore(
rootReducer,
initialState
);
This will hydrate your reduce store when it is initialized
There are some other libs/frameworks/boilerplates that you could use that also implement server rendering with will render the HTML to the page on the initial render and then load React once the DOM has loaded some of this libs are
Next.js,Electroide,react-redux-universal-hot-example
Going down this route will add significant complexity to your app so you should weigh up the pros and cons before embarking down this route
I had this problem, so as somebody mentioned here, i used the localStorage.
In my reducer, i did like this:
const exampleKey = "_exampleKey"
const INITIAL_STATE = {
example: JSON.parse(localStorage.getItem(exampleKey))
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case 'SET_TRUE':
localStorage.setItem( exampleKey, JSON.stringify(true));
return { ...state, example: true };
case 'SET_FALSE':
localStorage.setItem( exampleKey, JSON.stringify(false));
return { ...state, example: false};
default:
return state
}
Pointing INITIAL_VALUES.example to localStorage's item example ensure us to keep the correct value when the page reloads.
Hope it helps somebody.

Resources