Why we use spread operator into redux - javascript

I have seen the following code:
export default function productReducer(state = initialState, action) {
switch(action.type) {
case FETCH_PRODUCTS_BEGIN:
return {
...state,
loading: true,
error: null
};
case FETCH_PRODUCTS_SUCCESS:
return {
...state,
loading: false,
items: action.payload.products
};
case FETCH_PRODUCTS_FAILURE:
return {
...state,
loading: false,
error: action.payload.error,
items: []
};
default:
return state;
}
}
But don't understand why do we use ...state every time for each case.
Can't we just write:
return {
loading: false,
error: action.payload.error,
items: action.payload.products
};
Anyone can explain this?

Because commonly you want to keep other keys inside your state...
If your state has:
{
items:['a', 'b', 'c'],
loading: false,
error: null
}
and you only return for example:
case FETCH_PRODUCTS_BEGIN:
return {
// ...state, --> Without this line
loading: true,
error: null
};
Your new state will be
{
loading: true,
error: null
}
And your items will be lost.
Then, returning:
case FETCH_PRODUCTS_BEGIN:
return {
...state,
loading: true,
error: null
};
You are saying
Return a copy of state, but overriding loading and error keys"

This is for creating new copied state object with new or updated values (without this you would need manually specify every state field).
Object.assign can be used as alternative
Redux Docs has really good explanation about using spread operator.

Related

useReducer - Enforce only one true value without explicitly setting others false?

