In redux, preloading state using with combineReducers - javascript

I am just getting introduced to redux, and am stucked with a problem of preloading the state for some time.
When using a single reducer, I was using the following code, and it used to work fine. Relevant snippets::
const head = (state = {}, action) => {
switch (action.type) {
case 'TOGGLE_VISIBLITY':
if (state.head.content !== action.id) {
return state
}
state.body.visible = !state.body.visible;
return state;
default:
return state
}
}
const heads = (state = [], action) => {
switch (action.type) {
case 'TOGGLE_VISIBLITY':
state.body = state.body.map(t =>
head(t, action)
);
}
return state;
}
export const store = createStore(heads, config);
But instead this I just changed to combinerReducers, and it started thowing JS errors.
Unexpected keys "head", "body" found in preloadedState argument passed to createStore. Expected to find one of the known reducer keys instead: "heads". Unexpected keys will be ignored.
My change was::
const plannerApp = combineReducers({
heads
});
export const store = createStore(plannerApp, config);
In case you wanna check the full code,please visit here.
Any help is highly appreciable. Thanks a lot in advance.. I appreciate your time and efforts...

In a nutshell, the preloaded state needs to match the structure of your reducers. Since you switched to using combineReducers, your state tree structure has changed. You now have a top level key of heads that has a child key of body, so you probably need to update your config to look like:
export default {
heads: {
body: {
...
As it is now, the config object contains top level keys of head and body, which do not have entries at the top level of your state tree.

Related

React-redux does not re-render on state change

I just started with redux and react-redux. I am observing a very weird behavior and not able to wrap my head around it.
I am trying something like this.
const fetchedFolders = useSelector(state=>{
console.log("redux state = ",state);
return state.fetchedFolders;
});
const updateFetchedFolders = useDispatch();
I have callback function that receives a new set of values and will update the state in store.
let appendFoldersToList=(newFolders)=>{
console.log(typeof(newFolders))
if(typeof(newFolders) === undefined)
console.log("go to error");
else{
updateFetchedFolders(setFetchedFolders([...fetchedFolders,...newFolders]));
}
}
this works perfectly and re-renders the list with new value
but if I replace the line
updateFetchedFolders(setFetchedFolders([...fetchedFolders,...newFolders]));
with
updateFetchedFolders(setFetchedFolders([...newFolders]));
it does not re-render and it still shows the old list. but in console, I can see data is updated.
I am not able to understand why it re-renders in first case and not in second case.
This is how my reducers look:-
export const reducer = (state=initialState, action)=>{
switch(action.type){
case 'SET_FOLDERS': return {
...state,
fetchedFolders:[...action.payload]
}
}
}
this is my action creator:-
export const setFetchedFolders = (payload)=>{
return {
type:'SET_FOLDERS',
payload:payload
}
}
this is my initial state:-
const initialState = {
fetchedFolders:[],
}
I don't think I am not mutating the state.
my array looks something like this::-
[
{name:cats, id:SOME_ID},
{name:dogs, id:SOME_ID}
]

Redux reducer for inserting into an array at a specific index

I am trying to insert an array questions into a state array at a certain index in my array, however it is not always getting the order correct. I am expecting something like this:
[[/*arr 0*/], [/*arr 1*/], [/*arr 2*/], [/*arr 3*/], ...]
But I keep getting something like this:
[[/*arr 0*/], [/*arr 2*/], [/*arr 1*/], [/*arr 3*/], ...]
I tried following this guide from the official Redux docs, but to no avail. My reducer is the following:
export const questions = (state = [], action) => {
switch (action.type){
case SET_QUESTIONS:
const {questions, index} = action.payload;
let newArray = state.slice()
newArray.splice(index, 0, questions);
return newArray
case RESET_QUESTIONS:
return [];
default:
return state;
}
};
What am I doing wrong?
EDIT:
I have been asked to show how the actions are called, so here is the snippet where the actions are called. This loops about 7 times or so, depending on the length necessary. These calls are asynchronous, but I don't think this should necessarily change how the reducer functions.
axios.post(`${process.env.REACT_APP_SERVER_ENDPOINT}/getQuestionnaireData`, data).then(res => {
store.dispatch(setQuestions(res.data, index));
resolve();
}).catch(err => {
store.dispatch(setError(true));
});
The dispatched action looks like this:
export const setQuestions = (questions, index) => ({
type: SET_QUESTIONS,
payload: {
questions,
index
}
})
EDIT 2:
Because there was no way around the way that the dispatch calls are made (can't force insertions to be in order), and unfortunately none of the responses I got were able to solve my problem, I opted for a different solution. I ended up changing my reducer to the following:
export const questions = (state = {}, action) => {
switch (action.type){
case SET_QUESTIONS:
const {questions, index} = action.payload;
//Retrieve the previously stored state
let newObj = {
...state,
}
//Create a new object at the step key if it doesn't exist
if (!newObj[index]) newObj[index] = {};
//Assign the value at the id key in the step object
newObj[index] = questions;
return newObj;
case RESET_QUESTIONS:
return {};
default:
return state;
}
};
From there, I just ended up using Lodash to iterate over the object like an array. This approach proved to be pretty reliable, so that's what I stuck with.
Thanks to everyone for their answers. I hope they work for someone else who might come across this problem later.
Actually, you are not using Spread operator so Use spread operator and you can read about spread operator from following this link
Try the following code
export const questions = (state = [], action) => {
switch (action.type){
case SET_QUESTIONS:
const {questions, index} = action.payload;
return [
...state.slice(0,index),
questions,
...state.slice(index)
]
case RESET_QUESTIONS:
return [];
default:
return state;
}
};
You need to take account of the occasions (including the first time SET_QUESTIONS is dispatched) when your state array has fewer items in it than the new index.
Bearing that in mind, I'd probably do something like this:
export const questions = (state = [], action) => {
switch (action.type) {
case SET_QUESTIONS:
const { questions, index } = action.payload
const stateCopy = [...state]
stateCopy[index] = payload
return stateCopy
case RESET_QUESTIONS:
return []
default:
return state
}
}

javascript/redux: how to avoid null when fetching state?

say I have a reducer like this:
export const fetchStuff = (state, action) => {
const s = state || someDefaultState
switch (action.type) {
case FETCH_STUFF:
return {...s, isFetching: true}
case SET_STUFF:
return {...s, isFetching: false, stuff: action.values}
default:
return s
}
}
In this case if actions.values has objects that are null they will be very hard to deal with in my components because I will manually have to ensure that the component is not passed a null prop and then also manually have to deal with null fields in the component somewhere.
const component = ({ prop }) => {
return {
<div>
<span>{prop.testnull ? '' : prop.testnull}</span>
<div
}
}
const mapStateToProps = (state) => {
const p = prop || someDefaultProp
return {
prop: state.prop
}
}
It might seem easy enough in this example, but I have found it a pain to manage a bigger component/component-set. What is the idiomatic way of doing this? DO I have to bite the bullet and manage it in the component and mapStateToProps? or is there a better way to manage it in the reducer?
EDIT:
I should clarify that I am not trying to take care of the case where state is null I am trying to take care of the case where , on fetching of state, some attribute if state is set to null
We have worked with a few similar solutions for a while, and found this to be a nice way of handling this use case in our reducers. Lodash's _.get is a nice solution to provide a default value for multi-level get in your reducer:
https://lodash.com/docs/4.17.4#get
_.get(object, path, [defaultValue])
object (Object): The object to query.
path (Array|string): The path of the property to get.
[defaultValue] (*): The value returned for undefined resolved values.
For example:
const testValue = get(action, 'values.test', {});

Add logic to the store?

I have a redux application with a "campaign" reducer/store.
Currently I have repeated code to check if a specific campaign is loaded or needs an API call to fetch details from the DB. Much simplified it looks like this:
// Reducer ----------
export default campaignReducer => (state, action) {
const campaignList = action.payload
return {
items: {... campaignList}
}
}
// Component ----------
const mapStateToProps = (state, ownProps) => {
const campaignId = ownProps.params.campaignId;
const campaign = state.campaign.items[campaignId] || {};
return {
needFetch: campaign.id
&& campaign.meta
&& (campaign.meta.loaded || campaign.meta.loading),
campaign,
};
}
export default connect(mapStateToProps)(TheComponent);
Now I don't like to repeat the complex condition for needFetch. I also don't like to have this complex code in the mapStateToProps function at all, I want to have a simple check. So I came up with this solution:
// Reducer NEW ----------
const needFetch = (items) => (id) => { // <-- Added this function.
if (!items[id]) return true;
if (!items[id].meta) return true;
if (!items[id].meta.loaded && !items[id].meta.loading) return true;
return false;
}
export default campaignReducer => (state, action) {
const campaignList = action.payload
return {
needFetch: needFetch(campaignList), // <-- Added public access to the new function.
items: {... campaignList}
}
}
// Component NEW ----------
const mapStateToProps = (state, ownProps) => {
const campaignId = ownProps.params.campaignId;
const campaign = state.campaign.items[campaignId] || {};
return {
needFetch: state.campaign.needFetch(campaignId), // <-- Much simpler!
campaign,
};
}
export default connect(mapStateToProps)(TheComponent);
Question: Is this a good solution, or does the redux-structure expect a different pattern to solve this?
Question 2: Should we add getter methods to the store, like store.campaign.getItem(myId) to add sanitation (make sure myId exists and is loaded, ..) or is there a different approach for this in redux?
Usually computational components should be responsible for doing this type of logic. Sure your function has a complex conditional check, it belongs exactly inside your computational component (just like the way you currently have it).
Also, redux is only for maintaining state. There's no reason to add methods to query values of the current state inside your reducers. A better way would be having a module specifically for parsing your state. You can then pass state to the module and it would extract the relevant info. Keep your redux/store code focused on computing a state only.
Your approach is somewhat against the idiomatic understanding of state in redux. You should keep only serializable data in the state, not functions. Otherwise you loose many of the benefits of redux, e.g. that you can very easily stash your application's state into the local storage or hydrate it from the server to resume previous sessions.
Instead, I would extract the condition into a separate library file and import it into the container component where necessary:
// needsFetch.js
export default function needsFetch(campaign) {
return campaign.id
&& campaign.meta
&& (campaign.meta.loaded || campaign.meta.loading);
}
// Component ----------
import needsFetch from './needsFetch';
const mapStateToProps = (state, ownProps) => {
const campaignId = ownProps.params.campaignId;
const campaign = state.campaign.items[campaignId] || {};
return {
needFetch: needsFetch(campaign),
campaign,
};
}
export default connect(mapStateToProps)(TheComponent);

Can a Redux action affect multiple parts of the state tree?

What is the consensus on an action affecting multiple parts of the state tree in Redux?
For example:
const ADD_POST = 'POST/ADD';
function postsReducer(state = initialState, action = {}) {
// switch ...
case ADD_POST:
return {
...state,
...action.result.post
}
}
function anotherReducer(state = initialState, action = {}) {
// switch ...
case ADD_POST:
return {
...state,
post_id: action.result.post.id
}
}
I'm seeking advice on:
Actions affecting multiple parts of the redux store/state
Yes, absolutely. It’s the whole reason why actions exist: to separate what happened from the component’s point of view from what actually happens in terms of state change.
Yes, it's ok. If that's what you want to happen.

Categories

Resources