I defined a reducer but it always returns NaN value instead of number.I defined a single store in store.js like createStore(reducer,{},applyMiddleWire(). Reducer is working but always return NaN value.
import { INCREMENT, DECREMENT } from "./buttonAction";
const initialState = {
counter: 1
};
const Count = (state = initialState, action) => {
switch (action.type) {
case "INCREMENT":
state = {
...state,
counter: state.counter + 1
};
console.log("Number", state);
break;
case "DECREMENT":
state = {
...state,
counter: state.counter + 1
};
console.log("Number", state);
break;
default:
}
return state;
};
export default Count;
You need to return the new state in SWITCH cases.
The point is that Redux state is not like React state.
When you changing React state, setState() change only specific part of state, while other state content remains untouched.
On the other hand, Redux wants you to always return a new state, a new object. Redux does not merge old state and new state like React do. So the answer is you need to create new object in every switch case and return it, and Redux will replace old state with new one.
switch (action.type) {
case "INCREMENT":
return {
...state,
counter: state.counter + 1
};
console.log("Number", state);
break;
I was looking for the same mistake, I think maybe your problem as others said is because payload is not defined into the Component(method-properties).
I solved and learn a little bit else with this mistake about lifecycle of redux.
https://codesandbox.io/s/react-redux-counter-03-xjqnq
increment = () => {
// this.setState({ count: this.state.count + 1 });
this.props.dispatch({ type: "INCREMENT", payload: 5 });
};
decrement = () => {
// this.setState({ count: this.state.count - 1 });
this.props.dispatch({ type: "DECREMENT", payload: 3 });
}; enter code here
Related
The task is to create a reducer function to handle multiple authentication actions. Use a JavaScript switch statement in the reducer to respond to different action events. This is a standard pattern in writing Redux reducers. The switch statement should switch over action.type and return the appropriate authentication state.
There are two approaches that seem same to me. In fact, I feel method 2 is better than method 1 as it actually updates the state. However freecodecamp seems to believe otherwise. Can someone tell me what is the difference between the 2.
Method 1 - Create new objects
const defaultState = {
authenticated: false
};
const authReducer = (state = defaultState, action) => {
// change code below this line
switch (action.type) {
case "LOGIN":
return {
authenticated: true
};
case "LOGOUT":
return {
authenticated: false
};
default:
return defaultState;
}
// change code above this line
};
const store = Redux.createStore(authReducer);
const loginUser = () => {
return {
type: "LOGIN"
};
};
const logoutUser = () => {
return {
type: "LOGOUT"
};
};
Method 2 : Update existing object
const defaultState = {
authenticated: false
};
const authReducer = (state = defaultState, action) => {
// Change code below this line
switch(action){
case 'loginUser':
state.authenticated = true;
return state;
case 'logoutUser':
state.authenticated = false;
return state;
default:
return state;
}
// Change code above this line
};
const store = Redux.createStore(authReducer);
const loginUser = () => {
return {
type: 'LOGIN'
}
};
const logoutUser = () => {
return {
type: 'LOGOUT'
}
};
The error message that I get in Method 2 is as follows
Error Log
In order to decrease code duplication in the redux-related part of my app, I have come up with reducer factories in order to group repetative logic into an abstract reducer which is then turned into concrete reducer instances by passing name param to the factory.
ReduxFactory/reducer.js
const initialState = {
foo: null
}
function reducerFactory(name = '') {
return function reducer(state = initialState, action) {
switch (action.type) {
case `${name}_DO_SOMETHING`: {
return state
}
default:
return state;
}
}
}
export default reducerFactory;
Then I have two modules for which I build instances of a reducer factory:
fileA.js
import reducerFactory from './ReduxFactory/reducer'
const reducer = reducerFactory('OBJECT_A')
fileB.js
import reducerFactory from './ReduxFactory/reducer'
const reducer = reducerFactory('OBJECT_B')
Now imagine that on objects of type B I need to implement some ad-hoc functionality, which I do not want to include into the general reducer factory body because it is too specialized. Is there any valid JS code pattern to implement this ?
If I understand you correctly I think you can do the following:
function reducerFactory(name = '', extras = (x) => x) {
return function reducer(state = initialState, action) {
switch (action.type) {
case `${name}_DO_SOMETHING`: {
return state;
}
default:
return extras(state, action);
}
};
}
const reducer = reducerFactory(
'OBJECT_B',
(state, action) => {
if (action.type === 'extra') {
//return changed state
}
return state;
}
);
I am just trying to run a simple redux program when i use command node index it shows me error that action must be plain objects below is my code for that
const redux = require('redux')
const createStore = redux.createStore
const BUY_CAKE = 'BUY_CAKE'
function buyCake () {
return
{
type: BUY_CAKE
}
}
const initialState = {
numOfCakes: 10
}
const reducer = (state = initialState, action) => {
switch (action.type) {
case BUY_CAKE: return {
...state,
numOfCakes: state.numOfCakes - 1
}
default: return state
}
}
const store = createStore(reducer)
console.log("initial state is ", store.getState())
const unsubscribe = store.subscribe(() => console.log("updated", store.getState()))
store.dispatch(buyCake())
store.dispatch(buyCake())
store.dispatch(buyCake())
store.dispatch(buyCake())
unsubscribe()
when i dispatch(buyCake()) then only it shows error but if i do store.dispatch({type:BUY_CAKE}) then code runs fine why is the error occuring
Because your return statement is wrongly formatted. Be aware of this deadly feature:
JavaScript will automatically insert semicolons. Without the parentheses, JavaScript would ignore the following lines and return without a value.
This is your function with semicolons, which will return undefined:
function buyCake(){
return;
{
type:BUY_CAKE
};
};
Solution: Move your curly brackets to the return line:
function buyCake(){
return {
type: BUY_CAKE
}
}
Below are my action and reducer files - In my component state I am only seeing this.props.mainData - but others subdataOneData etc., are not being loaded in to the state - till reducer i see the right actions are being dispatched and I also see the data for sub - calls - but they are not reaching my component - I have mapStatetoprops - where I am doing
New issue: as per the updated code - when i print out payload in reducer - I see maindata with the api data but SubData [{}, {}, {}] ..?
Updated code:
import { GET_DATA_AND_SUBDATA } from '../constants/types';
export function getMainData() {
return async function getMainData(dispatch) {
const { data } = await getMainDataAPI();
const subData = data.map((item) => {
const endpoint = 'build with item.name';
return Request.get(endpoint);
});
console.log('subddd' + subData); prints -> **[object Promise],[object Promise],[object Promise]**
dispatch({
type: GET_DATA_AND_SUBDATA,
payload: { data, subData }
});
};
}
async function getMainDataAPI() {
const endpoint = 'url';
return Request.get(endpoint);
}
The problem lies on the way you dispatch the actions.
You are not providing data for mainData and subdataOneData at the same time.
export function getData() {
return async function getData(dispatch) {
const { data } = await getDataAPI();
// This will cause first re-render
dispatch({ type: GET_DATA, payload: data });
Object.keys(data).map((keyName, keyIndex) => {
const endpoint = 'ENDPOINT';
Request.get(endpoint).then((response) => {
// This will cause second re-render
dispatch({
type: GET_subdata + keyIndex,
payload: response.data });
});
return keyIndex;
});
};
}
At first render your subdataOneData is not availble yet.
You are not even specifying a default value in the reducer, therefore it will be undefined.
You can change your action thunk like this
export function getData() {
return async function getData(dispatch) {
const { data } = await getDataAPI();
const subDataResponse = await Promise.all(
Object.keys(data).map( () => {
const endpoint = 'ENDPOINT';
return Request.get(endpoint)
})
)
const subData = subDataResponse.map( response => response.data )
dispatch({
type: GET_DATA_AND_SUBDATA
payload: { data, subData }
});
};
}
And change your reducer accordingly in order to set all data at once.
export default function myReducer(state = {}, action) {
switch (action.type) {
case GET_DATA_AND_SUBDATA:
return {
...state,
mainData: action.payload.data,
subdataOneData: action.payload.subData[0],
subdataTwoData: action.payload.subData[1]
};
default:
return state;
}
}
Note: it's also a good practice to set your initial state in the reducer.
const initialState = {
mainData: // SET YOUR INITIAL DATA
subdataOneData: // SET YOUR INITIAL DATA
subdataTwoData: // SET YOUR INITIAL DATA
}
export default function myReducer(initialState, action) {
My question relates to redux and more specifically how to handle errors/failures from within reducer functions. I am in reference to the ngrx example app (https://github.com/ngrx/example-app) and the way it handle errors/failures.
Here is the reducer function I am referring to:
export function reducer(state = initialState, action: collection.Actions): State {
switch (action.type) {
case collection.ActionTypes.LOAD: {
return Object.assign({}, state, {
loading: true
});
}
case collection.ActionTypes.LOAD_SUCCESS: {
const books = action.payload;
return {
loaded: true,
loading: false,
ids: books.map(book => book.id)
};
}
case collection.ActionTypes.ADD_BOOK_SUCCESS:
case collection.ActionTypes.REMOVE_BOOK_FAIL: {
const book = action.payload;
if (state.ids.indexOf(book.id) > -1) {
return state;
}
return Object.assign({}, state, {
ids: [ ...state.ids, book.id ]
});
}
case collection.ActionTypes.REMOVE_BOOK_SUCCESS:
case collection.ActionTypes.ADD_BOOK_FAIL: {
const book = action.payload;
return Object.assign({}, state, {
ids: state.ids.filter(id => id !== book.id)
});
}
default: {
return state;
}
}
}
Can someone please explain the necessity for dealing with those two actions from within the reducer function:
REMOVE_BOOK_FAIL
ADD_BOOK_FAIL
For instance why remove the book from the state (in the case of the ADD_BOOK_FAIL action)?
If the add book action has failed, then the book is not present in the store. Is it?
Maybe it's the naming used that makes it a red herring, my guess is that ADD_BOOK_FAIL could be in use somewhere else for a different use case as a fall back mechanism.
I agree the way you describe it this doesnt make sense the developer did it for this reason.