I'm trying to get some thoughts on what people would consider the best practices for how people organize network calls in their react+redux apps. I usually let my components make the calls, get data and then pass that into an action that will get reduced. Is this the best practice or is it better to separate networking out of my components and place that logic somewhere else in the app, maybe in the reducers?
The best place to make network calls is in your action creators. However, you're going to need some middleware to make that work best. Take a look at this promise-middleware (in fact, I'd suggest checking out that whole tutorial). If you use that middleware, you can have action creators that return a promise and also have three action types - one for the request, one to handle successful responses, and one to handle failed requests. Then you just listen for those 3 actions in your reducers.
So with that middleware, you could have an action creator like this:
function networkCall() {
return {
types: ['MAKE_REQUEST', 'REQUEST_SUCCESS', 'REQUEST_FAILURE'],
promise: () => {
return new Promise((resolve, reject) => {
$.ajax({
url: 'example.com/api'
type: 'GET'
});
})
}
}
}
Obviously you are free to build your own promise middleware, but that should set you in the right direction.
I think this is one thing that was done right in Angular. You have all your network calls neatly placed in your services. This can easily be done in Redux.
The docs rightly suggest that network calls are in your actions. I'd factor them out into a separate place, you can call it "services". There you would define all your constants such as your API server URL, authentication-related stuff, etc. This would be the only place that is aware of the implementation details of your network calls - which library you use (jQuery, axios, superagent, etc).
Your actions files would import function from those services and call them. If you decide later on to swap out your networking library, you wouldn't have to change your actions.
You could use an API middleware, either redux-api-middleware or something of your own (it isn't very hard to write one).
Then, for example, your action creators could return actions like
{type: 'API_GET', url: '/api/userList', nextType: 'USER_LIST'}
...that would be later handled by a middleware that would send the actual request and then dispatch a new action like:
{type: 'USER_LIST_FETCHED', status: 200, payload: [{id: 1, ...}, ...]}
{type: 'USER_LIST_FAILED', status: 404, payload: {message: '...'}}
I pretty much follow the pattern of the actions in the redux tutorials for Async Actions. It makes most sense to me to keep everything async in the actions -- away from both the components and the store/reducers.
I also use Redux Crud to standardize the actions related to network actions.
Related
How can we get the data from the saga directly into our component ?
or
Is this a pattern we should not follow and directly make a service/ api
call from the component using some service layer.
I have been following this issue on git on if this is possible there are too many permutations and combinations and i am a bit confused ..
I tried this small example by refering this
stackblitz.
In this case when i try and return this
function* helloSaga() {
return new Promise(function(resolve, reject) {
resolve('start of new Promise');
});
}
and access it like this
let response = dispatch(action('SHOW')).then(data => {
console.log(data); // i cannot get this to work say if this were a api response .
})
Nothing happens .
Is this pattern acceptable if yes then how can we make it work and what am i missing it
And if this pattern is an anti-pattern then making service calls from a layer like getData() should be enough from componentDidMount() .
A generator function doesn't really return a value (or the promise in your case) as you'd expect with any "regular" function. It returns a generator-object that you can pause, continue, cancel etc.
Check https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
The way to go with redux-saga in a web app would be:
Trigger an action like "SHOW" in your example
Have a saga take your action "SHOW"
The saga would then do the async API request
Once the API request is done, the saga dispatches another action (something like FETCH_SUCCEEDED
Handle the action in your reducer and store the response in the redux store.
Once it's stored in the redux store, you can select it from any react component you want to (use connect from redux for that)
Here's your stackblitz with a minimal example:
https://stackblitz.com/edit/react-redux-sagas-demo-app-4tpevb?file=index.js
For a production app, you'd want to wrap the API request in a try{..}catch(){..} and handle the error with a proper action like FETCH_FAILED and display an error message in your FE (if it effects the user).
I've been reading about the best way to integrate WebSockets into a React/Redux app, and I'm finding answers but with some sentence along the lines of "The best place for websocket implementation is usually middleware."
My question is WHY this is preferred? What is the benefit of doing this vs setting up the websocket/having listeners dispatch actions in the outer App-level React container (in componentWillMount for instance)?
This seems like it would be equivalent in terms of lasting throughout the lifecycle of the app, etc. What am I missing here?
There are couple of pros with placing such logic in middle-wares instead of actual components.
The main reasons in my opinion are:
Each connected component will instantiate a WebSocket or you will
need a global declaration of the connection which will be
independently from the store, in other words, not part of the redux
flow.
Middle-Wares have access to the store and are part of the redux
flow.
You also get access to the entire store, hence you can pass
forward more data then initially dispatched.
You decouple the components from the web-socket logic, hence you can
centralize your code and reuse your components.
All in all, there are no special reasons that are specific regarding using web-sockets with middle-wares, using middle-wares have great advantages in general.
Edit
As a followup to your comment
How would you suggest managing a case where you might want a
particular component to initialize a websocket connection, but want to
manage whether it's already connected, etc... would it be just as
simple as having a flag in the store that says it's connected or is
there a better approach?
As i said, I wouldn't initialize a web-socket connection within a component, rather I would do it in the entry point of my application. like index.js for example.
If your concern is to make sure you won't try to connect when there is already a connection, then a simple socketStart method that get invoked at the point when you create the store and initialize all your App data, you can pass it a callback that will do the rendering and store update through dispatch.
A simple example (keep in mind this is a pseudo code ):
Our Socket-start Method:
export function socketStart(store, callback) {
// this is only a pseudo code!
// register to server -> client events
_socketClient.someFunction = (data) => {
store.dispatch({ type: "Server_Invoked_Some_Function", data });
}
_socketClient.someFunction2 = (data) => {
store.dispatch({ type: "Server_Invoked_Some_Function2", data });
}
// connect to the server via the web-socket client API
_socketClient.connect(() => callback());
}
We can use it in our index.js file:
let store = createStore(
// your reducers here...
// ...
applyMiddleware(socketMiddleware) // our web socket middleware
)
// the callback will invoked only if the connection was successful
// the React-Dom's render function is our callback in this case
socketStart(store, () => { render(<App />, document.getElementById("root")); });
With middleware, you can easily unfold/relay messages between Redux and Web Socket. Also, you can use Redux middleware without React, that means you can write API using Redux on server-side code (probably with Redux saga).
I would agree lifetime management as a React component is easier than a Redux middleware. But if you want to reconnect (destroy/recreate), you will need to use key props to make the reconciler to consider it as a new object, which is a little bit weird.
You can look at redux-websocket-bridge, which unfold Web Socket messages into Redux actions, and relay Redux actions to Web Socket.
On your Web Socket server, you send an action:
ws.on('connection', conn => {
conn.send(JSON.stringify({
type: 'GREETING',
payload: { now: Date.now() }
}));
});
You will get the GREETING action on your Redux reducer. And vice versa, when you want to relay an action to Web Socket, you mark your action with meta.send with true:
this.props.dispatch({
type: 'SIGN_IN',
meta: { send: true },
payload: { authToken: '...' }
});
Would there be anything wrong/anti-pattern-ish (in terms of 'thinking-in-react/redux') in added a callback to the action.data passed into an action?
// reducer
ACTION_FOR_REDUCER() {
var x = 123
if ( action.data.callback ) action.data.callback( x )
return {
something: action.data.somedata
}
},
Then accessing that data later in the App when the action is called (in an container perhaps)
// later in the app
this.props.dispatch(changeSomething({
somedata: somedata,
callback: (x) => { console.log(x) }
}))
The idea is not wrong, the implementation is.
Instead of adding a callback, return a promise from the action (you will need redux-thunk middleware or some similar alternative).
Then you can simply do:
dispatch(myAction).then(callback);
Of course, you can also simply dispatch another action in your callback, which usually end up being one compound action.
const myAction = ...
const callbackAction = ...
const compoundAction = () => dispatch => {
dispatch(myAction())
.then(() => dispatch(callbackAction()));
};
The third principle of Redux (listed in the introduction section of the docs) is that 'changes are made with pure functions'.
A pure function is a function that always returns the same result given the same inputs, and doesn't cause any side effects. Having a callback log something out most definitely would be a side effect!
The docs go on to say:
It's very important that the reducer stays pure. Things you should never do inside a reducer:
Mutate its arguments;
Perform side effects like API calls and routing transitions;
Call non-pure functions, e.g. Date.now() or Math.random().
The reasoning behind this is it makes your reducers predictable - you know that, given an action with a certain payload, you're guaranteed to have the same output. This makes your app both easier to understand as a developer, and easier to unit test.
If you want to perform side effects when an action takes place, you should install a middleware that allows you to do so - some common examples of these are:
redux-thunk, which allows you to dispatch functions that can in turn dispatch actions (commonly used to make API calls)
redux-logger, which allows you to log out actions that are dispatched.
redux-devtools-extension, which allows you to view your state and actions in a Chrome devtools extension.
If you can't find a middleware package that does what you need (rare, to be honest - there's lots out there already!), you can write your own quite easily.
Callbacks introduce side-effects, so when using Redux you should consider returning a new state with the result, for example with done: true flag and react to that in componentDidUpdate.
For managing asynchrony and effects you can use redux-saga, redux-thunk, RxJs or another library.
If you want to stick with callbacks, why even bother with Redux?
Here are two code samples:
onClick() { // click-handler of a button
axios.get(someUrl)
.then(response => {
// setData is a fn dispatching an action-creator passed through react-redux's connect()
setData(response.data);
});
}
or
// buttonClicked is also a fn dispatching an action-creator
// Difference being the middle-ware handles the entire async process
<button onClick={this.buttonClicked}>Click me</button>
The latter method will use Axios in some middleware, and then dispatch another action which will set the response data in the store.
So this means that the first approach will only dispatch one action, while the second approach will dispatch two actions.
Both ways obviously seem to work, but I would like to know what the best way would be. Is there a downside to either approach?
Disclaimer: This is an opinionated answer, and somewhat rambly.
The thing about promises is that they work the way a human being would think of a promise. So use them like that in your program. Typically IMO you should only use Promises when you know that an event would occur in the normal course of your program workflow, or when you are promising a result.
So for example if you ask for a socket connection, I promise to give one to you whenever I am able to, you don't have to wait for me, just go on and do your thing, as soon as I have done everything needed to get that back to you I will hand it to you; and you can move on in your workflow from the point that needs it. For example, (pseudo code):
var Socket = new Promise(function(resolve, reject) {
resolve(do_something_to_get_a_socket());
});
Socket.then(authenticate()).then(sendData());
etc.
Sticking a promise to an event handler like onClick should be a promise to do something for the user — use it in your code to create threads that will do the heavy lifting of complex processing, while the user is still able to interact with the interface.
For example, in a game a click could fire a dart and you promise that it will animate on the screen (and even if that glitches) you still promise that it will hit the target etc, but the user doesn't have to wait for the promise to be fulfilled to fire another dart.
So use Promises to make your program more readable by you and other coders, and use it to make workflow of your program more realistic to your usecase.
I heavily recommend something like: https://www.npmjs.com/package/redux-api-middleware
This middleware (or others like it) contain quite a few features you would most likely have to write yourself if you were to implement this with just axios in a callback. For example, it will automatically dispatch request, success, and failure actions based on the AJAX call's result.
When dispatching this action using the middleware, many things are taken care of for you.
{
[CALL_API]: {
endpoint: "http://example-api.com/endpoint",
method: "GET",
headers: { ... },
types: [
"GET_X_REQUEST", "GET_X_SUCCESS", "GET_X_FAILURE"
]
}
}
Something like this will automatically fire a "GET_X_REQUEST" action when it begins to load. Then a success or failure action (with appropriate data or error objects attached as a payload) when the AJAX call completes or fails.
Or any similar middleware where Redux ends up handling the entire async process.
I keep reading that I should use redux-thunk or redux-saga to handle side effects.
Why not simply use action creators like that to dispatch multiple actions :
function loadProductActionCreator(dispatch) {
dispatch({
type: 'load_product',
})
fetch('api/product').then(
function (r) {
return r.json();
}
)
.then(function (res) {
dispatch({
type: 'loaded_product',
data: res
})
})
}
I tried that and it worked (complete code). So I guess there must be some inconvenients I'm not aware of.
You code is similar to what thunk does.
As per redux docs, actions should be pure. And they should always return same values for same input parameters. By using fetch you are allowing action to return not specific value, rather value from server and that mean action response may vary upon time.
That is called side effects. And it's something what shouldn't be in redux actions by default.
But why?
Yes, you can type it inside action like you have, in small apps it does not matter.
In larger application there are benefits of using redux-saga:
actions are predictable, they just return payload like
{
type: 'FETCH_POSTS',
params: {
category: 'programming'
}
}
and then you build middleware which will take actions with all data required to perform request to real API
Possible advantages:
Cleaner codebase (but may be overhead on smaller applications)
Separation of "dummy" actions with all required information to perform requests and actual API middleware
Request parameters are visible directly in redux dev tools
Possible to easily debounce, throttle fetches which may be really tricky with redux-thunk
Possible to easily combine actions (wait for another event/fetch, chain events)
Possible to stop running tasks
From personal experience, on one project (larger codebase) we have started with redux-thunk, but later we needed to integrate more advanced features, like throttle, and some dependencies between actions. So we rewrote everything to redux-saga and it worked well for us.
You are kind of replicating redux-thunk here. A pure redux action creator should return an action object to be dispatched and not dispatch an action itself (see redux doc on action creator).
To better understand why your technic is a replication of redux-thunk, look at this post from its author