redux dispatch action and reducer with new state - javascript

I have actions like:
export function login(){
my_login(function(response){
if(response.status == "ok"){
loginSuccess(response)
}
})
}
export function loginSuccess(response){
return dispatch => {
dispatch({ response, type: types.LOGIN });
console.log("dispatched");
console.log(getState()); ---> I want the updated state
router.transitionTo("/some_url");----> after this I want to route it to somewhere
};
}
When my login action is called it again calls my_login and then I am dispatching my loginSuccess action.
I have a reducer like:
const initialState = [
{
fname: null,
lname: false,
}
]
export default function login(state = initialState, action) {
switch (action.type) {
case LOGIN:
console.log("actions dude")
console.log(action)
return
[{
fname: action.fname,
lname: actin.lname,
}]
default:
return state
}
}
Here my state is not changing and I am not getting the getState() value in action above.
I dont think my reducer is called and state is updated.
Can anyone sugges me whats wrong in here ?

I think you are missing dispatch(loginSuccess(response))

Beware of the bare return statement. One of the known pitfalls of automatic semicolon insertion is that return becomes return;.
So instead of:
return
[{
fname: action.fname,
lname: action.lname,
}]
Do this instead:
return [{
fname: action.fname,
lname: action.lname,
}];

Related

Redux Toolkit caseReducers skip prepare callback in reducer function

