Confusion on how to properly use reducer in Redux - reactjs

I was learning Redux and wanted to properly understand how reducer works. If you look at the reducer called titleReducer:
const initialState={
titleColor: null,
}
const titleReducer = (state = initialState, action) => {
switch (action.type) {
case TURN_TITLE_GREEN:
return {
...state,
titleColor: 'green'
}
case UPDATE_TITLE:
return {
...state,
title: action.payload
}
default: return state;
}
}
So, I wanted to ask some questions on the above code. Firstly, imagine that there are three reducers: reducer1. reducer2 and titleReducer(I know silly example but bear with me). The question is why even if state of titleReducer has only one property "titleColor", we need to create new make a copy of state by using ...state. What if we just use:
{
title: action.payload
}
The second question is ok every time titleReducer get "TURN_TITLE_GREEN" action, the reducer takes the previous state and creates a new copy and sends back to a component. The question is "where is the previous state is taken from? I mean, is it true that when titleReducer receives the action of TURN_TITLE_GREEN, it creates new copy and at the same time puts 'titleColor: 'green' into state which will serve as previous state next time. Is that true?"

Related

Should we have a different action type for every time we update a react context state?

Say we have built everything in a large Context and we are using this context for state management (maybe better suited with redux... but regardless...)
Do we want to have a new action type for every time we want to update one property of the state? Or should we have a generic action like UPDATE_STATE in the reducer that handles the times we have a simple change that doesn't really have any reduction logic.
For example:
switch(action.type) {
case "SET_MODAL":
return {
...state,
isModalOpen: action.isModalOpen
}
case "SET_ERROR_MSG":
return {
...state,
errMsg: action.errMsg
}
case "SET_HAS_CLICKED_THING":
return {
...state,
clickedThing: action.clickedThing
}
// ***ALOT MORE OF THESE^^^***
// ***ALOT MORE OF THESE^^^***
case "GET_ITEMS_SUCCESS":
const { items } = action
const newItems = items.map(*some reduction change logic that is not standard*)
return {
...state,
items: newItems
}
}
vs
switch(action.type) {
case "UPDATE_STATE":
return {
...state,
...action.state
}
case "GET_ITEMS_SUCCESS":
const { items } = action
const newItems = items.map(*some reduction change logic that is not standard*)
return {
...state,
items: newItems
}
}
It seems like we will have a large list of actions that Im not sure is adding value. Really we just want to store a value in the state. Do we really need an action for every time we just want to update the state?
It's up to you. But I would choose the first way. It seems more readable and much better to see data flow. Defining actions helps you to debug easily and find out what happens in the app.

Strange behavior with Redux action and reducer when creating action to change keys in state

My apologies for the title.
Anyway, I'm seeing strange behavior in my Redux Dev Tool when I create a certain type of action. The only way to show what I mean is via an example.
Let's say I have a state with this structure:
const INITIAL_STATE = {
isFile: false,
isUploaded: false,
isUser: true
};
Since there are simply keys with no nested objects inside the state, I figure I can just create one action to change all of the keys instead of a different action for each particular key.
So the reducer becomes:
const CHANGE_KEY = "CHANGE_KEY";
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case CHANGE_KEY:
return {
...state,
[action.payload.key]: action.payload.value,
};
default:
return state;
}
};
const changeKeyInState = (key, value) => ({ type: CHANGE_KEY, payload: { key, value } });
Now all of this works. So I can call one action to change simple key-value pairs in the state. However, let's say I have different reducers that handle different areas of the state with their own key changing action. When I look at Redux Dev tools, it shows keys added to states that shouldn't be there, which doesn't make sense. For example, if I had a reducer for a logon that has an action that changes isLoggedIn key, that key is also added to the above reducer, which doesn't make any sense.
Here is the code for the different reducer:
const CHANGE_LOGIN_KEY = "CHANGE_LOGIN_KEY";
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case CHANGE_LOGIN_KEY:
return {
...state,
[action.payload.key]: action.payload.value,
};
default:
return state;
}
};
const changeKeyInLoginState = (key, value) => ({ type: CHANGE_LOGIN_KEY, payload: { key, value } });
I only see this in Redux Dev tools. I don't know what's going on. Can anyone help me figure out what the issue is?

How can a state be cleared when moving away from a page

