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
Related
I have this reducer.
const userInitialState = {
users: [],
};
const users = (state = userInitialState, action) => {
if (action.type === "FETCH_USERS") {
return {
...state,
users: action.payload,
};
}
return state;
};
export default combineReducers({
users,
});
initially the users property is edmpty array,when the new results from the api call comes
for example response like
https://jsonplaceholder.typicode.com/users
is this the correct way for immutable way in redux store for my array inside ?
A proper immutable update is best described as "a nested shallow clone". You don't want to copy every value in a nested data structure - just the ones that need to be updated.
But yes, that looks correct.
A couple additional observations:
You should read through the post The Complete Guide to Immutability in React and Redux and the Redux docs page on Immutable Update Patterns to better understand how to do immutable updates correctly
But, you should really be using our official Redux Toolkit package, which uses Immer to let you write "mutating" update logic that is turned into safe and correct immutable updates.
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) {...}
Been reading through Redux tutorials and a bit new to Redux. I am trying to access the current state that I have in my Redux Store. I can see the state object with my data in Redux State in dev tools but when I go to connect mapStateToProps the state returns undefined.
export class VersionDiff extends Component {
render() {
const { diffs } = this.props;
console.log(diffs, 'im the diffs');
return (
<div>
Empty Div
</div>
);
}
}
const mapStateToProps = state => {
return {
diffs: state.diffs,
};
};
export default connect(mapStateToProps)(VersionDiff);
The console log I have in the render method returns undefined when I thought I could access the items from my redux store on this.props in the render method. This is my reducer below.
export default (state = [], action) => {
switch (action.type) {
case 'SET_DIFFS':
return [...state, action.diffs];
default:
return state;
}
};
When I look at Redux devtools I can see the diffs object which I am trying to pull from the store. Im sure this is something very simple but just cannot seem to access this.
I wanted to try and provide some more clarity in case this might be helpful to anyone in the future. I believe what was happening here was since I am exporting my module as default in my connect function at the bottom of the file I would be able to use the standard import syntax. I believe I was trying to import my VersionDiff module as a named import so something like the following.
import { VersionDiff } from '../components/VersionDiff;
This wouldn't work as I was not able to access my component as it's a default export and therefore my props wouldn't be available to be passed into my component as the component would return undefined.
I believe this was a mistake on my end of not actually being able to find the component in my file before adding it into the connect function and try to pull state from the store.
Solution:
import VersionDiff from '../components/VersionDiff';
Consider this example:
class App extends Component {
constructor() {
super();
this.person = new Person("Tim", 23);
this.state = {
name: this.person.name
}
}
changeName() {
this.person.setName("Jane");
this.person.setAge(22);
setState({name: this.person.name});
}
render() {
return (
<div>
<div>Your name is: {this.state.name}</div>
<div>Your age is: {this.person.age}</div>
<div><button onClick={this.changeName.bind(this)}>Change Name</button></div>
</div>
)
}
}
What I'm querying here is when a variable should be added to the state. In this example, although this works age isn't in the state.
I run into this a lot when working with objects, I'm not sure if it's best practise to add any rendered object property to the state, or if I should only worry about adding properties to the state if they're potentially going to be updated. I'm quite sure what I'm doing in this example would be bad, as age is updating, but isn't being reflected in the state.
Any ideas on the "correct" way to do this?
React doesn't dictate how you manage your data. If you're using an object with getters/setters, then it might be simpler to store the entire object in state:
changeName() {
this.person.setName("Jane");
this.person.setAge(22);
this.setState({person: this.person});
}
In this manner, your object continues to be responsible for the data, and whatever internal processing this implies, while the resultant object itself is stored in the component state.
That said, using data objects like Person, while possible, is not idiomatic React. I would recommend using something like Redux, and setting up unidirectional data flow. This means creating a reducer to manage your state, and using action creators to communicate with the Redux store.
You can initialize your object's default values in the reducer. This is returned by default from the Redux store.
Your reducer would listen for an UPDATE_PERSON action, which would carry the payload for the entire updated Person object. This would be stored in state, as below:
reducers/person.js
const UPDATE_PERSON = 'UPDATE_PERSON';
const initialState = {
name: "Tim",
age: 23
}
const personReducer(state = initialState, action) {
switch (action.type) {
case UPDATE_PERSON:
return {
...state,
name: action.payload.name,
age: action.payload.name
}
default:
return state;
}
}
Your action creator is a simple function with a type property and some kind of payload:
(presumably) actions/person.js
export const updatePerson(data) {
return {
type: UPDATE_PERSON,
payload: data
}
}
You then connect the Redux store to your component, and use the action creator to dispatch the action to the store:
import { connect } from 'react-redux';
import * as PersonActionCreators from '../actions/person';
class App extends Component {
changeName() {
this.props.updatePerson({name: "Jane", age: 22});
}
render() {
return (
<div>
<div>Your name is: {this.props.person.name}</div>
<div>Your age is: {this.props.person.age}</div>
<div><button onClick={this.changeName.bind(this)}>Change Name</button></div>
</div>
)
}
}
const mapStateToProps = (state) => ({
person: state.person
});
const mapDispatchToProps = {
updatePerson: PersonActionCreators.updatePerson
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
The above code assumes you have a root reducer in the following format:
import { combineReducers } from 'redux';
import personReducer from './reducers/person';
const appReducer = combineReducers({
person: personReducer
})
const rootReducer = (state, action) => appReducer(state, action);
export default rootReducer;
You will need to create the store and connect your root reducer to it. Details on that can be found here.
The combineReducers function simply helps to construct the root reducer:
The combineReducers helper function turns an object whose values are
different reducing functions into a single reducing function you can
pass to createStore.
This is more boilerplate, but it is the established and most popular way of handling application state in React. It may seem like a lot to wrap your head around at first, but once you become familiar with reducers, action creators, and the connect function, it becomes very straightforward.
Redux uses a uni-directional data-flow, which means data streams downwards from the top-level components to child components. Stateful components are kept to a minimum; but where state is required, the connect function provides it. When a component needs to modify state, it does so through an action creator. The reducers listen to actions and update state accordingly.
See Dan Abramov's free egghead courses on this topic for an excellent introduction to Redux:
Getting started with
Redux
Building React Applications with Idiomatic
Redux
It's simple. The right way is using state for properties you want to display.
In this case, your code should be
setState({
name: this.person.name,
age: this.person.age
});
Why? Well, it's best practice, and using this.state is encouraged in docs. Attaching properties on the component (this), is generally usual for methods.
Also, consider the component methods shouldComponentUpdate(nextProps, nextState), componentWillUpdate(nextProps, nextState), componentDidUpdate(prevProps, prevState).
Obviously, if you need their arguments, you will not be able to retrieve the old/new properties you change, if them're not on state or props.
I have been working on a React / Redux Application, where now the reducer for single feature has become big enough for the thought of dividing the reducer further.
So I was thinking is there a way to divide the reducer into two different reducers and combining them without adding keys of their functional names.
For Ex:
const nowShowing = function(state = {}, action){
switch(action.type){
case types.NS_MOVIES:
return Object.assign({}, state, { nowShowingMovies: action.data })
}
const comingSoon = function(state = {}, action){
switch(action.type){
case types.CS_MOVIES:
return Object.assign({}, state, { comingSoonMovies: action.data })
}
I am looking to check if its possible to combine these reducers
const movies = combineReducers({
nowShowing,
comingSoon
})
in such a way that i can access them in the following manner
mapStateToProps(state){
nowShowing: state.movies.nowShowing,
comingSoon: state.movies.comingSoon
}
The reason i am looking for a solution like this is because, it will allow my entire application to work as earlier, but the nowShowing & comingSoon reducer become separate and my code becomes more modular.
I did try returning an object instead of combining the reducer using the combineReducers function. But that causes the entire state of the reducer to reset when there is a change in value as it recreates the object.
I think this kind of solution is not possible or it should not be attempted. But I would to understand more why, as I do agree that this points to more over architectural flaw in constructing the reducers in the first place.
Any help would be highly appreciated.
Thanks