I came across these reducers in the codebase I am working on at my job.
const ACTION_HANDLERS = {
[LOGIN_REQUEST]: (state, action) => ({
...state,
isAuthenticating: true
}),
[LOGIN_SUCCESS]: (state, action) => ({
...state,
isAuthenticating: false,
isAuthenticated: true,
userId: action.userId,
authToken: action.auth,
authTTL: action.ttl,
authCreatedAt: action.created,
isNewUser: action.isNewUserFlag
}),
};
export default function authReducer(state = initialAuthState, action) {
const handler = ACTION_HANDLERS[action.type];
if(handler!==undefined){
console.log('login handler',handler);
// debugger;
}
return handler ? handler(state, action) : state;
}
My concern is related to how to debug this method of pre-written reducers.
I introduced console logs before every [] in ACTION_HANDLERS but they are syntactically wrong.
I had written reducers before and they were like this.
export default function FundsReducer(state=INITIAL_STATE,action={}){
switch(action.type){
case GET_ALL_FUNDS_FAILED:{
return{
...state,
funds:{
...state.funds,
failed:true,
pending:false,
}
};
}
case GET_ALL_FUNDS_PENDING:
{
let {options}=action.payload;
return{
...state,
funds:{
...state.funds,
data:[],
failed:null,
pending:true,
}
};
}
case GET_ALL_FUNDS:
{
let data;
data=action.payload.response.data;
return{
...state,
funds:{
...state.funds,
data,
pending:false,
}
}
}
I am having difficulty in debugging these reducers and introducing console logs .
You can use redux middleware as mentioned by #remix23 or just change your action as below so you will able to log state or action.
[LOGIN_REQUEST]: (state, action) => {
console.log(action);
return {
...state,
isAuthenticating: true
}
}
Hope this will help you.
Related
I am trying to fetch response from this api with redux thunks via three actions: 'fetchProductsPending, fetchProductsSuccess, fetchProductsError'
here's the fetch request:
function fetchProducts() {
return dispatch => {
dispatch(fetchProductsPending());
fetch('https://api.spacexdata.com/v3/launches')
.then(res => res.json()
)
.then(
res => {
if(res.error) {
throw(res.error);
}
dispatch(fetchProductsSuccess(res.products));
return res.products;
})
.catch(error => {
dispatch(fetchProductsError(error));
})
}
}
export default fetchProducts;
I initialized the state as empty array:
const initialState = {
pending: false,
products: [],
error: null
}
const middlewares = [thunk];
export const store=createStore(rootReducer, initialState, applyMiddleware(...middlewares));
and here's the reducer:
export function productsReducer(state = initialState, action) {
switch(action.type) {
case FETCH_PRODUCTS_PENDING:
return {
...state,
pending: true
}
case FETCH_PRODUCTS_SUCCESS:
return {
...state,
pending: false,
products: action.products
}
case FETCH_PRODUCTS_ERROR:
return {
...state,
pending: false,
error: action.error
}
default:
return state;
}
}
here's a sandbox for the problem:
https://codesandbox.io/s/polished-sunset-wxefc?file=/src/style.js
Your selectors are wrong.
You are selecting state.products, but your rootReducer mounts the productReducer at state.productData.products, so you'd have to select that instead.
Generally: this is a very outdated style of redux that we mostly encouraging to learn for "what is going on under the hood", but not for really writing apps with any more, as it leads to a lot of unnecessary code. So you might be following a very outdated tutorial. For a more up-to-date apporach, please take a look at the official redux tutorials
can we pass the value directly to the state in redux reducer like
export default (state = [], action) => {
switch (action.type) {
case 'FETCH_USER':
return [...state, action.payload];
default:
return state;
}
}
here i added action payload value to state , i was wondering is it safe to do that ?
if i only return acion.payload , i would get a error that says
state.users.find is not a function
here is mapStateToProps function
const mapStateToProps = (state, ownProps) => {
return { user: state.users.find(user => user.id === ownProps.userId) };
}
what is the best practice?
if you need more information please let me know
You should define an initial state in your reducer like:
const initialState = {
users: []
}
Then initialize the state with the initial state, and update it with your payload:
export default (state = initialState, action) => {
switch (action.type) {
case 'FETCH_USER':
return {...state, users: action.payload};
default:
return state;
}
}
In my code, action.payload points to a todo.index, so my REMOVE_TODO case should filter out a todo if it has the same index as the one the event was fired on. However, I get syntax errors all over. I have messed with my wrappings and even tried rewriting it from scratch but cannot get these syntax errors away. Does someone see something I'm missing here??
Just let me know if any more information is needed please. Thank you all!
import { ADD_TODO } from '../constants/action-types';
import { REMOVE_TODO } from '../constants/action-types';
const initialState = {
todos: []
};
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_TODO:
return {
...state,
todos: [...state.todos, action.payload]
}
case REMOVE_TODO:
return {
...state,
todos: [...state.todos.filter((todo, i) => {i !== action.payload})]
}
default:
return state;
}
}
export default rootReducer;
Well you missed returning a value inside filter function.
Have a look at the demo.
const initialState = {
todos: [1,2,3,7,8,5]
};
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: [...state.todos, action.payload]
}
case 'REMOVE_TODO':
return {
...state,
todos: [...state.todos.filter((todo, i) => i !== action.payload)]
}
default:
return state;
}
}
let op = rootReducer({todos:[1,2,3,4,5,6]},{type:'REMOVE_TODO',payload:1});
console.log(op)
Turns out I was simply returning an object from my filter method. todos: [...state.todos.filter((todo, i) => {i !== action.payload})]
Just took away the curly brackets from the return on the arrow function and it's all good now.
I want to create a Redux store that has this shape:
store = {
loaded: Boolean,
loading: Boolean,
view: Object, // uses combineReducers
layers: Object // uses combineReducers
}
So far, my root reducer looks like this:
rootReducer.js
import view from './view';
import layers from './layers';
const initialState = {
loaded: false,
loading: false,
};
function loadState(state = initialState, action = {}) {
switch (action.type) {
case 'LOADED':
return {
...state,
loaded: true,
loading: false,
};
case 'LOADING':
return {
...state,
loaded: false,
loading: true,
};
default:
return state;
}
}
export default combineReducers({
view,
layers,
// hmmmm, putting loadState here would give me a loadState object property,
// not loose 'loaded' and 'loading' properties
});
How do I also have these "loose" properties like loaded and loading alongside them?
Sometimes I find writing individual reducers for a few simple properties to be obnoxious overhead, so I have a combineReducersWithRoot utility I use sometimes.
export function combineReducersWithRoot(rootReducer, reducers) {
return (state, action) => {
// Ensure the root state object is a new object; otherwise
// React may not re-render.
let newState = {...rootReducer(state, action)};
Object.keys(reducers).forEach(domain => {
let obj = state ? state[domain] : undefined;
newState[domain] = reducers[domain](obj, action);
});
return newState;
};
}
Now, given a state structure something like this:
{
loading: bool
loaded: bool
data: {
filter: string
arr: object[]
}
}
You can do this:
function rootReducer(state = {loading: false, loaded: false}, action) {
switch(action.type) {
case STARTED_LOADING:
return {...state, loading: true, loaded: false};
case FINISHED_LOADING:
return {...state, loading: false, loaded: true};
default:
return state;
}
}
function dataReducer(state = {filter: '', arr: []}, action) {
switch (action.type) {
case SET_FILTER:
return {...state, filter: action.value};
case SET_DATA:
return {...state, arr: action.arr};
default:
return state;
}
}
export default combineReducersWithRoot(rootReducer, {data: dataReducer});
#PhiNguyen is right, I need to turn these loaded/loading properties into their own reducers!
import { LOADED, LOADING } from '../ActionTypes';
export function loaded(state = false, action = {}) {
switch (action.type) {
case LOADED:
return true;
case LOADING:
return false;
default:
return state;
}
}
export function loading(state = false, action = {}) {
switch (action.type) {
case LOADED:
return false;
case LOADING:
return true;
default:
return state;
}
}
rootReducer.js
import { loaded, loading } from './load';
import view from './view';
import layers from './layers';
export default combineReducers({
loaded,
loading,
view,
layers
});
I'm having surprisingly difficult time figuring this out, essentially I'm trying to set state to initial state, so far I tried:
// -- Initial state ------------------------------------------------------------
const INITIAL_STATE = {
search: {
listings: []
},
listings: []
}
// -- Story structure for story editor -----------------------------------------
export default function(state = INITIAL_STATE, action) {
switch(action.type) {
case ACTIONS.RESET_STATE:
return { ...state, INITIAL_STATE }
default:
return state;
}
}
this just adds initial state to existing one
case ACTIONS.RESET_STATE:
return { ...state, state = INITIAL_STATE }
this returns error
case ACTIONS.RESET_STATE:
return { ...state, state: INITIAL_STATE }
this is adding initial state to existing one gain
case ACTIONS.RESET_STATE:
return { ...state, search: { listings:[] }, listings: [] }
This works, but I start getting weird mutation errors.
The proposed solution of Anders is right, but has potential problem with immutables. This generates always new object.
case ACTIONS.RESET_STATE:
return { ...INITIAL_STATE };
Look at Jiri Fornous solution instead, as this will mutate your data.
An even easier way is to just return INITIAL_STATE.
case ACTIONS.RESET_STATE:
return INITIAL_STATE;
If you simply want to reset state completely, just return the value of INITIAL_STATE:
export default function(state = INITIAL_STATE, action) {
switch(action.type) {
case ACTIONS.RESET_STATE:
return {
search: {
listings: []
},
listings: []
};
default:
return state;
}
}
If you want to keep the INITIAL_STATE in a single place. Change the initial state creator to a function:
function get_INITIAL_STATE => {
return { search: {
listings: []
},
listings: []
}
}
export default function(state = get_INITIAL_STATE(), action) {
switch(action.type) {
case ACTIONS.RESET_STATE:
return get_INITIAL_STATE();
default:
return state;
}
}