I have created a Quiz app that tracks the correct answers in state called correct_answer. The issue is when the user leaves one quiz and moves on to the next one, The quiz answers are still stored from the last quiz.
I have tried using LOCATION_CHANGE from react-router-redux, but I am not sure I am using it correctly.
import { LOCATION_CHANGE } from "react-router-redux";
const initialState = {
questions: [],
answers: [],
correct_answer: []
};
export default function(state = initialState, action) {
switch (action.type) {
case "GET_QUESTIONS":
return { ...state, questions: action.payload };
case "GET_ANSWERS":
return { ...state, answers: action.payload };
case "CORRECT_ANSWER":
return {
...state,
correct_answer: [...state.correct_answer, action.payload]
};
case LOCATION_CHANGE:
return {state = initialState};
default:
return state;
}
}```
The app needs to clear the correct_answers state anytime the user moves away from the page.
Keep in mind that the redux store is an omnipresent data structure. The data persists regardless of any ui changes in your app, which includes local state changes in a component and mounting/unmounting components (unless you tear down your reducer, but that's not what you're doing at all).
As mentioned in the comments, it's your job to clear your state. Create an action that will reset the reducer. How you implement it is based on your exact implementation of your Quiz component(s).
How does mounting/unmounting/prop changes work when you switch quizes? Are you mounting an entirely new component or are you feeding new data into an existing component?
If the next quiz is an entirely new instance, then you call it when you unmount the prior quiz:
componentWillUnmount() {
this.props.resetQuizState() // your action that resets the data in your store
}
If it is the same component but new props are passed in:
handleNextQuizClick() {
this.props.resetQuizState()
// and then rest of data manipulation/calling/parsing
}
render() {
return (
<button onClick={this.handleNextQuizClick}>
next quiz
</button>
}

How Does redux custom reducer work with Thunk

I have some experience working with React and Redux
Frankly, Till now, i have been usually just copying-pasting the code without comprehending what is happening.
I was thinking about how reducer works (while trying Vuex) and that lead me to following question
import {
MEETUP_GROUP_DATA_SUCCESS,
MEETUP_GROUP_DATA_LOADING,
MEETUP_GROUP_DATA_ERROR,
GOOGLE_PROFILE_LOGOUT
} from "./../config/type.js"
const intialState = {
meetupProfileData: null,
meetupProfileLoading: null,
meetupProfileError: null,
}
export default function (state = intialState, action) {
switch (action.type) {
case MEETUP_GROUP_DATA_LOADING:
return {
...state,
meetupProfileLoading: true
}
case MEETUP_GROUP_DATA_SUCCESS:
return {
...state,
meetupProfileLoading: false,
meetupProfileError: false,
meetupProfileData: action.payload,
}
case MEETUP_GROUP_DATA_ERROR:
return {
...state,
meetupProfileLoading: false,
meetupProfileError: action.payload,
}
case GOOGLE_PROFILE_LOGOUT:
return {
...state,
meetupProfileLoading: null,
meetupProfileError: null,
meetupProfileData: null,
}
default:
return state
}
}
Here notice our const intitalState
Now, Suppose an action is dispatched. This will make redux call this function
export default function (state = intialState, action) {
Here our state is equal to initialState. So every-time we dispatch an action our state should be equal to intialState? since we are saying state = intialState
So what is the use of ...state here? If you are going to answer by saying that it makes copy of previous state then please mention how would our state have copy of previous state because every time an action is being dispatched our state is being equal to initial state and our initial state have all the parameters as null
The parameter state = initialState means that state defaults to initialState, if it was not provided. Redux will call your reducers once in the beginning to "initialize" your state, meaning it will call your reducer with null or undefined. Since you're setting the state to initialState in this case and returning that, Redux will receive the initial state you've set up.
All subsequent calls will use the new state - so when your reducer receives an action that will change something, you need to return the whole state again with the relevant updates.
Returning { ...state, somethingToUpdate: "foo" } means you're essentially copying the state variable into a new object, overwriting the somethingToUpdate key with "foo".
Basically, Redux only calls your reducers and if your reducer receives an action it does not care about, it needs to return the current state. If you happen to return nothing (maybe forgot to add the default: return state in the switch), the state gets undefined again and will reset to the initialState due to the default parameter provided in the function signature (state = initialState).
This statement
export default function (state = intialState, action) { }
says initialize it with intialState for the first time.
...state
Is like copy state and after that we are changing values while returning state/object.

React-redux - state overwrites itself

I am using react-redux (for the first time). I have a component into which users put a 'startDate' and an 'endDate'. These should then be stored in the redux store, so that they persist.
I have the following action creator:
export const setDates = dates => ({
type: "SET_DATES",
payload: dates
});
The following reducer:
const dates = (state = {}, action) => {
switch (action.type) {
case "SET_DATES":
return action.payload;
default:
return state;
}
};
export default dates;
The state is set conditionally (i.e. only if the start and end dates actually make sense) like this:
handleSubmit = () => {
if (this.state.startDate <= this.state.endDate) {
store.dispatch(setDates([this.state.startDate, this.state.endDate]));
window.location = `/search/${
this.state.location
}&${this.state.startDate.format("DDMMYYYY")}&${this.state.endDate.format(
"DDMMYYYY"
)}&${this.state.guestCount}&${this.state.offset}&${this.state.count}`;
} else {
console.log("HANDLE ERROR");
}
};
The problem, according to the chrome redux dev-tools, is that when the submit is triggered, the store does indeed change to the new dates, but it then seems to be immediately overwritten to the empty state. By modifying the reducer to take state = {dates: 'foo'} as its first argument, I can get the store to persist 'dates:foo'. This suggests to me that, for some reason, the reducer is being called twice - once with an action of type "SET_DATES", which works, and then again, immediately, with an action of unknown type (confirmed by console.log-ging action.type), which causes it to return the default state.
So I'm pretty sure I know what the problem is, but I have no idea why it would do this.
I Already commented, but anyways. The problem is that you reload the page. It reloads redux, and it boots up from initial state, which is probably an empty array. Here is a great video from one of the brains behind redux.
https://egghead.io/lessons/javascript-redux-persisting-the-state-to-the-local-storage
It all boils down to subscribing to the store state changes, and saving it / loading the state back from storage of your choise.
Try changing you reducer like this
const dates = (state = {}, action) => {
switch (action.type) {
case "SET_DATES":
return Object.assign({}, state, {
action.payload
});
default:
return state;
}
};
export default dates;

Resources