I am facing a problem, I am new to Redux, and I am just playing around with it, So I have a problem, I created a data json file, I get the data from it in my reducer, and everything works fine, here is my reducer :
import update from "immutability-helper";
import data from "../../../../data";
export default function notificationsReducer(state, action) {
switch (action.type) {
case "NOTIFICATIONS_EDIT_TO_FOLLOW":
return update(state, {
[action.id]: {
follwing: { $set: false }
}
});
break;
case "NOTIFICATIONS_EDIT_TO_UNFOLLOW":
return [];
break;
default:
return data.notifications;
}
}
As you can see, in the default part, I return data.notififications, this causes a probleme of course, because the data in the json file does not change, the state does.
When I work on my component and click 'follow' and 'unfollow' and stuffs everything looks fine, but When I click somewhere else outside the component, others actions got dispatched, ( others for different purposes ) and the default part get executed again, so when I open my notifications again all changes I made are gone.
That's my problem, if any explanation from me is needed just ask and I will edit my question.
Any help would be much appreciated.
Recapping from the comments:
In your default case (for all reducers), you need to return the current state of your reducer so that unrelated actions don't interfere with their values:
default:
return state;
You also (if needed) have to set up the initial value of your reducer, in this case the notifications from the data JSON file:
export default function notificationsReducer(state = data, action) {...}
Related
i've taken a few courses on react and redux and so far the challenges are still piling up. i'm creating a simple app were at the time i need to just map the response from the api, but the state looks weird. the actions is returning the data, but is being displayed awkward. Please see the images attached. Does anyone know why the console.log of the data returns a number instead of an object or array name? also if you see the state from the redux dev tools, you can see that where an object name would be displayed, you see undefined instead. Please help. I've been at it for a couple of days and have been banging my head against the wall. enter image description here
reducer
import _ from 'lodash';
import {
FETCH_CLIENT,
FETCH_CLIENTS,
ADD_CLIENTS_COMMENTS
} from "../actions/types";
export default (state = {}, action) => {
switch (action.type) {
case FETCH_CLIENT:
return { ...state, ..._.mapKeys(action.payload, 'ClientUno') };
case FETCH_CLIENTS:
return { ...state, [action.payload.ClientUno]: action.payload };
case ADD_CLIENTS_COMMENTS:
return { ...state, [action.payload.ClientUno]: action.payload };
default:
return state;
}
};
combined reducer
import { combineReducers } from "redux";
import ClientsReducer from "./ClientsReducer";
export default combineReducers({
clients:ClientsReducer
});
action
const mapStateToProps = state => {
return{
clients:Object.values(state.clients),
}
}
export default connect(mapStateToProps,{fetchAllClients})(Clients);
after removing .ClientUno. This my new state
post man response attached
It looks like your reducer logic is wrong:
return { ...state, [action.payload.ClientUno]: action.payload }
Per the screenshots, your action.payload is an array of items. The individual items do have a .ClientUno field inside them. But the array most definitely will not. So, action.payload.ClientUno is indeed undefined, and that's what you're using as a key.
You'll need to rethink what you want to use as a key there. I don't know how you're actually trying to structure this slice state, so I can't provide a suggestion there.
I will note that the Redux patterns you're using here are very outdated. "Modern Redux" with Redux Toolkit is much simpler and easier to use - there's no "ACTION_TYPES", switch statements, or object spreads. Please see our official Redux docs tutorials to learn how to use Redux Toolkit:
https://redux.js.org/tutorials/index
I am creating a job list. In this job list, we have a button to open modal for enter the detail of a job. However, when I pass a default job object with default value to the modal, the modal cannot show the default job value.
Because some default attributes are came from remote API, so in the JobList component, I use the useEffect hook to retrieve data from remote API.
Would you tell me what's going on?
My code is resided here.
I checked your code and there is a logical error.
You need to add break; after addJob case statement in JobList.js.
Because of that, defaultJobObj is reiniting as undefined object.
-- ADDITION --
You need to handle defaultJobObj prop update in JobForm component.
You can define a hook and reinit items in reducer.
let reducer = (state, action) => {
let result = { ...state };
switch (action.type) {
case "reinit":
result.job = defaultJobObj;
break;
default:
break;
}
return result;
};
useEffect(() => {
updateItems({
"type":"reinit"
})
}, [defaultJobObj])
Maybe you can use state management like Redux/React Context to pass the values to your modal
This is driving me crazy for hours now... I have a module that displays a list that is fetched from a server and loaded into the redux store on button press. That works properly. I mention this as this is the reason why I don't understand the following behavior.
This object array from the store is mapped into my component with
const mapStateToProps = (state) => {
return {
extracted_templates: state.extracted_templates
}
}
And used in the render() as follows... I removed some other DOM parts to keep it simple
render(){
return(
<div className="main-container">
{Object.values(this.props.extracted_templates).length > 0 ?
<ExtractedTemplatesList templates={Object.entries(this.props.extracted_templates)} clickHandler={this.clickHandler} /> : '' }
</div>
);
}
The clickHandler modifies the store using the same action as the fetch function uses.
clickHandler(action, id, parent){
console.log(action+" "+parent)
switch(action){
case 'dismiss':
let new_template_list = this.props.extracted_templates
delete new_template_list[id]
// console.log(new_template_list)
this.props.dispatch(setExtractedTemplates(new_template_list))
break;
default:
break;
}
}
Everything is called correctly, the store updates correctly (as I can see in my web-dev console) but this time the DOM doesn't get updated.
For completeness, here's the action and the reducer implementation
action:
export const setExtractedTemplates = (templates) => ({
type: actions.SET_EXTRACTED_TEMPLATES,
payload: templates
});
reducer:
case actions.SET_EXTRACTED_TEMPLATES:
console.log({action})
return {
...state,
extracted_templates: action.payload
}
You're mutating the existing data, and you're putting the exact same object back into the store:
let new_template_list = this.props.extracted_templates
delete new_template_list[id]
this.props.dispatch(setExtractedTemplates(new_template_list))
Both of those are bugs. You should never mutate data from the store, and the result of an action should be new data in the store.
This is one of the reasons why we recommend putting as much logic as possible into reducers. Also, you should be using our official Redux Toolkit package, which would both catch this accidental mutation here, and simplify the update logic in a reducer.
Try this:
clickHandler(action, id, parent){
console.log(action+" "+parent)
switch(action){
case 'dismiss':
let new_template_list = {...this.props.extracted_templates} //make a new copy
delete new_template_list[id]
// console.log(new_template_list)
this.props.dispatch(setExtractedTemplates(new_template_list))
break;
default:
break;
}
}
You modified the same object saved in the redux store. This is potentially dangerous because you changed the state without using a reducer. When React did the shallow comparison, it didn't see difference so UI was not updated. You can make a copy before save it to store.
Further more you can modify your reducer in this way:
case actions.SET_EXTRACTED_TEMPLATES:
console.log({action})
return {
...state,
extracted_templates: [...action.payload] //make a new copy
}
I am trying to create a simple website using react-redux and the immutable-assign library (instead of immutable) to handle my state. (documentation for immutable-assign: https://github.com/engineforce/ImmutableAssign)
I've made solutions with both the 'immutable' and 'immutable-assign' libraries, but neither work (code for immutable solution is commented out in the reducer below. No matter which changes I make, the state never changes, and the values are never assigned to menuItems
The setMenu(newMenu) function is currently called with dummydata in the form of a list of arrays in the following format:
menuItems: {
id: "113",
foodItem: "tesatewr",
description: "gfdgsdfsdf",
price: 999
}
The reducer:
import { iassign } from 'immutable-assign'
export function setMenu(newMenu) {return {type: 'SET_MENU_ITEMS', newMenu}}
const initialState = {
date: 'test',
menuId: 'test',
menuItems: []
}
function menuViewReducer(state = initialState, action){
switch(action.type){
case 'SET_MENU_ITEMS':
var itemList = iassign(
state,
function (n) { n.push('testtest'); return n}
)
return state.set(['menuItems'], itemList)
default:
return state
}
}
/* CODE FOR IMMUTABLE
function menuViewReducer(state = fromJS(initialState), action){
switch(action.type){
case 'SET_MENU_ITEMS':
return state.updateIn(['menuItems'], (menuItems) => menuItems.push(fromJS(action.newMenu.menuItems)))
default:
return state
}
} */
export const menuSelector = {
date: state => state.menuViewList.date,
menuId: state => state.menuViewList.menuId,
menuItems: state => state.menuViewList.menuItems
}
export default menuViewReducer
Render function:
render(){
return (
<div>
Test data here: {this.props.menuItems}
<ul className="menuViewList">{ this.mapMenuItemsToListElements() }</ul>
<button
onClick={() => this.mapMenuItemsToListElements()}> get data
</button>
</div>
)
}
It's really hard to figure out what's not working from this code. The best I can do is give you some debugging tips:
First off, are you getting any errors? If yes, that seems like a good place to start.
Otherwise, try to narrow down where the problem is occurring.
Are you sure your reducer is actually getting called?
I would try putting a console.log right after your case 'SET_MENU_ITEMS': so you know when your code is being run.
If it's not:
The problem could be a number of things:
Your reducer isn't connected to your store properly
You're not properly dispatching actions to your store
The actions you're dispatching don't have their type property properly set.
If it is:
The problem could be a number of different things. Some that I can think of:
Your state isn't being updated (properly). Try logging the state at the start of your reducer and your new state right before you return it. Or consider using redux-devtools to inspect your state.
Your view isn't getting updated. Maybe your component isn't connected to your store properly.
I found the error and as Simon pointed out, its hard to find from my submitted code.
I was calling setMenu(newMenu) in a generator function, so I should have called it like this:
yield put(setMenu(newMenu))
instead of
setMenu(newMenu)
I am using recat redux for my project and in one component I need to update my state but since I am dealing with asynchronous call I need to do the action call in my componentDidUpdate as follows:
componentDidUpdate() {
this.props.updateHamburgerMenu(this.props.Channel.channelIdArr);
}
and here is my action:
export function updateHamburgerMenu(channelIdArr) {
return dispatch => {
dispatch(
{
type: "UPDATE_HAMBURGER_MENU",
payload: {
"channelIdArr":channelIdArr
}
}
);
};
}
and in my reducer I have :
switch (action.type) {
case "UPDATE_HAMBURGER_MENU":
var channelList=state.allChannelList.slice();
channelList.unshift({
"id": channelIdArr[0],
"channelName": "sssssssss",
"status": "Inactive"
});
alert("reducer called");
state.allChannelList=channelList;
break;}
return state;
Now when I run it I can see that the alert is working but state does not update at all.
Also I tried another way as follow:
state={"channelsArr":state.channelsArr,"AllChannels":state.AllChannels,"channelIdArr":state.channelIdArr,"channelLabelForScrolls":[], "latestAction":action.type, "allChannelList":channelList};
break;
This way, it seems that state keep updating and it goes in infinite loop.
It is really confusing, can anyone help? what am I missing?
Update:
When I separate the allChannelList in another reducer it works. So it seems that updating allChannelList in a specific case of componentdidupdate goes to infinite loop and state keep updating itself. BUt I have no idea why it is happenning
in your reducer case statements, you should be returning a new object which represents the state after the current action - you appear to be trying to assign directly to the allChannelList property on the passed in state object.
i.e.
return {
...state,
allChannelList: channelList
};