I'm looking for a smarter way to enforce
one value true, setting the others to false
This Python q&a suggests an enum
(I'm not familiar with either)
I can already
switch (action.type) {
case 'loadCurrent':
return {...state,
loadCurrent: true,
loadPrev: false, etc ...};
But this is a lot of boilerplate and intuition (as well as that linked python question) tell me there's a more elegant solution.
function reducer (state, action) {
switch (action.type) {
case 'loadCurrent':
return {...state, loadCurrent: true};
case 'fetchNew':
return {...state, fetchNew: true};
case 'loadPrevHook':
return {...state, loadPrevHook: true };
case 'loadNextHook':
return {...state, loadNextHook: true };
case 'loaded':
return {...state, loaded: true };
}
}
const initialState = {
loadCurrent: false,
fetchNew: false,
loadPrevHook: false,
loadNextHook: false,
loaded: true }
Having separate properties for these doesn't make the most sense. Put all the data into a single property instead - perhaps call it loadStage or whatever you think is appropriate.
If, as the code here suggests, the state doesn't have any other properties, just setting the action.type looks like it'd do.
function reducer (state, action) {
return { loadStage: action.type };
}
If you expanded the reducer to include other actions as well not related to the loading actions, you could make an array corresponding to the loading actions.
const loadStageActions = ['loadCurrent', 'fetchNew', 'loadPrevHook', 'loadNextHook', 'loaded'];
function reducer (state, action) {
if (loadStageActions.includes(action.type)) {
return { ...state, loadStage: action.type };
}
// implement other logic here
}
Then, in the rest of your code, just check the one string property instead of the multiple booleans. Eg instead of doing state.loadCurrent, do state.loadStage === 'loadCurrent'.

does not exist on type 'string'.ts(2339)

Im new to typescript, here i have been adding ts to my react project, i'm getting this kind of error: Property 'identifier' does not exist on type 'string'.ts(2339), the code works as it is but when added ts it gives that error, i can solve it like this: currentBroker: "" as any, but then thats not the right way.
any help/suggestion ?
my code:
const initialSelectionState = {
currentBroker: "",
};
export function selectionReducer(
state = initialSelectionState,
action: selectionReducerAction
) {
switch (action.type) {
case ActionType.CHANGE_SITE:
return {
...state,
currentSite: action.payload,
};
case ActionType.CHANGE_CAMERA:
return {
...state,
currentCamera: action.payload,
};
case ActionType.CHANGE_ANALYSER:
return {
...state,
currentAnalyser: action.payload,
};
case ActionType.CHANGE_PLATFORM:
return {
...state,
currentPlatform: action.payload,
};
case ActionType.CHANGE_BROKER:
return {
...state,
currentBroker: action.payload,
};
case ActionType.CLEAR_ANALYSER_DATA:
return {
...state,
currentAnalyser: "",
};
case ActionType.CLEAR_CAMERA_DATA:
return {
...state,
currentCamera: "",
};
case ActionType.CLEAR_PLATFORM_DATA:
return {
...state,
currentPlatform: "",
};
case ActionType.CLEAR_BROKER_DATA:
return {
...state,
currentBroker: "",
};
case ActionType.UPDATE_BROKER:
const ucurrentBroker =
state.currentBroker.identifier == action.payload.identifier
? action.payload
: state.currentBroker;
return { ...state, currentBroker: ucurrentBroker };
}
}
currentBroker is of type string, it's not an object, and typescript tells you that you cannot access properties because it doesn't have them.
If you are using typescript I suggest you to declare interface of your state first:
interface IState {
currentBroker: string;
}
const initialSelectionState: IState = {
currentBroker: "",
};
and of you need your broker to have an identifier property you can modify the state interface as you need:
interface IState {
currentBroker: {
identifier: string;
}
}
const initialSelectionState: IState = {
currentBroker: {
identifier: ""
};
};
With this you should not have the error again (if I understood correctly)
You initialise the "currentBroker" as a string.
const initialSelectionState = {
currentBroker: "",
};
Then later attempt to access the currentBroker as if it's an object with a property identifier
state.currentBroker.identifier == action.payload.identifier
Casting to any will allow the code to run, and most likely just return undefined when trying to get that property.
If all you are trying to do is update a string in a state object, surely no logic is required?
const initialSelectionState = {
currentBroker: "",
};
export function selectionReducer(
state = initialSelectionState,
action: selectionReducerAction
) {
switch (action.type) {
case ActionType.UPDATE_BROKER:
return { ...state, currentBroker: action.payload};
}
}
Since you mentioned the structure of the payload is as follows;
{
identifier: 'b9b6381e-2d42-4953-b747-4b25fd382a04',
name: 'Broker 244',
host: 'localhost',
port: 1783,
site_id: '0-0-0-0'
}
Then your currentBroker should in some way match the structure of this. Alternatively, if there is 'no current broker initially', you should make it undefined. In either case, it definitely shouldn't be an empty string i.e. "" since that is a totally different data type.

How to use an object property as a key inside of another object using react/redux

I am trying to set a part of the state inside a reducer but I need to access part of the initial state that is nested.
What I am trying to do:
return {
...state,
list: action.payload,
filter: action.params,
filter.totalPages : action.totalPages,
};
This however does not working, I have tried doing filter['totalPages'] and it also does not work. How and what is the best practice to do this?
You should use spread operator for filter object too
return {
...state,
list: action.payload,
filter: {
...action.params,
totalPages: action.totalPages
}
};
return {
...state,
list: action.payload,
filter: action.params,
[filter.totalPages] : action.totalPages,
};
For more check Computed property keys in es6
return Object.assign({}, state, {
...state,
{
list: action.payload,
filter: {
...action.params,
totalPages: action.totalPages
}
}
})
It's problably missing Object.assign check it out

immutable reducer in redux

Is this reducer looks fine? why the author used a let users;, that look unnecessary to me. Isn't that will cause 2 users in FETCH_USER_FULLFILLED case?
const initalState = {
users: [],
loading: false,
error: null,
};
// REDCUER
function usersReducer(state = initalState, action) {
let users;
switch (action.type) {
case 'FETCH_USER_PENDING':
return { ...state, loading: true };
case 'FETCH_USER_FULFILLED':
users = action.payload.data.results;
return { ...state, loading: false, users };
case 'FETCH_USER_REJECTED':
return { ...state, loading: false, error: `${action.payload.message}` };
default:
return state;
}
}
export default usersReducer;
The reducer is fine and it will not cause two users in FETCH_USER_FULLFILLED. However, you are right, there is no need for let users;. So the code will look like
case 'FETCH_USER_FULFILLED':
return { ...state, loading: false, users: action.payload.data.results };
When the reducer first executed, it will take its start state from initalState as it is the default if no param passed. So users will be empty array at the beginning and will be filled on FETCH_USER_FULLFILLED action
Edit users Added to take advantage of property value shorthand feature in ES6

Why my state is not not defined?

Below is es5 syntax
function customMsg(state, action) {
state = state || {};
return $.extend({}, state, {
isFetching: false,
didInvalidate: false,
checkStatus: checkStatus(state.checkStatus, action)
});
}
function checkStatus(state, action) {
state = state || {
isFetching: false,
didInvalidate: false,
type: "room"
};
return state;
}
}
Below is es6 syntax
const initialStae = {
isFetching: false,
didInvalidate: false,
checkStatus: checkStatus(state.checkStatus, action)
}
function customMsg(state = initialStae, action) {
return state;
}
function checkStatus(state, action) {
state = state || {
isFetching: false,
didInvalidate: false,
type: "room"
};
return state;
}
Why my line 7 will show "state is not defined" from es6?
customMsgReducer.js:36Uncaught ReferenceError: state is not defined(…)
In ES5, you define state = state || {}; and then accessing state.checkStatus. Hence its working.
function customMsg(state, action) {
state = state || {};
return $.extend({}, state, {
isFetching: false,
didInvalidate: false,
checkStatus: checkStatus(state.checkStatus, action)
});
}
But in ES6, you access state.checkStatus without defining the state which is undefined here. Hence accessing checkStatus of undefined is throwing error.

Categories

Resources