How does reducer works when state = initialState? - reactjs

A sample code from an online course:
import * as PlayerActionTypes from '../actiontypes/player';
const initialState = [
{
name: 'Jim Hoskins',
score: 31,
},
{
name: 'Andrew Chalkley',
score: 20,
},
{
name: 'Alena Holligan',
score: 50,
},
];
export default function Player(state=initialState, action) {
switch(action.type) {
case 'PlayerActionTypes.ADD_PLAYER':
return [
...state,
{
name: action.name,
score: 0,
}
];
case PlayerActionTypes.REMOVE_PLAYER:
return [
...state.slice(0, action.index),
...state.slice(action.index+1),
]
}
}
As the initialState is immutable(always the same), then the state is the same. So, let's say, if I first add an new player, a total of four players now. If then I want to remove the fourth player (whose index is 3 in the array), but how does it work?
case PlayerActionTypes.REMOVE_PLAYER:
return [
...state.slice(0, action.index),
...state.slice(action.index+1),
]
There is no index = 3 in the 'state' (only three players).
I don't understand. Please provide some help on my confusion. Thanks in advance.

export default function Player(state=initialState, action) {
whenever action is dispatched, this function gets called with 2 parameters:
state (which is current app state)
action
based on those 2, this function returns new app state (which will be used as parameter in next call to this function with new action)
now, for the very first run, when app state is still null, the parameter state will be set to initialState. for every next run, since state will no longer be null, your state parameter will be whatever the current app state is.
check ES6 function default parameters.

Related

No using class Component , only function in React

Hello I am just learning react and I am looking at tutorials but in the version that was installed, react is no longer using the classes, it only appears functions and I would like to continue that way if possible, but I have a problem with where to change the name in this part with a click but it does not allow me to access persons(const), how could I do it?
import Person from './Person/Person'
function App() {
const persons = {
persons: [
{ name: 'Jose', age: 32},
{ name: 'Gabriel', age: 2}
]
}
const switchNameHandler = () => {
persons({
persons: [
{ name: 'Jose Fernando', age: 32},
{ name: 'Gabriel', age: 2}
]
})
}
return (
<div className="App">
<h1>Hi, I'm a React App!</h1>
<button onClick={switchNameHandler}> Switch Name</button>
<Person name={persons.persons[0].name} age={persons.persons[0].age}/>
<Person name={persons.persons[1].name} age={persons.persons[1].age}> I like play</Person>
</div>
);
}
export default App;
How could I fix the switchNameHandler part?
I know that if I use classes I can access this.setPersons, but is there any way to access without using classes?
You need to use the useState hook. All hooks return two things in a de-structured array (1) the state value and (2) the function to set that state value. and the value you put in the useState() function is the initial value.
For example:
const [name, setName] = useState("Ben");
Here the initial value of name is "Ben". If I wanted to change that value, I could use the setName function and do something like this setName("Jerry");. Now the value of name is "Jerry".
The biggest difference between the setter (in this case setName) and this.setState (in a class component), is that this.setState remembers and spreads the previous state automatically for you if you don't explicitly define it. With hooks, you have to do that your self:
For example:
const [person, setPerson] = useState({ name: "", id: "", age: "" });
If I have this state, and want to edit just the name of the person, I have to remember to spread the previous state object every time I update state, by using a callback function - where the parameter in the callback is the previous state:
// just updating name
setPerson(prevPerson => ({ ...prevPerson, name: "Bob" }));
Here, the only thing that changes was the "name" value and everything else stayed the same:
Result: { name: "Bob", id: "", age: ""}
Check out the react documentation for more tips and examples: https://reactjs.org/docs/hooks-state.html

ReactJS - Proper way for using immutability-helper in reducer

I have the following object which is my initial state in my reducer:
const INITIAL_STATE = {
campaign_dates: {
dt_start: '',
dt_end: '',
},
campaign_target: {
target_number: '',
gender: '',
age_level: {
age_start: '',
age_end: '',
},
interest_area: [],
geolocation: {},
},
campaign_products: {
survey: {
name: 'Survey',
id_product: 1,
quantity: 0,
price: 125.0,
surveys: {},
},
reward: {
name: 'Reward',
id_product: 2,
quantity: 0,
price: 125.0,
rewards: {},
},
},
}
And my reducer is listening for an action to add a reward to my object of rewards:
case ADD_REWARD:
return {
...state, campaign_products: {
...state.campaign_products,
reward: {
...state.campaign_products.reward,
rewards: {
...state.campaign_products.reward.rewards,
question: action.payload
}
}
}
}
So far so good (despite the fact that every object added is named "question")... its working but its quite messy. I've tried to replace the reducer above using the immutability-helper, to something like this but the newObh is being added to the root of my state
case ADD_REWARD:
const newObj = update(state.campaign_products.reward.rewards, { $merge: action.payload });
return { ...state, newObj }
return { ...state, newObj }
First, you must understand how the object shorthand works. If you're familiar with the syntax before ES2015, the above code translates to:
return Object.assign({}, state, {
newObj: newObj
});
Note how the newObj becomes a key and a value at the same time, which is probably not what you want.
I assume the mentioned immutability-helper is this library: https://www.npmjs.com/package/immutability-helper. Given the documentation, it returns a copy of the state with updated property based on the second argument.
You're using it on a deep property so that it will return a new value for that deep property. Therefore you still have to merge it in the state, so you have to keep the approach you've labelled as messy.
What you want instead is something like:
const nextState = update(state, {
$merge: {
campaign_products: {
reward: {
rewards: action.payload
}
}
}
});
return nextState;
Note how the first argument is the current state object, and $merge object is a whole object structure where you want to update the property. The return value of update is state with updated values based on the second argument, i.e. the next state.
Side note: Working with deep state structure is difficult, as you've discovered. I suggest you look into normalizing the state shape. If applicable, you can also split the reducers into sub-trees which are responsible only for the part of the state, so the state updates are smaller.

Update immutable state with Redux

I am using Redux to update my state which is immutable. I want to update nested array of object in my reducer by simply targeting list[] as I need to update it with new object. My first item's (board1) list does get updated as I dispatch action but once I dispatch for the next item(s) board2 and above, they overwrite my state and it return single item. Your help would be highly appreciated.. Thanks
const initialState = {
board: [
{ boardId: 1, boardname: "board1", list: [] },
{ boardId: 2, boardname: "board2", list: [] }
]
};
export default function(state = initialState, action) {
switch (action.type) {
case "ADD_LIST":
state = {
...state.board,
board: [
...state.board[action.payload.boardId - 1],
{
...state.board[action.payload.boardId - 1],
list: [
...state.board[action.payload.boardId - 1].list,
{
listId: state.board[action.payload.boardId - 1].list.length + 1,
listname: action.payload.listname
}
]
}
]
};
break;
default:
break;
}
return state;
}
My choice is to use dotprop immutable.
https://github.com/debitoor/dot-prop-immutable.
In addition. For updating different keys at once. I write a wrapper function to do it.
You are using ES6 spread operator which isn't bad, but it starts to get annoying when working with nested objects. My advice is to try immer, it will make your life much easier!!!

React/Redux nested state issue

I am new to React/Redux and I am trying to store one of my objects in Redux state as a Map/Hash with the keys being the primary keys from the objects from the db and the values being the object itself.
However the state seems to get overidden each time I am updating and the new value I am adding is the only one that remains. Here is my code:
import { RECEIVE_CURRENT_SCAN_RESULT } from '../constants';
const initialState = {
currentScanResult: {info:{}, results:[]},
};
export default createReducer(initialState, {
[RECEIVE_CURRENT_SCAN_RESULT]: (state, payload) =>
Object.assign({}, state, {
currentScanResult: payload
})
});
export function createReducer(initialState, reducerMap) {
return (state = initialState, action) => {
const reducer = reducerMap[action.type];
return reducer
? reducer(state, action.payload)
: state;
}
}
I would like to just pass in my object:
{id: 1, thing: "blue"}
and have the state be updated with it. Then if I pass in:
{id: 2, thing: "red"}
I would like my redux state to reflect:
currentScanResult: {1: {id: 1, thing: "blue"}, 2: {id: 2, thing: "red"}}
Is there any easy way for me to do this? Will redux re-render if I am updating a nested value? For example if I pass in:
{id: 2, thing: "purple"}
=> currentScanResult: {1: {id: 1, thing: "blue"}, 2: {id: 2, thing: "purple"}}
I would like to see a behavior like this. I've looked into Immutable JS I am just wondering if I can make this simple use case work without it?
When you do
Object.assign({}, state, {
currentScanResult: payload
})
you are overriding state.currentScanResult. If you want to update it, you need to do something like
Object.assign({}, state, {
currentScanResult: Object.assign({}, state.currentScanResult, payload)
})

Updating nested redux state

I have a reducer that receives an action with a payload that I need to update the state with. The problem is the data I need to update in the state is nested data.
I've added my reducer below with some comment and what i tried to do so far.
export default function(state=data, action){
switch (action.type) {
case 'UPDATE_CONTACT_INFO':
let appointment = state[action.appointmentIndex]; // This is the appointment that needs to be updated
appointment.notification.contactInfo = action.payload; // this is the data that needs to be updated with the payload. I tried updating it like this but then not sure how to add it to the state.
return state; // Somehow need to update the state with the new state
break;
default:
return state;
}
}
Below is my initial data structure which I pass into the reducer as the default state.
data = [
{
date: 'Friday, January 6',
time: '4:00 PM-5:00 PM',
notification:
{
contactInfo: [
{
displayMethod:"Phone Call",
method:"Phone",
value:"3473686552"
},
{
displayMethod:"Email",
method:"Email",
value:"memedoe#gmail.com"
}
]
}
},
{
date: 'Saturday, January 7',
time: '2:00 PM-6:00 PM',
notification:
{
contactInfo: [
{
displayMethod:"Phone Call",
method:"Phone",
value:"2123686552"
},
{
displayMethod:"Email",
method:"Email",
value:"johndoe#gmail.com"
}
]
}
}
];
The action.payload in the reducer data is the same structure as contactInfo array in one of the appointments. [Object, Object]
With redux you never update the state. You will have to return a new state object, with the updated data.
In order to do that, you need to use either Object.assign() or the ES6 spread operator {...}
I have provided links to both:
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Spread_operator
Read up on the reducers here:
http://redux.js.org/docs/basics/Reducers.html
Pay specific attention to We Dont Mutate the state point.
All problems of this type may be solved using react-addons-update package. Read here.
This case can be solved that way:
export default function(state=data, action){
switch (action.type) {
case 'UPDATE_CONTACT_INFO':
return update(state, {[action.appointmentIndex]:{notification: {contactInfo: {$set: action.payload}}}});
default:
return state;
}
}
You need to use object.assign to change the data in your store
const newstateobject = Object.assign({}, state[someIndex], {notification: Object.assign({}, state[someindex].notification, {contactInfo: action.payload})});
return Object.assign({}, state, {[someindex]: newstateobject);

Resources