I am getting this error: actions must be plain objects. Use custom middleware for async actions - react-native

I'm getting the error:
Actions must be plain objects. Use custom middleware for async actions.
I've tried the solution in the following Stack Overflow question, but it didn't work:
React-Redux: Actions must be plain objects. Use custom middleware for async actions
action
export async function signupp(data){
console.log('In signupp:');
try{
const request = await axios({
method:'POST',
url:'http://192.168.1.10:3003/users/signup',
data:{
email:data.email,
password:data.password
},
}).then(response=>{
console.log(response.data);
return response.data
}).catch( e => {
console.log(e);
return false
});
return {
type:'signup',
payload:request
}
}
catch(e) {
console.log(e);
return false;
}
}
reducer
export default function(state={},action){
switch(action.type){
case 'signup':
return {
...state,
auth:{
email: action.payload.email,
password:action.payload.password
}
}
}
}
store
const createStoreWithMiddleware = applyMiddleware()(createStore);
const appRedux = () => (
<Provider store = {createStoreWithMiddleware(reducers)}>
<App/>
</Provider>
)
AppRegistry.registerComponent(appName, () => appRedux);
BTW, I am getting the right response in the log.

Inside of the component, in the place where you call signupp function, you have mapDispatchToProps function as callback in connect function from react-redux lib, which is doing behind the hoods something like dispatch(signupp())(or maybe you are doing dispatch directly without react-redux lib).
According to redux API, this dispatch function expects to receive a plain object, but your signupp() function returns a promise(as you have async inside).
To solve this problem you can simply use redux-thunk middleware. Also you can see some examples in the redux docs section about async actions.
An alternative solution could be to move fetch logic to component and then dispatch just plain object with data that you received from the request.

Related

Unit test jest enzyme throws error on Formik 'resetForm'

I am trying to run unit test (enzyme) throws error on Formik 'resetForm'.
TypeError: Cannot read property 'resetForm' of undefined
FormikForm.js
_handleSubmitPress = (values, { resetForm }) => {
const { onSubmit } = this.props;
if (onSubmit) {
onSubmit({ ...values, resetForm });
}
};
UnitTest.js:
it('Should fire formik form submit', () => {
const UpdateButtonPressMock = jest.fn();
const component = Component({
onSubmit: UpdateButtonPressMock,
});
expect(component.find(Formik)).toHaveLength(1);
component.find(Formik)
.first()
.simulate('Submit');
expect(UpdateButtonPressMock).toHaveBeenCalled();
});
I couldn't find any solution for this error.
Could someone help me on the above? I would really appreciate any help.
According to official docs for simulate, the function signature accepts an optional mock event.
The code you are testing uses properties that are not included in the default SyntheticEvent object that ReactWrapper passes to your event handler by default, for instance event.resetForm.
One way to do this is by triggering Formik's onSubmit directly like so:
// UnitTest.js
.simulate("submit", { resetForm: whateverYourMockResetFormValueShouldBe })
component.find(Formik)
.first()
.prop('onSubmit')(valuesMock, { resetForm: UpdateButtonPressMock });
expect(UpdateButtonPressMock).toHaveBeenCalled();
I haven't tested this, but you should be able to pass the event along with simulate as well.
// UnitTest.js
component.find(Formik)
.first()
.simulate("submit", { resetForm: UpdateButtonPressMock })
expect(UpdateButtonPressMock).toHaveBeenCalled();

Nuxt Fetch Doesn't Update on First Load

