I need to post data from a user to my database (firebase), but when I click on Touchable Opacity, my app returns this error:
Single Ocurrence
Possible Unhandled Promise rejection (id:0): TypeError: undefined is not an object (evaluating 'res.data'
My code:
import {USER_LOGGED_IN, USER_LOGGED_OUT } from './actionTypes'
import axios from 'axios'
const authBaseURL ='https://www.googleapis.com/identitytoolkit/v3/relyingparty'
const API_KEY ='my api key is here'
export const login = user => {
return {
type: USER_LOGGED_IN,
payload: user
}
}
export const logout = () => {
return {
type: USER_LOGGED_OUT
}
}
export const createUser = user => {
return dispatch => {
axios.post (`${authBaseURL}/signupNewUser?key=${API_KEY}`, {
email: user.email,
password: user.password,
returnSecureToken:true
})
.catch(err => console.log(err))
.then (res => {
if (res.data.localId) {
axios.put(`/users/${res.data.localId}.json`, {
name: user.name,
tipouser: user.tipouser,
telcelular: user.telcelular,
telfixo: user.telfixo,
facebookuser: user.facebookuser,
instagramuser: user.instagramuser
})
.catch(err => console.log(err))
.then(res => {
console.log('Usuário criado com sucesso')
})
}
})
}
}
react-native: 0.60.4
axios: 0.19.0
react-redux: 7.1.1
redux: 4.0.4"
Related
So basically as the APP.js renders it is not sending requests to the backend. I am calling the currentUser function inside App.js function. Please help me I am stuck
app.js file
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(async (user) => {
if (user) {
const getidtoken = await user.getIdTokenResult();
currentUser(getidtoken.token)`enter code here`
.then((res) => {
console.log(res);
dispatch({
type: 'LOGGED_IN_USER',
payload: {
email: res.data.email,
name: res.data.name,
role: res.data.role,
_id: res.data._id,
},
});
})
.catch((err) => {
console.log(err);
});
}
});
currentuser.js Function
export const currentUser = async (authtoken) => {
return await axios.post(
process.env.REACT_APP_API_USER,
{},
{ headers: { authtoken: authtoken } }
);
};
enter image description here
How can I use the current status of redux after the thunks and actions have finished? The problem is in the handleSubmit function if I register a user with errors, it updates the status of redux with the message "Email already registered", but when accessing the state in the dispatch promise sends me a wrong state, without the message.
Function hanldeSubmit
const handleSubmit = (e) => {
e.preventDefault()
const form = {
name: e.target[0].value,
email: e.target[1].value,
password: e.target[2].value,
confirmPassword: e.target[3].value
}
const { name, email, password } = form
if (isFormValid(form)) {
//TODO: FIX IT synchronize redux with errors
dispatch( startRegisterUser(name, email, password) ).then(() => {
console.log(state)
})
}
}
register action and thunk
export const startRegisterUser = (name, email, password) => {
return (dispatch, state) => {
dispatch(startLoadingAction())
return firebase.auth().createUserWithEmailAndPassword(email, password)
.then(async ({ user }) => {
await user.updateProfile({
displayName: name,
photoURL: ''
})
dispatch(registerUserAction(user.uid, user.displayName))
})
.catch(e => {
if (e.code === "auth/email-already-in-use") {
dispatch(errorAction("Email already registered"))
} else {
dispatch(errorAction("Unexpected error"))
}
})
.then(() => {
dispatch(finishLoadingAction())
console.log("finished dispatch's", state())
return
})
}
}
export const registerUserAction = (uid, displayname) => {
return {
type: types.register,
payload: {
uid,
displayname
}
}
}
console logs
I want to get the status of the first console log but in the handlesubmit function
You should handle the errorAction in the reducer, update the ui store slice with the error message. And, you need to return the state() in the promise in the thunk function. Then, you will get the whole state inside the handleSubmit event handler.
E.g.
import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
function errorAction(message) {
return {
type: 'ERROR',
payload: message,
error: true,
};
}
export const startRegisterUser = (name, email, password) => {
return (dispatch, state) => {
return Promise.reject({ code: 'auth/email-already-in-use' })
.catch((e) => {
if (e.code === 'auth/email-already-in-use') {
dispatch(errorAction('Email already registered'));
} else {
dispatch(errorAction('Unexpected error'));
}
})
.then(() => state());
};
};
export const registerUserAction = (uid, displayname) => {
return {
type: 'REGISTER',
payload: {
uid,
displayname,
},
};
};
function rootReducer(state = { ui: { error: '' } }, action) {
switch (action.type) {
case 'ERROR':
return { ui: { error: action.payload } };
default:
return state;
}
}
const store = createStore(rootReducer, applyMiddleware(thunk));
function handleSubmit() {
store
.dispatch(startRegisterUser('name', 'example#gmail.com', '123') as any)
.then((state) => {
console.log('handleSubmit state: ', state);
});
}
// triggered by user submit event
handleSubmit();
Output:
handleSubmit state: { ui: { error: 'Email already registered' } }
I need some help with jest test. I am getting error TypeError: Cannot read property 'then' of undefined in my test
signIn function in Action Creators makes a POST call
import {SIGNING_IN, SIGNED_IN, SIGNING_FAILED, SIGNED_OUT} from "../actionTypes";
import {makePostCall} from "../../api/apiCalls";
export function signIn(data){
return dispatch => {
dispatch({type: SIGNING_IN});
makePostCall('http://localhost:8000/api/v1/signin', data)
.then(response => {
const auth = {token: response.token, userId: response.id};
localStorage.setItem('auth', JSON.stringify(auth));
dispatch({type: SIGNED_IN, message: 'You signed in successfully.'});
})
.catch(error => {
console.log('error: ', error);
dispatch({type: SIGNING_FAILED, message: 'Email or Password incorrect. Please try again!'});
});
}
}
this is POST call function which gets called in above call
export function makePostCall(url, data){
return axios({method: 'post', url, data})
.then(response => response.data)
}
Test for signIn Method
jest.mock('../../../src/api/apiCalls');
describe('authenticationActionCreators', () => {
describe('signIn', () => {
let dispatch;
beforeEach(() => {
jest.clearAllMocks();
const authResponse = getAuthResponse();
makePostCall.mockReturnValue(Promise.resolve(authResponse));
dispatch = jest.fn();
});
test('should make post call with correct URL and data', () => {
const data = {email: 'user#user.com', password: 'password'};
return signIn(data)(dispatch).then(() => {
expect(makePostCall).toHaveBeenCalledWith('http://localhost:8000/api/v1/signin', {
email: 'user#user.com',
password: 'password'
})
})
});
});
Whenever i run test i get error on line return signIn(data)(dispatch).then(() => {
i was doing it wrong. I changed it and it works
test('should make post call with correct URL and data', () => {
const data = {email: 'user#user.com', password: 'password'};
signIn(data)(dispatch);
expect(makePostCall).toHaveBeenCalledWith('http://localhost:8000/api/v1/signin', {
email: 'user#user.com',
password: 'password'
})
});
I'm trying to write a unit test for a redux async action creator using jest.
asyncActions.js:
const startSignInRequest = () => ({
type: START_SIGNIN_REQUEST
});
// action creator to dispatch the success of sign In
export const signInSucceded = user => ({
type: SIGNIN_USER_SUCCEEDED,
user
});
// action creator to dispatch the failure of the signIn request
export const signInFailed = error => ({
type: SIGNIN_USER_FAILED,
error
});
const signInUser = user => dispatch => {
dispatch(startSignInRequest);
return signInApi(user).then(
response => {
const { username, token } = response.data;
dispatch(signInSucceded(username));
localStorage.setItem("token", token);
history.push("/homepage");
},
error => {
let errorMessage = "Internal Server Error";
if (error.response) {
errorMessage = error.response.data;
}
dispatch(signInFailed(errorMessage));
dispatch(errorAlert(errorMessage));
}
);
};
signInApi.js:
import axios from "axios";
import { url } from "../../env/config";
const signInApi = async user => {
const fetchedUser = await axios.post(`${url}/signIn`, {
email: user.email,
password: user.password
});
return fetchedUser;
};
In the Writing tests of redux's official documentation, they use fetch-mock library. However, I think that this library call the real Api.
I tried to mock the axios api using jest mocks.
/__mocks/signInApi.js:
const users = [
{
login: 'user 1',
password: 'password'
}
];
export default function signInApi(user) {
return new Promise((resolve, reject) => {
const userFound = users.find(u => u.login === user.login);
process.nextTick(() =>
userFound
? resolve(userFound)
// eslint-disable-next-line prefer-promise-reject-errors
: reject({
error: 'Invalid user credentials',
}),
);
});
}
__tests/asyncActions.js:
jest.mock('../axiosApis/signInApi');
import * as actions from '../actions/asyncActions';
describe('Async action creators', async () => {
it('Should create SIGN_IN_USER_SUCCEEDED when signIn user has been done', () => {
const user = {
login: 'user 1',
password: 'password'
}
await expect(actions.signInUser(user)).resolves.toEqual({
user
})
})
});
The test failed and I got:
expect(received).resolves.toEqual()
Matcher error: received value must be a promise
Received has type: function
Received has value: [Function anonymous]
How can I mock this async action creator only with jest?
Looks like you need to update your mock to resolve to an object like this:
export default function signInApi(user) {
return new Promise((resolve, reject) => {
const userFound = users.find(u => u.login === user.login);
process.nextTick(() =>
userFound
? resolve({ // <= resolve to an object
data: {
username: 'the username',
token: 'the token'
}
})
// eslint-disable-next-line prefer-promise-reject-errors
: reject({
error: 'Invalid user credentials',
}),
);
});
}
...then what you are really testing is that actions.signInUser returns a function which can be called with a user...
...which then returns another function which can be called with a dispatch which dispatches the proper actions:
jest.mock('./signInApi');
import * as actions from './asyncActions';
describe('Async action creators', () => {
it('Should create SIGN_IN_USER_SUCCEEDED when signIn user has been done', async () => {
const user = {
login: 'user 1',
password: 'password'
};
const dispatch = jest.fn();
await actions.signInUser(user)(dispatch); // <= call the function on a user, then call the resulting function on a dispatch
expect(dispatch).toHaveBeenCalledTimes(2); // Success!
expect(dispatch).toHaveBeenNthCalledWith(1, { type: START_SIGNIN_REQUEST }); // Success!
expect(dispatch).toHaveBeenNthCalledWith(2, { type: SIGNIN_USER_SUCCEEDED, user: 'the username' }); // Success!
})
});
EDIT: I have to edit my answer as the first one pointed to a wrong direction.
So from my understanding you want to mock the Action + Return value. In your case I would just immediately return the result of your mock function. As you're not mocking axios.post you don't need to wrap everything inside a promise and return that. You're not mocking only the HTTP call but the whole action.
const users = [
{
login: 'user 1',
password: 'password'
}
];
export default function signInApi(user) {
const userFound = users.find(u => u.login === user.login);
return (userFound ? userFound : {
error: 'Invalid user'
});
}
I have the following actions in my Vuex store:
import { HTTP } from '#/services/http'
import router from '#/router'
export const actions = {
loginUser ({ commit, state }, params) {
HTTP.post('v1/login.json', { email: params.email, password: params.password })
.then(response => {
localStorage.setItem('access_token', response.data.token)
router.push({name: 'Hello'})
}).catch(error => {
commit('SET_LOGIN_ERROR', error.response.data.error)
})
},
myAccount ({ commit }) {
HTTP.get('v1/my_account.json').headers({'Authorization': ('Token token=' + localStorage.getItem('access_token'))})
.then(response => {
commit('SET_USER', response.data)
})
}
}
I want to launch myAccount action when loginUser succeeds. How can I do that?
I've tried something like this:
import { HTTP } from '#/services/http'
import router from '#/router'
export const actions = {
loginUser ({ commit, state }, params) {
HTTP.post('v1/login.json', { email: params.email, password: params.password })
.then(response => {
localStorage.setItem('access_token', response.data.token)
router.push({name: 'Hello'})
}).catch(error => {
commit('SET_LOGIN_ERROR', error.response.data.error)
})
},
myAccount ({ dispatch, commit, state }, payload) {
dispatch('loginUser', payload)
.then((res) => {
console.log('dupa')
// Do this when loginUser finished
})
}
}
but this not works...
actions receive the context object, so you can simply either pass the entire object or add dispatch to your destructuring assignment :
const store = new Vuex.Store({
actions: {
foo(context) {
console.log('foo called');
},
bar({dispatch}) {
setTimeout(() => dispatch('foo'), 1000)
}
}
});
Here's the JSFiddle: https://jsfiddle.net/y1527vxh/
Since vue actions can be asynchronous you can add dispatch handler to an action to call another action when it is done;
export const actions = {
loginUser ({ commit, state }, params) {
... // some http request or what would you like to do
},
myAccount ({ dispatch, commit, state }, payload) {
dispatch('loginUser', payload)
.then((res) => {
...
// Do this when loginUser finished
})
},
}
I am doing autentication in my projects like this, i am using axios btw:
loginUser ({ dispatch, commit, state }, payload) {
let loginData = {
username: payload.username,
password: payload.password
}
return axios.post(state.url, loginData)
.then((res) => {
// You can console.log(res.data) to see if your token data is fine
window.localStorage.setItem('AuthTokens', JSON.stringify(res.data))
dispatch('myAccount', { tokens: res.data })
})
.catch((err) => {
// Error handling...
})
},
myAccount ({ commit, state }, { tokens }) {
let headerOptions = {
// Header options with tokens.access_token...
}
return axios.get(state.url, headerOptions)
.then((res) => {
// You have the user data
console.log(res.data)
})
.catch((err) => {
// Error handling...
})
}