I have a reducer for adding comment as follow
addComment: {
reducer: (state,action)=>{
const { newId, comment } = action.payload
console.log(newId)
// functions that create an new comment entry with the newID and update state
},
prepare: (input) =>{
return {
payload: {
...input,
newId: nanoid(),
}
}
}
If I call dispatch within my app as follow, the console.log shows the id generated by nanoid(). So far so good.
dispatch(addComment({comment: comment}))
However if I call the reducer within another reducer using the caseReducers, console.log returns undefined
commentSlice.caseReducers.addComment(state,{
payload: {
comment : comment
}
})
How do I get the prepare callback to run when using caseReducers.
Based on the docs it looks like if you are to invoke using the caseReducer way, you need to include the "type":
const slice = createSlice({
name: 'test',
initialState: 0,
reducers: {
increment: (state, action: PayloadAction<number>) => state + action.payload,
},
})
// now available:
slice.actions.increment(2)
// also available:
slice.caseReducers.increment(0, { type: 'increment', payload: 5 })

Using spread operator to add new value to key value pair in object

Struggling to figure out how to add a new item to an object from my reducer. This is the payload I am receiving GH
This is the relevant code in my reducer
const initialForm = {
nationality: '',
}
function addName(state= initialForm, action) {
switch(action.type){
case ADD_NEW_NATIONALITY:
console.log('nationality',action.payload.name)
return {
...state,
[action.payload.nationality]: action.payload.value
}
default:
return state
}
}
and my action creators
export const addFullName = newName => dispatch => {
console.log(newName)
axios.get(`https://api.nationalize.io?name=${newName.name}`)
.then(res => {
dispatch({ type: ADD_NEW_NATIONALITY, payload: res.data.country[0].country_id})
})
Please help me figure this out
Your payload is just a plain value (not an object), so you cannot find action.payload.name and action.payload.value
And similarly, you cannot find action.payload.nationality either.
Here is how your action looks like
{ type: ADD_NEW_NATIONALITY, payload: 1} //`1` is country_id
To set nationality correctly, you can check the below implementation
const initialForm = {
nationality: '',
}
function addName(state= initialForm, action) {
switch(action.type){
case ADD_NEW_NATIONALITY:
return {
...state,
nationality: action.payload //update your `nationality` property with a plain value from `payload`
}
default:
return state
}
}

How to handle, after state change in React context dispatch

I have a react version 16 application. The state changes are handled using React's Context, reducer concept.
To change the state and do something post that change can be handled as given below. For Example - I am in component 'Contact'. Inside this I am dispatching a state change on a variable age which is a number as given below -
dispatch({
type: "CHANGE_AGE",
payload: age
})
and catch the dispatch in the reducer as given below
case "CHANGE_AGE":
return {
...state,
age: action.payload
}
To do something in 'Contact' component after there is a change in age, we can use 'useEffect' to watch for change in 'age'
useEffect({
//doSomething()
},[state.age])
The problem is - What if we had to change an object in state, ie instead of changing the age, we had to change the address like given below and we had to do something after address has changed.
How do we watch for a change in address?
dispatch({
type: "CHANGE_ADDRESS",
payload: {
city: "Delhi",
country: "India",
zip: '22233'
}
})
One probable solution - Along with address dispatch, we can dispatch a variable, on change of which we can do something. But this is not a perfect solution.
Just store both: the previous and the current addresses
// your-state.js
const initialState = {
age: 0,
prevAdress: {
city: '',
country: '',
zip: ''
},
currAdress: {
city: '',
country: '',
zip: ''
},
...
}
and when you dispatch, write current as previous in a reducer
// your-reducer.js
...
case "CHANGE_ADDRESS":
return {
...state,
prevAddress: { ...state.currAddress },
currAddress: { ...action.payload }
}
Then compare in the effect
// your-component.js
...
useEffect(() => {
if (state.prevAddress.zip !== state.currAddress.zip) { // just an example of comparison
// doSmthg
}
}, [state]);
...
UPDATE
If you need to compare address only once and do something based on that comparison then just compare before dispatching a value:
// your-component.js
...
useEffect(() => {
const doDispatch = () => dispatch({
type: "CHANGE_ADDRESS",
payload: {
city: "Delhi",
country: "India",
zip: newZip,
},
});
if (state.address.zip !== newZip) {
doSmthgAsPromise().then(() => { // do something and then dispatch
doDispatch();
});
} else {
doDispatch();
}
}, [state, dispatch, doSmthgAsPromise]);

useReducer React approach

I'm playing with useReducer react's api, and wonder to know the difference between the theoretical (documentation) part and one I implement.
intialState of commponent with useReducer hook:
const [fields, dispatch] = React.useReducer(formReducer, {
firstName: { value: '', isValid: false },
lastName: { value: '', isValid: false },
});
theoretical Variant
const formActionTypes = {
firstName: 'FIRST_NAME',
lastName: 'LAST_NAME',
};
....
function formReducer(state, action) {
switch (action.type) {
case formActionTypes.firstName:
return { ...state, firstName: { ...action.payload } };
case formActionTypes.lastName:
return { ...state, lastName: { ...action.payload } };
default:
return state;
}
}
....
dispatch({
type: formActionTypes[name], //name is input name
payload: { value, isValid } //value is e.target.value
});
MY implimentation
function formReducer(state, action) {
return { ...state, [action.name]: { ...action.payload } };
}
....
dispatch({
name, //name is input name
payload: { value, isValid } //value is e.target.value
});
The two reducers you've shown will both work and produce identical results if that's what you're asking. I think the theoretical version version you're getting from the documentation is meant to demonstrate a particular concept, though, which your reducer arguably violates (though it's not a big deal; our job is to make working code, not to pass some purity test!).
Specifically, you typically want to somewhat decouple actions from state. The action shouldn't just be a mirror of your state data structure; if you want that coupling, you'd might as well use useState instead and just set the state directly. A reducer is meant to decouple this by you modeling a description of the action, and then it's only the reducer that decides how that action acts on state. You might, for example, decide to add a clear form button. With your current pattern, you'd have to dispatch two actions, which would cause two state updates, because your actions closely model the state. The switch statement pattern allows you to easily apply different types of logic based on different types of actions.
There are no wrong answers, just different approaches all with their merits. Here's one that I think introduces better decoupling by letting the reducer logic take care of knowing about whether a field is valid:
const SET_FIRST_NAME = Symbol();
const SET_LAST_NAME = Symbol();
const CLEAR_FORM = Symbol();
// Call action creators instead, like: dispatch(setFirstName(e.target.value));
const setFirstName = name => { type: SET_FIRST_NAME, value: name };
const setLastName = name => { type: SET_LAST_NAME, value: name };
const clearForm = () => { type: CLEAR_FORM };
const initialState = {
firstName: { value: '', isValid: false },
lastName: { value: '', isValid: false }
};
const notEmpty = value => !!(value && value.trim().length);
const validateFirstName = notEmpty; // Or replace with different logic
const validateLastName = notEmpty;
const reducer = (state, action) => {
switch (action.type) {
case SET_FIRST_NAME:
return {
...state,
firstName: {
value: action.value,
isValid: validateFirstName(value)
}
}
case SET_LAST_NAME:
return {
...state,
lastName: {
value: action.value,
isValid: validateLastName(value)
}
}
case CLEAR_FORM:
return initialState;
default:
return state;
}
};

react redux merge state with given array

Lets say I have a reducer which is like :
const initialState = [
{
accessToken: null,
isLoggedIn: false,
}
]
export default function my_reducer(state = initialState, action) {
switch (action.type) {
case LOGIN:
return state.merge(user: action) ---> how to handle this
and the output should be like:
[
{
accessToken: null,
isLoggedIn: false,
user: {
name: 'some name',
email: 'some email'
}
]
In action I am getting a array which I am providing by doing JSON.stringify(response)
previous data should not be changed and new data should be updated
The ES6 Way
To create a new object with the state in ES6 we can use the spread operator. Like this
...
case ActionType.SUCCESS_GET_DATA : {
let newState = { ...state, [action.uniqueKey]: action.payload };
return state.merge(newState);
}
...
I did the uniqueKey part as a variable because you will want a unique name for your state.
IMO this syntax is much easier to understand than the Object.assign
You can use Object.assign() function:
var state = {
accessToken: null,
isLoggedIn: false,
};
var user = {
name: 'some name',
email: 'some email'
};
var newState = Object.assign({}, state, {user});
console.log(newState);
First I see that your state is actually an array, but I think you would need an object right?
So it would be:
const initialState = {
accessToken: null,
isLoggedIn: false,
}
(requires Babel) So with spread operator you can:
return {
...initialState,
user: {
name: '...',
surname: '...'
}
};
Or if you do not transpile via Babel alike:
return Object.assign({}, initialState, {
user: {
name: '...',
surname: '...'
}
});
Using ES6 spread syntax
...
case 'ACTION_TYPE_A': {
return { ...state, action.key: action.value };
}
...
This will return the merged state by updating the 'key' if it exists in the original state.
Everything according to new Es6 format : )
A total addToDo reducer function where the data is appended to the previous state. And you get the output of a new state data : )
export const addToDo = (state, action) => {
const { name, email, phone, image,key } = action;
var data = [...state.data];
var newData = {
name: name, email: email, phone: phone, image: image,key:key
}
data.push(newData)
return(
state.merge({
data : data
})
)};
Happy coding.

Categories