I'm new to Redux and having a bit of trouble with it, so what I've done so far might very well be wrong, but bear with me. Overall, I want a very simple redux state for my app, with only two keys, "user" and "landing". The way I have everything set up is defaulting the state to "user: {user: null}", and "landing: {landing: true}". I'm wondering why it's duplicated like that, and it's not just "user: null" and "landing: true"?
My reducers:
const initialUserState = {
user: null
};
const initialUiState = {
landing: true
};
export function userReducer(state, action){
if (typeof state === "undefined"){
return initialUserState;
}
switch(action.type){
case SIGN_IN:
return {...state, user: action.userName};
case SIGN_OUT:
return {...state, user: null};
default:
return state;
}
}
export function uiReducer(state, action){
if (typeof state === "undefined"){
return initialUiState;
}
switch(action.type){
case LANDING:
return {...state, landing: true};
case NOT_LANDING:
return {...state, landing: false};
default:
return state;
}
}
and the combination of the reducers:
import {userReducer, uiReducer} from "./reducers";
import {combineReducers} from "redux";
export default combineReducers({
user: userReducer,
landing: uiReducer
});
Again, I'm hoping to see just "user: null" and "landing: true", but I'm console logging out the state in a component that's wrapped in connect and I'm seeing "user: {user: null}" and "landing: {landing: true}".
The first hierarchy level in the state is defined by the name of the reducers in the call to combineReducers. This is necessary to separate the sub-state that each single reducer is responsible for. The structure of the state inside the first hierarchy level is then completely defined by the corresponding reducers.
In your case, if you really want to manage only a single value, you could define your reducers as follows:
const initialUserState = null;
export function userReducer(state, action){
if (typeof state === "undefined"){
return initialUserState;
}
switch(action.type){
case SIGN_IN:
return action.userName;
case SIGN_OUT:
return null;
default:
return state;
}
}
And similarly for the uiReducer.
But this would be rather uncommon and won't scale well if you need to add further attributes in the future.
Related
I am attempting to have default information in my User reducer and then add my Firebase login response to that default information. For example:
let userDefaultState = {startingNumber: 0}
const user = (state = userDefaultState, action) => {
switch (action.type) {
case 'RESET_USER':
return userDefaultState
case 'LOGIN':
return action.payload
case 'SET_MY_FEED':
return { ...state, myFeed: action.payload }
case 'UPDATE_FEED':
return { ...state, myFeed: [action.payload, ...state.myFeed]}
default:
return state
}
}
However, when I dispatch 'LOGIN', it fires Firebase.db.collection('user').doc(uid).get() and overwrites the current state with the response (ie payload) instead of adding it to the existing object. I believe it is because of the way the 'LOGIN' case is set up.
The following is incorrect but I think it illustrates what I'm trying to do.
return { ...state, [action.payload, ...state]}
Desired outcome would be something like this:
user = {
//Firebase response
"username": "name", "email": "email#email.com",
//userDefaultState
"startingNumber": 0
}
Thanks for any help!!
why state is returned empty
i had tried many solutions from this website but non of them worked
const initialState = [];
export default function(state = initialState, actions) {
const { type, payload } = actions;
switch (type) {
case DASHBOARD:
return [...state,payload]
default:
return state;
}
}
when this state is mapped to props , payload become undefined.
here when i console log action.payload it is defined but could not be passed in return statement.
May I know for which case it failing?
For Dashboard case,it will be "DASHBOARD" until and unless it is not constant.
And for your default case,you need to change your initial state like this:
const initialState = {payload:"your initial payload",your_other_properties:"here"};
export default function(state = initialState, actions) {
const { type, payload } = actions;
switch (type) {
case "DASHBOARD":
return [...state,payload]
default:
return state;
}
}
Using redux, I have a bunch of actions and a bunch of reducers that coincide with each of the action types.
Each of the actions map to a different part of the state being updated (all the action creators are primarily for fetching data from an API, for example, which maps to some part of the state). Our reducers currently look rather silly (simplified to A, B, C, for the sake of example):
export const rootReducer = combineReducers({
somePartOfStateA: someAReducer,
somePartOfStateB: someBReducer,
somePartOfStateC: someCReducer,
... (and many other reducers)
});
function someAReducer(state = [], action) {
switch (action.type) {
case FETCH_SOME_A:
return action.payload;
default:
return state;
}
}
function someBReducer(state = [], action) {
switch (action.type) {
case FETCH_SOME_B:
return action.payload;
default:
return state;
}
}
function someCReducer(state = [], action) {
switch (action.type) {
case FETCH_SOME_C:
return action.payload;
default:
return state;
}
}
// and a bunch of pretty much identical reducers continue below
From my understanding, the purpose of having them split is so that each reducer handles a part of the state's namespaces. The resulting reducers are simple, but pretty much the same thing over and over. Is there a recommended way to consolidate all of these reducers per piece of state?
Reducer is just a function. You could use higher order function to make reducer for you.
const makeSingleActionReducer = type => (state, action) =>
action.type === type ? action.payload : state
export const rootReducer = combineReducers({
somePartOfStateA: makeSingleActionReducer(FETCH_SOME_B)
...
})
Also you could go further by creating a config {stateKey: actionType, ...} and loop over it.
I have a redux app that, amongst other things, updates a single string, hence the state could be reflected as:
{
theDataString: "someString",
otherData: { ...some other data... },
someListItems: [ ...a data array... ]
}
Hence I have the following reducer:
function updateDataString(state = {}, action) {
switch (action.type) {
case UPDATE_DATA_STRING:
return Object.assign({}, ...state, {theDataString: action.theDataString});
default:
return state;
}
}
However, when dispatch the UPDATE_DATA_STRING action, the theDataString value in the state becomes double nested:
{
theDataString: {theDataString: "someString"},
otherData: {... some other data... },
someListItems: [ ... a data array ... ]
}
This same problem has already been encountered here. However, the solution for them was that they were calling combineReducers on a single reducer when that was unnecessary. In my case, I'm calling combineReducers on multiple reducers - so their answer doesn't work for me. Also note that the same nesting problem does not occur for other data, only the top-level string gets double nested.
What is going wrong here?
EDIT:
I'm connecting the component that updates theDataString like this:
import {connect} from 'react-redux';
import {updateDataString} from './actions/actions';
import SomeList from './components/someList';
const mapStateToProps = (state) => {
return {someListItems: state.someListItems}
};
const mapDispatchToProps = (dispatch, placeHolder) => {
return {
updateDataString: (aString) => dispatch(updateDataSting(aString))
}
};
export default SomeListConnected = connect(mapStateToProps, mapDispatchToProps)(SomeList)
The action is set up as follows:
export const UPDATE_DATA_STRING = 'UPDATE_DATA_STRING';
export function updateDataString(aString) {
return {type: UPDATE_DATA_STRING, theDataString: aString}
}
EDIT2: Changing the way the reducer updates the state is a natural place to look for answers. However I have tried various permutations here with little effect:
Object.assign({}, ...state, {theDataString: action.theDataString});
Object.assign({}, state, {theDataString: action.theDataString});
{...state, {theDataString: action.theDataString}};
None of the above fix the problem.
Assuming that you've used combineReducers, your updateDataString reducer should be treating the state as a string, not an object:
function updateDataString(state = "", action) {
switch(action.type) {
case UPDATE_DATA_STRING: return action.newString;
default; return state;
}
}
The slice reducer will only see the string value as its "state", so you need to treat it that way.
See Using combineReducers for some further information on the topic.
Try this
return { ...state, theDataString: action.newString};
instead of this
return Object.assign({}, ...state, {theDataString: action.newString});
in your reducer.
I think you are using ... spread operator in the wrong way. And no need to use Object.assign() here.
Read more about spread operator here
I have the following two #ngrx/store reducers:
import {ActionReducer, Action} from '#ngrx/store';
import {UserAccount} from '../shared/models/useraccount.model';
export const SET_CURRENT_USER_ACCOUNT = 'SET_CURRENT_USER_ACCOUNT';
export const UPDATE_CURRENT_USER_ACCOUNT_FIRST_NAME = 'UPDATE_CURRENT_USER_ACCOUNT_FIRST_NAME';
export const currentUserAccountReducer: ActionReducer<UserAccount> = (state: UserAccount, action: Action) => {
console.log('currentUserAccountReducer:', state, action);
switch (action.type) {
case SET_CURRENT_USER_ACCOUNT: {
return action.payload;
}
case UPDATE_CURRENT_USER_ACCOUNT_FIRST_NAME: {
state.firstName = action.payload;
return state;
}
}
};
export const SET_AUTHENTICATED = 'SET_AUTHENTICATED';
export const SET_UNAUTHENTICATED = 'SET_UNAUTHENTICATED';
export const authenticatedReducer: ActionReducer<boolean> = (state: boolean, action: Action) => {
console.log('authenticatedReducer:', state, action);
switch (action.type) {
case SET_AUTHENTICATED: {
return true;
}
case SET_UNAUTHENTICATED: {
return false;
}
}
};
However, for some reason when I issue a dispatch for the 1st reducer (i.e. currentUserAccountReducer) then it changes the state for the 2rd reducer (i.e. authenticatedReducer)...
Here is the dispatch causing this issue:
this.store.dispatch({type: SET_CURRENT_USER_ACCOUNT, payload: currentUserAccount});
Here is how I initialize the store in the imports section:
StoreModule.provideStore(
{
currentUserAccount: currentUserAccountReducer,
authenticated: authenticatedReducer
})
Can someone please provide advice?
edit: The issue is that authenticated ends up undefined!!
The switch statements in your reducers do not contain default cases. You need to add default cases that return the state, as the reducers will be called for all actions - the store has no way of knowing which reducer should be called for a particular action type, so each dispatched action is passed to every reducer.