Where to put API calls in React/Redux architecture? - reactjs

I am trying to retrieve some data from an API and pass it into my application. Being new to React/Redux however, I am wondering where to make these calls from and how to pass it into my application? I have the standard folder structure (components, reducers, containers, etc.) but I'm not sure where to place my API calls now.

The easiest way to get started with this is to just add it into your actions, using a function called a thunk along with redux-thunk. All you need to do is add thunk to your store:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';
const store = createStore(
rootReducer,
applyMiddleware(thunk)
);
Then create a function in your actions that calls the api:
export const getData() {
(dispatch) => {
return fetch('/api/data')
.then(response => response.json())
.then(json => dispatch(resolvedGetData(json)))
}
}
export const resolvedGetData(data) {
return {
type: 'RESOLVED_GET_DATA',
data
}
}

The "Teach a man to fish answer."
This depends on the type of call and the situation.
Generally for simple "gets" this can easily be done by placing them
into your action creators as Nader Dabit has shown.
There are many side effect management libraries which would opt for
you to place them in their blocks(redux-sagas, axios calls, redux-thunk)
I use redux-sagas for now. At least until we decide yay or nay on async/await which is possibly coming in a newer version of JS.
This may be the most important part!
Just be sure to take into account the general "conventions" that are used with your particular set of tools usually found in the documentation, and be sure to google "best practices" in the future for things like this. This will help others new to your project to get their bearings and just jump in without ramping up learning your new customized version.

Behavior such as AJAX calls are referred to as "side effects", and generally live in either your components, "thunk" action creators, or other similar Redux side effects addons such as "sagas".
Please see this answer in the Redux FAQ for more details:

Related

Where to put useDispatch and bindActionCreators in a project?

My question is this, where do I put the methods mentioned above? Because in each component in which I want to use the Redux store, I need to basically repeat the mantra of,
import { useSelector, useDispatch } from "react-redux";
import { bindActionCreators } from "redux";
import * as actions from "../../redux/actions";
and then, for example,
const dispatch = useDispatch();
const { fetchStats } = bindActionCreators(actions, dispatch);
I've seen that some people make like a containers folder?
Also, what's Your file structure? Where do you guys put the actions? How do you export them? All in one file or what? In bigger projects it's not really efficient.
As always, thanks in advance!
The answer is, don't.
bindActionCreators was really only ever used internally by the React-Redux connect function and not generally used by app code.
Today, with the React-Redux hooks API, we recommend just manually writing:
const dispatch = useDispatch();
const handleClick = () => dispatch(someActionCreator())
That way it's more explicit what's actually going on in the component.
Yes, that does require importing the hooks into the component. That's both intentional and necessary to use them, like any other function.
We recommend against trying to separate out "container components", especially if you're using the hooks API.
As for logic, you should be using a "feature folder" file structure using Redux Toolkit to create one "slice" file per feature containing the logic.

How to structure data reducers in Redux-toolkit with multiple apis

I'm beginner developer developing an application with react using typescript and redux-toolkit (mainly with createSlice, createAsyncThunk).
I'm still confused on how to structure/manage data with multiple data apis
for example :
there are 3 endpoints from domain.
/products,
/photos,
/info
I've created each slice according to the endpoint
productSlice,
PhotoSlice,
infoSlice
each slice has reducer, createAsyncThunk, extraReducers,
then combined all reducers like below
const rootReducer = combineReducers({
product: productSlice.reducer,
photo: photoSlice.reducer,
info: infoSlice.reducer
});
each slice has reducer, createAsyncThunk, extraReducers,
then combined all reducers like below
Actually I've spent a lot of time to find a right way of structuring data.
then I've come across this code that other developer did like below
const rootReducer = combineReducers({
getProduct: getProductSlice.reducer,
createProduct: createProductSlice.reducer,
updateProduct: updateProductSlice.reducer,
deleteProduct: deleteProductSlice.reducer,
getPhotoDetails: getPhotoDetailsSlice.reducer,
updatePhotoDetails: updatePhotoDetailsSlice.reducer,
deletePhotoDetails: deletePhotoDetailsSlice.reducer,
getInfo: getInfoSlice.reducer,
createInfo: createInfoSlice.reducer,
updateInfo: updateInfoSlice.reducer,
deleteInfo: deleteInfoSlice.reducer,
});
I'm actually not sure (neither is he )
which way is better of structuring Reducers and data?
or if you show me other way or examples of structuring reducer and data,
It would be really nice.
Since the last version, Redux Toolkit ships with RTK Query, which would replace all those reducers with one auto-generated one. That might help you reduce your code quite a bit, give it a look.