I'm having the following issue and hope someone could help me on it:
Fetch is not working on the first load (nor on reloads). It only works when on the client-side (when I move between routes).
I've read that watchQuery could help but didn't understand why and how to use it.
<script>
export default {
async fetch() {
const userId = await this.$nuxt.context.store.state.auth.authUser.userId
await this.$store.dispatch('case/fetchMyCases', userId.uid)
await this.$store.dispatch('case/fetchMyPendingCases', userId.uid)
...
It doesn't work even if I import and use firebase/auth directly.
<script>
import * as firebase from 'firebase/app'
import 'firebase/auth'
export default {
async fetch() {
const userId = await firebase.auth().currentUser
await this.$store.dispatch('case/fetchMyCases', userId.uid)
await this.$store.dispatch('case/fetchMyPendingCases', userId.uid)
...
Does anyone have any tips for it? I'd really appreciate it.
Thanks!
After literally 3 days searching/testing, I finally found out why I was having this issue.
The problem was that I simply put async/await for fetch but didn't put async/await for the actions itself. Therefore, my getter (in computed) was getting the store state before the dispatches have been finished.
Thanks, everyone!
Warning: You don't have access of the component instance through this inside fetch because it is called before initiating the component (server-side).
async fetch({ store }) {
await store.dispatch('case/fetchMyCases')
await store.dispatch('case/fetchMyPendingCases')
}
If you need parameter:
async fetch({ store, params }) {
await store.dispatch('case/fetchMyCases', params.uid)
await store.dispatch('case/fetchMyPendingCases', params.uid)
}
I gave an example of id. The name of the parameter depends on the name of your page.
_id => params.id
_uid => params.uid
_slug => params.slug
...
Yes, You must put async/await on actions.
async automatically returns a promise
If you don't need the value, in this case, don't anything return.
export const Actions = {
async fetchUsers() {
// It will return automatically promise
await this.$axios.get('API')
}
}
// If you need returne value
// First way
export const Actions = {
async fetchUsers() {
// It will return promise and value
return await this.$axios.get('API')
}
}
// Second way
export const Actions = {
async fetchUsers() {
// It will return promise and value
const response = await this.$axios.get('API')
return response;
}
}

redux-thunk: actions are not dispatching

I am trying to build an app in react native that is suppose to take take two inputs by a user and then make a query to an api and get information about the two inputs. I have been having trouble with redux and redux-thunk and specifically with async actions.
This is the code in my app that i am specifically having trouble with
export const fetchData = url => {
console.log("start Fetching");
return async dispatch => { // this is where the problem is
dispatch(fetchingRequest());
try {
const response = await fetch("https://randomuser.me/api/?results=10");
const json = await response.text();
if (response.ok) {
dispatch(fetchingSuccess(json));
console.log("JSON", json);
} else {
console.log("fetch did not resolve");
}
} catch (error) {
dispatch(fetchingFailure(error));
}
};
console.log("Fetched data");
};
Upon debugging the function, I have ended with finding that when the fetchData function is called the function will execute but the async dispatch that is being returned has undefined behavior.
The output in the debugger when the function is called should be
start Fetching
JSON file information/Error
but the output in the debugger is actually
start Fetching
This is the function in which fetchData is called in
_onPress = () => {
let url = "https://randomuser.me/api/?results=10";
fetchData(url);
console.log("should have fetched");
};
this is the mapDispatchToProps function that I have added. The problem is i do not know what to add inside the function.
const mapStatetoDispatch = (url, dispatch) => {
return {dispatch(fetchData(url))}; // do not know what to place in body of function
};
i have connected it in the component with
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
these are the action creators that I import, if needed
import {
fetchingSuccess,
fetchingRequest,
fetchingFailure,
fetchData
} from "../data/redux/actions/appActions.js";
Assuming you have added redux-thunk as a middleware, it looks like the errors are here:
_onPress = () => {
const { fetchData } = this.props;
let url = "https://randomuser.me/api/?results=10";
fetchData(url);
console.log("should have fetched");
};
and
const mapStatetoDispatch = dispatch => ({
fetchData: url => dispatch(fetchData(url)),
}};

AsyncStorage in redux thunk, action not dispatching

I am trying to use asynchStorage to dispatch actions when I recieve the data:
componentWillMount() {
AsyncStorage.getItem("loggedIn")
.then(this.props.dispatch(isLoadingCredentials(true)))
.then(
data =>
data
? this.props
.dispatch(setCredentials(JSON.parse(data)))
.then(this.props.dispatch(navigate("Month")))
.then(
this.props.dispatch(
isLoadingCredentials(false)
)
)
: this.props.dispatch(isLoadingCredentials(false))
);
}
Using redux connect
export default connect(mapStateToProps)(HomeScreen);
I get the error:
Possible Unhandled Promise Rejection (id: 0):
TypeError: _this2.props.dispatch(...).then is not a function
TypeError: _this2.props.dispatch(...).then is not a function
How can I dispatch actions when I receive the data?
Try defining mapDispatchToProps and passing it as a second argument, alongside mapStateToProps.
const mapDispatchToProps = dispatch => ({
isLoadingCredentials: (loadingCredentials) => (
dispatch(isLoadingCredentials(loadingCredentials))
)
})
You could use bindActionCreators as Pegase745 suggested. It's shorthand for the above.
Then pass it through to your component via connect.
connect(mapStateToProps, mapDispatchToProps)(HomeScreen)
You would then use your function like so, notice there's no dispatch property.
componentWillMount() {
AsyncStorage.getItem("loggedIn")
.then(() => this.props.isLoadingCredentials(true))
...
}
You probably want to refactor your call to AsyncStorage because you're calling isLoadingCredentials(true) once the Promise has been fulfilled.
componentWillMount() {
this.props.isLoadingCredentials(true);
AsyncStorage.getItem("loggedIn")
.then((user) => {
// You have your User, do as you wish
// Add setCredentials and others to mapDispatchToProps
})
.catch(() => {
this.props.isLoadingCredentials(false); // Error
});
}

Returning Promise from action creator in React Native using redux-thunk

I have an action creator that is called from my React component:
// ...
import { connect } from 'react-redux';
// ...
import { submitProfile } from '../actions/index';
// ...
onSubmit() {
const profile = {
name: this.state.name
// ...
};
this.props.submitProfile(profile)
.then(() => { // I keep getting an error here saying cannot read property 'then' of undefined...
console.log("Profile submitted. Redirecting to another scene.");
this.props.navigator.push({ ... });
});
}
export default connect(mapStateToProps, { submitProfile })(MyComponent);
The definition of the action creator is something like the following. Note I am using the redux-thunk middleware.
export function submitProfile(profile) {
return dispatch => {
axios.post(`some_url`, profile)
.then(response => {
console.log("Profile submission request was successful!");
dispatch({ ... }); // dispatch some action
// this doesn't seem to do anything . . .
return Promise.resolve();
})
.catch(error => {
console.log(error.response.data.error);
});
};
}
What I want to be able to do is call the action creator to submit the profile and then after that request was successful, push a new route into the navigator from my component. I just want to be able to determine that the post request was successful so I can push the route; otherwise, I would not push anything, but say an error occurred, try again.
I looked up online and found Promise.resolve(), but it doesn't not seem to solve my problem. I know that I could just do a .then after calling an action creator if I was using the redux-promise middleware. How do I do it with redux-thunk?
The return value from the function defined as the thunk will be returned. So the axios request must be returned from the thunk in order for things to work out properly.
export function submitProfile(profile) {
return dispatch => {
return axios.post(`some_url`, profile) // don't forget the return here
.then(response => {
console.log("Profile submission request was successful!");
dispatch({ ... }); // dispatch some action
return Promise.resolve();
})
.catch(error => {
console.log(error.response.data.error);
});
};
}