I have the following action in my actions.js file. But I get the error "Actions must be plain objects. Use custom middleware for async actions." I'm assuming it's because I putting the return in a weird place. Any ideas?
export function loginLookUp(credentials) {
const request = axios.post(`${LOGINLOOKUP_URL}`, credentials).then(function (response) {
return {
type: LOGIN_SUCCESS,
payload: true
};
})
.catch(function (error) {
return {
type: LOGIN_FAIL,
payload: false
};
});
}
Your action creator doesn't return anything - your return statements are in the scope of the promise callbacks, not the surrounding function. But that's kind of beside the point - even if you did return the promise from the action creator, Redux wouldn't know what to do with it!
In order to do asynchronous actions, you need to use a library such as redux-thunk or redux-promise. I'm not going to go into detail on how to set these libraries up, as the READMEs on their repos do a much better job, but here's a few examples of how you'd use them.
redux-thunk allows you to dispatch a function that in turn has access to dispatch, like so:
export function loginLookUp(credentials) {
// This would look a lot cleaner if you used an ES2015 arrow function
return function (dispatch) {
const request = axios.post(`${LOGINLOOKUP_URL}`, credentials).then(function (response) {
dispatch({
type: LOGIN_SUCCESS,
payload: true
});
})
.catch(function (error) {
dispatch({
type: LOGIN_FAIL,
payload: false
});
});
}
}
This is about as simple as async actions get, and it's how the official Redux tutorial will teach you to do it - make sure to give those chapters a read, it's really helpful stuff!
redux-promise, on the other hand, lets you dispatch actions with a promise as the payload:
export function loginLookUp(credentials) {
return {
type: LOGIN,
// Payload must be a promise!
payload: axios.post(`${LOGINLOOKUP_URL}`, credentials)
};
}
Rather than the action immediately being passed to the reducer, it will use .then() to wait for the promise in the payload to complete. Then, it will dispatch the action in one of two forms.
If the promise resolves, the action will be dispatched with the payload set to the resolved value, like so:
{
type: LOGIN,
payload: // The response from the server
}
If the promise fails, the action will be dispatched with the payload set to the rejected value and the error property set to true, like so:
{
type: LOGIN,
payload: // The error object,
error: true
}
These are by no means the only ways of doing things - there's countless async action libraries, so if these both made you recoil in horror, there's bound to be something else that will suit you (I hear really good things from people smarter than me about redux-saga, but I can't comprehend it myself)!
Talking of sagas, this answer ended up being way longer than I intended. Hopefully it clears stuff up for you!
Related
I'm working on a React Native with Expo project, right now i'm trying to delete an item from a list inside a local database. Problem is in the action where I send it. Here is my code.
export const eliminatePlace = (placeId) => {
console.log(`Inside the action with placeID ${placeId}`);
return async dispatch => {
console.log('returned');
try {
const dbResult = await deletePlace(placeId);
console.log(dbResult);
dispatch({
type: DELETE_PLACE,
id: placeId
});
} catch (err) {
throw err;
console.log(err);
}
};
}
Somehow the console.log inside the return didn't fire up, my workaraound was this:
export const eliminatePlace = async (placeId, dispatch) => {
try {
console.log(`Trying to eliminate place with ID ${placeId}`);
const dbResult = await deletePlace(placeId);
console.log(dbResult);
dispatch({type: DELETE_PLACE, id: placeId});
} catch (err) {
throw err;
console.log(err);
}
};
Then it did work, but this is not the best practice, any ideas on why the correct way didn't work?
Here is the link to my github repo where you can download the project:
https://github.com/josmontes/rn-places
In case someone needs to see another place of the code please ask, I didn't add anything else so it doesn't bloats the question and because the problem is inside this function.
You shouldn't be calling async functions inside your action creators. You can read more about why here. Instead, you should use async actions. Even though you aren't making an API call, you can still represent your process as request-success-failure. Basically, dispatch a "request" action and as a side effect call your async function. Once it resolves, you dispatch either "success" or "failure" action, depending on the result. You can then put the results from the database in the payload of the "success" action. You can read more about that here.
I believe the the second example you gave works, because that's basically just the "success" action. It only dispatches a regular action once the async function resolves, while in the first example, the action function itself is async, which redux doesn't like.
I was working with redux-thunk and superagent npm for jwt authentication and i would want to know how to implement post calls using thunk-middleware in the actions.js file and not in the main reducer.js file
There's a couple of different ways to go about it, but I personally like to use the axios library. Axios essentially just assists with making an API request and parsing the data back from the API into json.
In your actions.js file.
export const authenticateUser = () => {
return (dispatch, getState) => {
//get the token from the reducer
const jwtToken = getState().jwtTokenReducer.tokenKey
axios.post("/api/authenticate-user", jwtToken) //jwtToken passed into request
.then((res) =>){
dispatch({
type: "AUTHENTICATE_USER",
payload: res.data
})
}
.catch((errors) => {
dispatch({
type: "ERRORS",
payload: errors.response.data
})
})
}
}
So take a look at the above syntax. Typically when you define an action-creator, you setup a function that returns an action (object). But thanks to redux-thunk, you can now setup your action-creators to return functions with dispatch as an argument.
So in your returned function you can define certain logic, like making a request to an API like we did up there. Then we can take that data by using the .then promise handler and use it as a payload for an action that we would explicitly dispatch to our reducers.
export function postRegister(credentials) {
console.log(credentials);
return dispatch => {
return fetch('/user/register', {
method: 'post',
body: JSON.stringify(credentials),
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
}
}
I have few doubts regarding code above.
Can I use export () => {} instead of writing the word function here? Just to stay cleaner.
dispatch is a global variable? I did not see it's imported or required somewhere in the file.
Is specifying headers necessary here? I'm seeing that in every of the api call.
Why there's no catch in this promise call? Overall the code is bad?
No really, you could but you need a name to actually use it in your components.
No, dispatch is a parameter of the arrow function, you can also define getState to access the current redux state. By the way, you can totally assign new names if you want.
It depends on your server, but generally if you are using a JSON API, you would want to send that header.
Yes, overall that code doesn't look good, I would recommend using a middleware to handle the fetch requests, your actions should only send the configurations such as the url, body, method, etc... and your middleware should handle adding common headers (such as the content-type).
You could have an action like this:
export function postRegister(credentials) {
return {
types: [REGISTER, REGISTER_SUCCESS, REGISTER_FAIL],
promise: {
url: '/user/register',
data: credentials,
},
};
}
Something as simple as that, then your middleware should do the fetch and dispatch the action types based on the server response.
If you want to know more about how the middleware should handle the fetch request and dispatch the actions, make sure to take a look at my post here: https://stackoverflow.com/a/39971763/146718
not unless it is export default. since later u will need to import it by name.
no, dispatch is an argument that is passed to your function:
(dispatch) => {}
Totally depends on your application, server, request, etc.
you could add .catch((e) => {}) your self, or use some interceptors for generic errors, do a dipatch from there and add a reducer which will handle these actions. you could read more here:
What is the best way to deal with a fetch error in react redux?
In my application, we have a somewhat long-running "provisioning" task that asynchronously completes that we essentially have to use polling to detect that it has finished. So that a user isn't blocked from using the app while this takes place, we kick that off at the beginning of the user interaction flow. However, at the end of the interaction flow, the user takes an action that needs to await that provisioning task if it hasn't completed. Here's a sort of sequence diagram to illustrate.
[provisioning start][poll for completion....]
[user interactions] [blocked action....][post provisioning task]
The problem I'm having is figuring out an idiomatic way to do this properly in Redux. Here are some things I've considered doing:
Have a "requests" reducer, where I'd store the Promise for that long-running provisioning task, then when [blocked action] executes, it simply awaits the promise. The problem with this is that Redux specifically requests that all store state is serializable, so it seems that things like Promises are not welcome there.
Subscribe to the store in the [blocked action] action creator and look for data that signals the provisioning has completed. Unfortunately, redux-thunk, which I'm pretty fond of, doesn't contain a reference to the store, just its dispatch and getState methods.
Having my React component wait for the provisioning to complete before executing [blocked action]. I don't like this because it adds too much action sequencing logic awareness to my view layer.
Since none of these seem like great options, I'm currently going with:
Store the provisioning Promise in a module property and essentially do option #1 without getting the Promise from the store. I'm not wild about this, since it moves state outside of the redux loop, but it seems like the easiest option.
Is there a more idiomatic way to achieve this with redux-thunk? If there's another asynchronous middleware that makes this cleaner, I'm willing to consider, though I'm already quite invested in redux-thunk.
There are several redux-promise-middleware options that let you dispatch promise actions and have the middleware consume them and emit PENDING,RESOLVED|REJECTED actions as the promise state changes. If you are working with promises and Redux, you might want to look at one of them. However they won't help you with this specific problem.
I think redux-saga is a middleware well suited to help you with this scenario. I've not yet used it personally so I can't provide an example.
Another, perhaps simpler, option is redux-tap. Use tap to expose the stream of actions that your app can subscribe to. Have a blocked action creator thunk subscribe to that stream and await the action that signals that the promise has completed (it should of course first check the contents of the store via getState to see if the promise completed before this blocked action was dispatched). Something like this:
// ========= configureStore.js
import ee from 'event-emitter';
// ...
export const actionStream = ee();
// ...
const emitActions = tap(({type} => type, (type, action) => actionStream.emit(type, action);
// install emitActions middleware to run *after* thunk (so that it does not see thunk actions but only primitive actions)
// =========== your action module.js
import {actionStream} from './configureStore';
export function blockedAction(arg) {
return (dispatch, getState) => {
if (getState().initDone) {
return dispatch({type: "blockedAction", payload: arg});
}
// wait for init action
actionStream.once("initAction", initAction => dispatch({type: "blockedAction", payload: arg}));
};
}
Keep in mind that it is not a binary "either thunk or xxx middleware" choice. You can load thunk as well as many other middlewares. In one app, I use:
thunk
a promise middleware
console logging middleware
custom middleware for batching updates
custom middleware for persisting actions to IndexedDB
custom middleware for pausing the action stream
middleware to prevent recursive store notifications
Without any middleware other than thunk, here is another option:
Option 5 - combine your option 2 with your option 4: Store the promise somewhere globally. Have the blocked action creator thunk await the promise before dispatching the raw action. To minimize the "state outside of the store", you can also have the promise action emit actions into the store to keep the store updated (possibly using a promise middleware)
I think this is simpler—unless I'm missing something—if we're more rigorous in thinking about the UI as a function of state. At every point in your interaction flow there are a few things that are either true or false:
Provisioning has started/not started
Provisioning has finished/not finished
User action is pending/is not pending
These three facts are enough to tell us what the user should be seeing on their screen at a given moment, so our state should include these three facts:
const initialState = {
provisioningStarted: false,
provisioningDone: false,
laterActionIsPending: false,
// ...
};
Using redux-thunk we can handle the provisioning action:
function startProvisioning() {
return dispatch => {
dispatch({ type: START_PROVISIONING });
pollProvisioning().then(postSetupInfo => {
dispatch({ type: DONE_PROVISIONING, postSetupInfo });
});
};
}
...and in our reducer:
function appReducer(state, action) {
// ...
switch (action.type) {
case START_PROVISIONING:
return { ...state, provisioningStarted: true };
case DONE_PROVISIONING:
return { ...state, provisioningDone: true, postSetupInfo: action.postSetupInfo };
case LATER_ACTION_PENDING:
return { ...state, laterActionIsPending: true };
// ...
}
}
As I said, this should be enough to tell us what the user should be seeing:
const Provisioner = ({provisioningStarted, provisioningDone, /* ... */}) => {
if (provisioningStarted) {
if (laterActionIsPending) {
if (provisioningDone) {
return <p>Finished! Here's your info: {postSetupInfo}</p>;
}
return <p>Provisioning...</p>;
}
return <button type="button" onClick={doAction}>Do action</button>;
}
return <button type="button" onClick={startProvisioning}>Start provisioning</button>;
};
export default connect(mapStateToProps, mapDispatchToProps)(Provisioner);
Not exactly redux-thunk but functionally equivalent is using
redux-background. A package I created to make the process easier.
It goes something like this:
import { startJob } from 'redux-background';
import store from './store';
store.dispatch(startJob('provision', function(job, dispatch, getState) {
const { progress, data } = job;
return new Promise((resolve, reject) => {
// Do some async stuff
// Report progress
progress(10);
// Return value
return 10;
})
}, { data: '...' } );
On your state when it ends you should have something like this
{
background: {
provision: {
active: true,
running: false,
value: 10,
error: null,
/* and lot of more metadata */
....
},
}
}
Meteor uses callbacks, but it seems like there is no 'wait' for them with Redux actions. So, if using something like Redux and having an action something like this:
export function loginWithPassword(email, password) {
return dispatch => {
Meteor.loginWithPassword(email, password, error => {
if (error) {
return dispatch({type: ASYNC_ERROR, data: error.reason})
});
}
}
... the action will complete and return before Meteor's call does (thus not re-rendering the UI if needed). I have scoured for hours trying to find the best way to deal with this to no avail. Based on what I have been able to find, it would seem the options are Meteor Futures or Promises, however I cannot find anything indicating which might be better or why.
I believe that Futures can only be server side and are less widely used than Promises, so Promises may be the better options based on that. I am not even sure if Futures is an option since actions are client side. But I am also not sure if Meteor will play nice with Promises given it's general sync nature.
Promises I believe could be either client or server. Assuming they were client, would it be better to put them in the actions or in the UI code dispatching the actions - or does it really matter?
Does anyone have any thoughts or insight into how best to handle something like this? A working example of one or the other (or both) would be great because I sure couldn't find one after hours of searching. Specifically it would be great to see an example of a simple Meteor login with password dispatched via Redux that displays a 'User not found" or some other 'async' error in the UI login form (the FIRST time).
The action above actually works, but it returns before Meteor is done returning the error, so the error is not displayed in the UI the initial time.
TIA!
P.S. There are a couple of examples out there that display the error in an alert window or console.log it - but that is not the same thing as updating/displaying it as a prop in the UI that dispatched the action. There are no prop re-renders tied to an alert or console.log. The whole idea is to show the error in the form the user is currently on (login form in this example case)
Using redux-thunk, you could write something like below. I think the trick here is knowing the status of the user object once it's logged in. One way of doing this is to use store.dispatch inside a Tracker.autorun:
// whenever the reactive data changes, we will dispatch the appropriate action
Tracker.autorun(() => {
if (Meteor.loggingIn()) {
return store.dispatch({
type: LOGGING_IN,
});
}
const user = Meteor.user();
store.dispatch({
type: SET_USER,
user,
}
});
export const login = ({ email, password }) => (dispatch) => {
Meteor.loginWithPassword(email, password, (error) => {
if (error) {
return dispatch({
type: LOGIN_ERROR,
error,
});
}
return dispatch({
type: LOGIN_SUCCESS,
});
});
};
You could imagine a user reducer that has a state like {user: {loggingIn: Boolean, authError: SomeErrorModel}}} or something like that
You could clear the authError on LOGGING_IN, and add it on LOGIN_ERROR
For any UI changes based on this reducer, just connect your react component with react-redux's connect()