how to convert my code to a sharable component

I've a component that uses Redux, Redux-Sage and I want to convert it to a sharable library.
How can I structure my code and what to export to make it easier to share?
Ideally we should create stateless component for shareable library. But your components are appearing lots of dependency or state management constraint like Redux, Redux-saga etc. My suggestion, you should avoid this.
If you really want to do this then please create some initialization library function and enforce the calling of this function. The function should check for all pre-requiste before showing your component. But that will be challenging in term of coding.
Usually I would just export the different constituents of the "library" like this, and offer a guide how to integrate them into the projects redux... Long story short it would be... messy. But I'd do it like this:
const defaultState = {};
export libReducer = ( /* ... */ ) {
//...
}
export libSaga = ( /* ... */ ) {
//...
}
export LibComponent = ()=> {
// ...
}
The problematic parts are redux and redux-saga though.
Because it will be required to integrate the reducer using combineReducers, and the saga inside the applyMiddleware part during redux store integration.
import {
createStore,
applyMiddleware,
combineReducers
} from 'redux';
import createSagaMiddleware from 'redux-saga';
// explain that this needs to be imported
import { libSaga, libReducer } from 'your-lib';
const sagaMiddleware = createSagaMiddleware()
// explain how to:
// create a rootSaga to use multiple sagas
// the equivalent of a combineReducers
// but only IF the target project uses
// saga at applyMiddleware. Else it would
// be sufficient to:
// sagaMiddleware.run(libSaga)
// later...
function* rootSaga () {
yield [
fork(libSaga),
fork(otherSagaThatProjectUses)
];
}
// explain howto use combineReducers
// and applyMiddleware to add your reducer
// and enable saga... again depending upon
// the project using redux/saga or not...
const store = createStore(
combineReducers{
// ...
fixedNamespaceForYourLib: libReducer
},
applyMiddleware(sagaMiddleware)
)
sagaMiddleware.run(rootSaga);
The best practice is encapsulating your redux provider to use in all project (even non-redux based projects):
<Provider store={store}>
<YourLibrary />
</Provider >
Frankly speaking, It is a really wide-open question. It'll significantly help to answer if you provide a code example or narrow down the context.
In general, it is preferable to avoid any other than React dependencies in a component as it would unlock it for projects without redux + redux saga stack. Often, state handling can be moved from Redux to the local component state, side-effects can be abstracted out. So it's boiling down the pure "view" component which gives enough flexibility for users to bind to custom business logic.
Here are a few general questions to consider regarding creating reusable lib:
What is the component actually supposed to do?
Is the component interface intuitive and unambiguous?
Can it be broken down into smaller and simpler components under the hood?
Does it have a good test coverage? Getting to a good test coverage will encourage having a well-structured and written set of components in a library.

Should I run all saga when launch app?

I've just started to learn and use redux-saga, at first I thought it worked this way: first you make action which is connected to saga, then saga detects action,calls api which returns data and saga returns this data to reducer. I built 2 testing saga files and one root saga (I wanted to make rootSaga, like combineReducer), so this is my rootSaga :
import * as SpreadsheetSagas from "../pages/spreadsheet/containers/spreadsheet/spreadsheet.saga";
import * as SpreadsheetFilterSagas from "../pages/spreadsheet/containers/spreadsheet_filter/spreadsheet_filter.saga";
export default function* rootSaga() {
yield all([
...Object.values(SpreadsheetSagas),
...Object.values(SpreadsheetFilterSagas)
].map(fork))
}
and this is one of my saga function:
export function* getData() {
const response = yield call(ApiService.get);
const payload = response ? response.data : {};
//send returned object back to reducer as payload:
yield put({ type: 'GET_MOCK_DATA', payload});
}
and my store folder looks like:
const middleWare = [];
// Setup Redux-Saga.
const sagaMiddleware = createSagaMiddleware();
middleWare.push(sagaMiddleware);
const store = createStore(rootReducer, {},
compose(applyMiddleware(...middleWare)));
// Initiate the root saga.
sagaMiddleware.run(rootSaga);
So, when I run my app, it calls every saga functions - and this is correct way to implement sagas :? or I should have several rootSaga files which will be depend on current page, and then when I open page, run appropriate rootSaga? I hope I explained everything correctly :/ please give me any suggestions. thank you.
I think generally you just have one tree of sagas that relate to your entire app, not multiple root sagas for different pages/routes. You just attach the rootSaga once, as middleware.
So yes, when your app starts the entire tree of sagas starts and beings watching actions. Sagas interact globally with your store, so it makes sense for them to start globally with your app.
Only start the sagas that watch for actions (using take, takeEvery etc). Other sagas that make side effects, or call apis, shouldn't be invoked when your app starts.
Creating a tree of sagas similar to reducers and combineReducer is the right way to go too.

