Data gets overwritten in the redux-reducer - javascript

I was trying to implement a page-by-page onboarding signup screen for which the first page collects users horoscopic sign and in the next page, it asks for name. The thing is the sign_id gets replaced by name. Please check the codes below
action.js
import * as types from './types';
export function addNewUserRequest(values) {
console.log('action data', values);
return {
type: types.NEW_USER_REQ,
values,
};
}
reducer.js
import createReducer from '../lib/createReducer';
import * as types from '../actions/types';
const initialState = {
values: [],
};
export const newUserReducer = createReducer(initialState, {
[types.NEW_USER_REQ](state, action) {
console.table('reducer action test', state, action.values);
return {
...state,
values: action.values,
};
},
createreducer.js
export default function createReducer(initialState, handlers) {
return function reducer(state = initialState, action) {
if (handlers.hasOwnProperty(action.type)) {
return handlers[action.type](state, action);
} else {
return state;
}
};
}
Page1.js
const dispatch = useDispatch();
const onPress = (val) => {
console.log('SELECTED SIGN', val);
let value = {
sign_id: val,
};
NavigationService.navigate('Login3');
dispatch(newUserActions.addNewUserRequest(value));
};
Page2.js
const dispatch = useDispatch();
const handlePress = () => {
let value = {
name: userName,
};
dispatch(newUserActions.addNewUserRequest(value));
NavigationService.navigate('Login4');
};
Console

Change param in addNewUserRequest from values to value as only single value is passed. Then append action.value to state.values.
export function addNewUserRequest(value) {
console.log('action data', value);
return {
type: types.NEW_USER_REQ,
value,
};
}
export const newUserReducer = createReducer(initialState, {
[types.NEW_USER_REQ](state, action) {
console.table('reducer action test', state, action.value);
return {
...state,
values: { ...state.values, ...action.value }
};
},

Related

access store data from one slice to another slice in redux toolkit to pass custom param in API

Profile Slice:
import { createSlice, createAsyncThunk } from '#reduxjs/toolkit'
import { IMAGE_API, ACCESS_KEY } from "../../app/utils/constant";
export const getImages = createAsyncThunk('images', async () => {
return fetch(`${IMAGE_API + ACCESS_KEY}`).then((res) =>
res.json()
)
})
console.log(IMAGE_API + ACCESS_KEY);
const ProfilePicSlice = createSlice({
name: 'imageList',
initialState: {
images: [],
loading: false,
},
extraReducers: (builder) => {
builder.addCase(getImages.pending, (state) => {
state.loading = true;
})
builder.addCase(getImages.fulfilled, (state, action) => {
state.loading = false;
state.images.push(action.payload);
console.log(action.payload)
})
builder.addCase(getImages.rejected, (state) => {
state.loading = true;
})
}
});
export default ProfilePicSlice.reducer
Form Slice:
import { createSlice } from "#reduxjs/toolkit";
const initialState = []
const UserSlice = createSlice({
name: 'users',
initialState,
reducers: {
addUser: (state, action) => {
state.push(action.payload);
}
}
});
export const {addUser} = UserSlice.actions;
export default UserSlice.reducer;
I want to add custom param in API URL in asyncThunk '${IMAGE_API + 'custom param' + ACCESS_KEY}
custom param should come from form slice data.
redux-toolkit async thunks are able to access the ThunkAPI, which includes a function to get the current redux state object, it is the second argument passed to the thunk callback. From here you can select the state value you need.
See PayloadCreator.
Example:
export const getImages = createAsyncThunk(
'imageList /images',
(_, { getState }) => {
const store = getState();
const param = state.users./* access into state to get property */;
return fetch(`${IMAGE_API}${param}${ACCESS_KEY}`)
.then((res) => res.json());
}
);

React Native Redux: props undefined

How to call a callback function?
Or how to set state after mapStateToProps get data?
RegistrationContainers:
function mapStateToProps(state, props) {
return {
isRegistered: state.user.registered // isRegistered: 1
};
function mapDispatchToProps(dispatch, props) {
return {
registration:(data) => dispatch(registration(data))
};
RegistrationComponents:
onRegister(){
let email = this.state.email;
let password = this.state.password;
let registerData = {email:email,password:password};
registerData = JSON.stringify(registerData);
this.props.registration(registerData)
this.setState({isRegistered: this.props.isRegistered}) // isRegistered: undefined, but second run after is correct
registration actions:
export function OnRegistration(registered) {
/* ... */
return registered }
export function registration(regData) {
return (dispatch) => {
sendDataToApi(regData, "registration",(isRegistered)=>{
dispatch({
type: ON_REGISTRATION,
registered: isRegistered
})
})
}
}
registration reducer:
const initialState = {};
export function loginReducer(state = initialState, action) {
switch(action.type){
case REGISTRATION:
return Object.assign({},state,{
registered: action.registered
})
}
return state; }
Containers get current state, but containers props can't reach from components.
modify your methods by adding callback() functions. i.e.
function mapDispatchToProps(dispatch, props) {
return {
registration:(data, cb = null) => dispatch(registration(data, cb))
};
and modify registration(data) method to:
registration(data, cb) {
/* your code here */
/* on successful registration, set user.registered to true in redux and
then call cb function */
cb && cb();
}
modify onRegister() method to:
onRegister(){
let email = this.state.email;
let password = this.state.password;
let registerData = {email:email,password:password};
registerData = JSON.stringify(registerData);
this.props.registration(registerData, () => {
this.setState({isRegistered: this.props.isRegistered})
}
})

React/Redux App stops processing MapDispatchToProps functions

So Im having an issue with a react/redux project. The program stops outputting information when the first onSetTechnicianXXX call is made. The interesting thing is I successfully perform multiple onSetGetData actions as well as an onSetTechnicianId (inside the parent file). I get no errors. Below is the debug code:
console.log("1");
const technician = fetchedTechnicians[0];
console.log("Tech.Intake.Name: " + technician.intakeData.name);
console.log("2");
const name = technician.intakeData.name;
console.log("Name: " + name);
this.onSetTechnicianName(name);
console.log("3");
this.onSetTechnicianName(technician.intakeData.name);
console.log("4");
this.onSetTechnicianCerts(technician.intakeData.certifications);
console.log("5");
this.onSetTechnicianPhone(technician.intakeData.phone);
console.log("6");
this.onSetTechnicianEmail(technician.intakeData.email);
console.log("7");
this.onSetTechnicianServicebay(technician.intakeData.servicebay);
console.log("8");
========Output from the Chrome DevTools Console========
1 TechnicianEditForm.js:118
Tech.Intake.Name: Joe Technician TechnicianEditForm.js:120
2 TechnicianEditForm.js:121
Name: Joe Technician TechnicianEditForm.js:123
========Output from the Chrome Redux DevTool=========
selectedTechnicianId(pin): "-LnECtv2Ms40OaFBu2t0"
getdata(pin): false
name(pin): ""
certifications(pin): ""
phone(pin): ""
email(pin): ""
servicebay(pin): ""
These are the React/Redux files for processing the state
===========actionTypes.js:==========================
export const SET_TECHNICIANID = 'SET_TECHNICIANID';
export const SET_GETDATA = 'SET_GETDATA';
export const SET_TECHNICIAN_NAME = 'SET_TECHNICIAN_NAME';
export const SET_TECHNICIAN_CERTS = 'SET_TECHNICIAN_CERTS';
export const SET_TECHNICIAN_PHONE = 'SET_TECHNICIAN_PHONE';
export const SET_TECHNICIAN_EMAIL = 'SET_TECHNICIAN_EMAIL';
export const SET_TECHNICIAN_SERVICEBAY = 'SET_TECHNICIAN_SERVICEBAY';
=================actions.js:===========================
import * as actionTypes from './actionTypes';
export const setTechnicianId = (selectedTechnicianId) => {
return {
type: actionTypes.SET_TECHNICIANID,
selectedTechnicianId: selectedTechnicianId
};
};
export const setGetData = (getdata) => {
return {
type: actionTypes.SET_GETDATA,
getdata: getdata
};
};
export const setTechnicianName = (name) => {
return {
type: actionTypes.SET_TECHNICIAN_NAME,
name: name
};
};
##### Others Not Shown But Simular #####
=================reducer.js:================
import * as actionTypes from './actionTypes';
import { updateObject } from './utility';
const initialState = {
selectedTechnicianId: '',
getdata: false,
name: '',
certifications: '',
phone: '',
email: '',
servicebay: ''
}
const baseReducer = (state = initialState, action) => {
switch (action.type) {
case actionTypes.SET_TECHNICIANID:
return updateObject(state, { selectedTechnicianId: action.selectedTechnicianId });
case actionTypes.SET_GETDATA:
return updateObject(state, { getdata: action.getdata });
case actionTypes.SET_TECHNICIAN_NAME:
return updateObject(state, { name: action.name });
##### Others Not Shown But Simular #####
=======================index.js:=====================
export {
setTechnicianId,
setGetData,
setTechnicianName,
setTechnicianCerts,
setTechnicianPhone,
setTechnicianEmail,
setTechnicianServicebay,
} from './actions';
===========Part of the TechnicianEditForm.js============
const mapStateToProps = state => {
return {
selectedTechnicianId: state.selectedTechnicianId,
getdata: state.getdata,
name: state.name,
certifications: state.certifications,
phone: state.phone,
email: state.email,
servicebay: state.servicebay
};
};
const mapDispatchToProps = dispatch => {
return {
onSetTechnicianId: (selectedTechnicianId) => dispatch(actionTypes.setTechnicianId(selectedTechnicianId)),
onSetGetData: (getdata) => dispatch(actionTypes.setGetData(getdata)),
onSetTechnicianName: (name) => dispatch(actionTypes.setTechnicianName(name)),
onSetTechnicianCerts: (certifications) => dispatch(actionTypes.setTechnicianCerts(certifications)),
onSetTechnicianPhone: (phone) => dispatch(actionTypes.setTechnicianPhone(phone)),
onSetTechnicianEmail: (email) => dispatch(actionTypes.setTechnicianEmail(email)),
onSetTechnicianServicebay: (servicebay) => dispatch(actionTypes.setTechnicianServicebay(servicebay)),
}
};
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(TechnicianEditForm), axios);

Action.type undefined error in Redux Reducer

I'm not sure why I'm forced to do a check if actions exists in my reducer. Could it be because we are using async await in our actions / API methods?
Reducer
export const partyReducer = (state = initState, action) => {
if (action) { // <-- should not need this
switch (action.type) {
case Actions.SET_ROLES: {
const roles = formatRoles(action.roles);
return {
...state,
roles
};
}
default:
return state;
}
}
return state;
};
export default partyReducer;
Actions
import {getRoles} from '../shared/services/api';
export const Actions = {
SET_ROLES: 'SET_ROLES'
};
export const fetchRoles = () => async dispatch => {
try {
const response = await getRoles();
const roles = response.data;
dispatch({
type: Actions.SET_ROLES,
roles
});
} catch (error) {
dispatch({
type: Actions.SET_ROLES,
roles: []
});
}
};
Component that dispatches the action:
componentDidMount() {
this.props.fetchRoles();
this.onSubmit = this.onSubmit.bind(this);
}
...
export const mapDispatchToProps = dispatch => {
return {
fetchRoles: () => {
dispatch(fetchRoles());
}
};
};
The Store
import {createStore, combineReducers, applyMiddleware, compose} from 'redux';
import thunk from 'redux-thunk';
import {reducer as formReducer} from 'redux-form';
// Reducers
import partyReducer from '../reducers/party-reducer';
export default function configureStore(initialState) {
let reducer = combineReducers({
form: formReducer,
party: partyReducer
});
let enhancements = [applyMiddleware(thunk)];
if (process.env.PROD_ENV !== 'production' && typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__) {
enhancements.push(window.__REDUX_DEVTOOLS_EXTENSION__());
}
return createStore(reducer, initialState, compose(...enhancements));
}
What I've tried
I noticed my mapDispatchToProps was written kinda strange so I fixed that, but I still get the error actions is undefined if I remove the if statement :'(
import {fetchRoles as fetchRolesAction} from '../../../actions/party-actions';
...
export const mapDispatchToProps = dispatch => ({
fetchRoles: () => dispatch(fetchRolesAction())
});
Figured it out! Was my test!
it('returns expected initState', () => {
let expected = {roles: []};
let actual = partyReducer();
expect(actual).toEqual(expected);
});
^ test above is suppose to see if the initial state is return if no state is passed in. However Actions should Always be passed in.
Fix:
it('returns expected initState', () => {
let expected = {roles: []};
let actual = partyReducer(undefined, {}); // <-- undefined state, + action
expect(actual).toEqual(expected);
});

react redux UI not updating after store change

Im relatively new to React and Redux, and I created a simple ajax Email form for learning. The issue i'm having is that after form submission I set the store state back to Initialstate, which should clear all fields but it doesn't. I can see the store changes in redux logger, *see image attached but these changes are not showing on the ui. Is my store not mapping to state correctly? Or am I mutating state somewhere?
My reducer looks like the following:
export default function contactForm(state = initialState.formValues, action) {
switch (action.type) {
case types.FORM_RESET:
return initialState.formValues;
case types.FORM_SUBMIT_SUCCESS:
return Object.assign({}, action.message);
default:
return state;
}
}
Combine Reducers:
import { combineReducers } from 'redux';
import message from './formReducer';
import ajaxCallsInProgress from './ajaxStatusReducer';
const rootReducer = combineReducers({
message,
ajaxCallsInProgress
});
My initialstate looks like:
export default {
formValues: {
name: '', email: '', message: '',
},
ajaxCallsInProgress: 0,
};
My Actions Look like this:
export function messageSuccess(message) {
return { type: types.FORM_SUBMIT_SUCCESS, message };
}
export function resetForm() {
return { type: types.FORM_RESET };
}
export function saveMessage(message) {
return function (dispatch) {
dispatch(beginAjaxCall());
return messageApi.saveMessage(message)
.then(() => {
dispatch(messageSuccess(message));
dispatch(resetForm());
}).catch((error) => {
dispatch(ajaxCallError(error));
throw (error);
});
}
}
In the view I am mapping state to props via:
constructor(props, context) {
super(props, context);
this.state = {
message: Object.assign({}, this.props.message),
}
}
render() {
return (
<ContactForm
onChange={this.updateMessageState}
onSubmit={this.submitForm}
message={this.state.message}
/>
);
}
function mapStateToProps(state) {
return {
message: state.message,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(formActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(ContactSection);
Log showing store changes
I would be very grateful to any advice.
I've updated my answer with the code that I think should work for your example. You were pretty close, however based on your comments on trying to combine two reducers, I've created two reducers so you can see how it works.
/* constants.js */
export default {
FORM_RESET: 'FORM_RESET',
FORM_SUBMIT: 'FORM_SUBMIT',
AJAX_REQUEST: 'AJAX_REQUEST'
};
/* form-values-reducer.js */
const initialState = {
name: '',
email: '',
message: ''
};
export default const formValuesReducer = (state = initialState, action) => {
switch (action.type) {
case Constants.FORM_SUBMIT:
return {
...state,
message: action.message
};
case Constants.FORM_RESET:
return {
..state,
name: '',
email: '',
message: ''
};
default:
return state;
}
};
/* ajax-request-reducer.js */
const initialState = {
ajaxRequestCount: 0
};
export default const ajaxRequestReducer = (state = initialState, action) => {
switch (action.type) {
case Constants.AJAX_REQUEST:
return {
...state,
ajaxRequestCount: state.ajaxRequestCount + 1
};
default:
return state;
}
};
/* action-creators.js */
export const resettedForm = () => {
return {
type: Constants.FORM_RESET
}
};
export const submittedForm = (message) => {
return {
type: Constants.FORM_SUBMIT,
message
}
};
export const ajaxRequested = () => {
return {
type: Constants.AJAX_REQUEST
}
};
/* actions */
export const resetForm = (dispatch) => {
return () => {
dispatch(resettedForm());
}
};
export const submitForm = (dispatch) => {
return (message) => {
dispatch(ajaxRequested());
dispatch(submittedForm(message));
}
};
/* reducers.js */
import { combineReducers } from 'redux';
import ajaxRequest from './ajax-request-reducer';
import formValues from './form-values-reducer';
export default combineReducers({
ajaxRequest,
formValues
});
/* Component */
import React from 'react';
import { connect } from 'react-redux';
import { resetForm, submitForm } from './actions';
const App = (props) => (
<div>Your app UI stuff goes here</div>
);
const mapStateToProps = (state) => {
return {
name: state.formValues.name,
email: state.formValues.email,
message: state.formValues.message,
ajaxRequestCount: state.ajaxRequest.ajaxRequestCount
};
};
const mapDispatchToProps = (dispatch) => {
return {
resetForm: resetForm(dispatch),
submitForm: submitForm(dispatch)
}
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
I've not run this through anything, so there may be some mistakes in the code here and there.
I added the following which updated the state. I'm not sure if this is best practise with Redux, but it worked
componentWillReceiveProps(nextProps) {
this.setState({ message: nextProps.mail });
}

Categories

Resources