how to reduce redux boilerplate

I'm new to redux and I find that every little thing x turns into x_success and x_failure, usually when fetching data or trying to create a new entity, and that means more action creators, and more handling in the reducers. What's the recommended approach here? thanks.
Recommended approach is x_success, x_failure etc. But this is for async operations only. Let's see why :
Async operations in your SPA are the operations you want to know
when operation started,
when you got response back
Type of response , success or failure
So that you will have seperate actions creator functions which return objects and one async action creator function which can return function instead of object and calls other action creators from its body.
For the reasons above you should have seperate action creators, one async action creator and of course for every action creator you should have a constant in your reducer.
Assuming you are writing your constants, actions and reducers in seperate folders, this can be a nightmare. If this is the case, you should take a look at here duck modular redux .
Duck modular redux is something you should definetely implement to reduce the boilerplate. Other things like seperate action creators, seperate constants, seperately checking on reducers for constants are required. There is nothing to feel bad about it.
I started to work with Redux almost since the beginning of its story, 2 years ago. While it is a great thing and it allows to eliminate a whole type of bugs and makes all business logic more explicit, it has a lot of concepts. For each entity, you need to create:
constants (for each state -- in case of async function, there are three of them, for start, failure and success)
action (function)
reducer (update state, sometimes a nested update)
It was mentioned that you can something like redux-ducks, which is an approach to organize your code, but you will not write less of the code. So, I strongly believe people should consider writing their own wrapper around redux more seriously.
I wrote a library redux-tiles, which deals exactly with this kind of situations -- it takes the burden of creating constants, update the state (so you don't need to write a reducer by yourself), and do nested updates if needed. So, code for async request, will look something like this:
import { createTile } from 'redux-tiles';
const apiRequest = createTile({
type: ['api', 'request'],
fn: ({ api, params }) => api.get('/api/items', params),
});
It also allows you to combine other actions easier, because in the fn you have access to dispatch and actions. You can take a look at more examples here.
I started using Redux and I loved the concept, but I got really annoyed by the big amount of boilerplate. I ended up creating actionware lib. Basically here's what you have with actionware:
no more action creators and action types, just actions (simple functions) and reducers
actions dispatch their result automatically
error status for every
action with no extra code busy status for every async action (yep, no extra code!)
cancellable actions
There are a number of ways you can reduce the bolierplate. Here's an excellent resource from official repo:
https://github.com/reactjs/redux/blob/master/docs/recipes/ReducingBoilerplate.md
Besides, there are various community-driven libraries that help you in reducing redux boilerplate and in organising your store better. Redux Box might interest you in this regard:
https://github.com/anish000kumar/redux-box
A huge Boilerplate is one of the drawbacks of the flux architecture in general. You might want to checkout redux-fluent, it has been designed with that in mind.
import { createAction, createReducer, ofType } from 'redux-fluent';
const addTodo = createAction('todos | add');
export const todos = createReducer('todos')
.actions(
ofType(actions.addTodo).map(
(state, { payload }) => state.concat(payload),
),
)
.default(() => []);
To reduce Redux boilerplate you can generate action creators and reducer by describing one function with redux-blaze:
import { buildReducer } from "redux-blaze";
export const { actionCreators, reducer: filtersReducer } = buildReducer(initialState, {
setMySearch: ({ search }) /* <- payload */ => state => ({ ...state, search }),
setCategory: ({ category }) => state => ({ ...state, category }),
setSort: ({ sort }) => state => ({ ...state, sort }),
}, {
prefix: 'MY_FILTER',
});
...
// Just add auto generated reducer to root reducer:
export const rootReducer = combineReducers({
filters: filtersReducer
})
...
// dispatch an action:
dispatch(actionCreators.setCategory({category: 'my category'}))
You can also find TypeScript examples here:
https://github.com/spiderpoul/redux-blaze

Resources