React Redux MapStateToProps - javascript

Why do I get undefined returned when I access the state? I use the Redux DevTools and see the state correctly updated via an action but I just cannot access the state values for some reason. I get this sort of object returned when I access state.dog which seems wrong:
ct {size: 1, _root: pt, __ownerID: undefined, __hash: undefined, __altered: false}
Here is my container code:
import { connect } from 'react-redux';
import Message from '../../components/message';
const mapStateToProps = (state) => {
console.log(state.dog.hasBarked);
return {
message: state.dog.hasBarked ? 'Barked' : 'It is quiet',
};
};
export default connect(mapStateToProps)(Message);
Here is the dog reducer:
import * as Immutable from 'immutable';
import { MAKE_BARK } from '../actions/dog';
const initialState = Immutable.Map({
hasBarked: false,
});
const dogReducer = (state: Object = initialState, action: Object) => {
switch (action.type) {
case MAKE_BARK:
return state.set('hasBarked', action.payload);
default:
return state;
}
};
export default dogReducer;

Seems like you are using immutable. state.dog is not a simple js array but a immutable map or list. You can access it natively with state.dog.toObject().hasBarked.

Related

React re-renders even with no change

We have set up a project with redux. In this project, we get an info objecat from an api and insert it into the store. Now we noticed that the function components re-render even if the api return the same state as in the previous request.
We think it's because we are overwriting the store but we are not sure.
ChatContainer.js
const mapStateToProps = function (state) {
return {
content: state.info.content,
loading: state.info.loading,
}
}
const ChatContainer = connect(
mapStateToProps,
)(Chat)
export default ChatContainer
Chat.js
function Chat(props) {
const { content, loading } = props;
return (
<Info content={content} loading={loading} />
)
}
action.js
export function setInfo(info) {
return {
type: SET_INFO, info: {
content: info,
loading: false
}
}
}
reducer.js
function setInfo(state = { content: [], loading: true }, action) {
switch (action.type) {
case SET_INFO:
return action.info
default:
return state
}
}
const appReducer = combineReducers({
...
info: setInfo,
...
})
export default appReducer
If state.info.content is an object, every time you change it with setInfo it will have a new reference. React-redux does a shallow compare on the result of mapStateToProps, so if your content is a different reference every time your component will re-render. connect HOC has an options parameter that you can use to implement a custom compare.
My advice would be to add a check to your setInfo or to the code calling setInfo and not calling your API if data is already loaded/didn't change(don't know your business logic).

How to display data from redux store?

I have created action and reducer for saving messages (array) in redux store. I have created actions and reducer for it but how can I display data once it is stored in redux store ?
reducer.js:
import { SAVE_ITEMS, SAVE_MESSAGES} from '../actions/types';
const initialState = {
messages: [],
items: []
}
export default function (state = initialState, action) {
switch (action.type) {
case SAVE_MESSAGES:
return {
...state,
messages: action.payload
};
default:
return state;
}
}
action.js:
import { SAVE_MESSAGES } from './types';
export const saveMessages = (messages) => ({
type: SAVE_MESSAGES,
payload: { messages }
})
In component I am saving data like this:
this.props.saveMessages(data)
and also the connect:
const mapStateToProps = state => ({
author: state.chat.author,
messages: state.chat.messages,
message: state.chat.message
})
export default connect (mapStateToProps, { saveAuthor, saveMessages, deleteAuthor, deleteMessage })(Chat);
In combineReducer i.e index.js:
import {combineReducers} from 'redux';
import users from './loginReducer'
import allusers from './userReducer'
import chatReducer from './chatReducer'
export default combineReducers({
users: users,
allusers: allusers,
chat: chatReducer
})
Now if I do console.log(this.props) see screenshot below:
Now if I do console.log(this.props.messages) see screenshot below:
Now I want to map over messages data and display it but I am getting error if I do this.props.messages.messages[0] -> error this.props.messages[0] gives undefined.
Screenshot: (redux tools)
I think first you can check if this.props.messages.messages is not undefined and then you can use map() to print messages like this:
{this.props.messages && this.props.messages.messages && this.props.messages.messages.map(function(msg,i) {
return (
<p>{msg.message}</p>
)
})}

Redux Reducer is returning an [Object Object] , However i want string?

I am trying to change state of a const in redux.
i am trying my dispatch directly in the component i want to change the state in. The State is changed after dispatch,However i get back an Object. And when i Console.log , i get [Object][Object] , which before calling dispatch i used to get the Value of the state.
This is my Store.
import { createStore,applyMiddleware , compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
//const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
compose(
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
);
export default store;
This is my Main Reducer.
import { combineReducers } from 'redux';
import sidebarReducer from './sidebarReducer';
export default combineReducers({
name : sidebarReducer
});
This is my CustomReducer , which i call sidebarReducer.
import { TOGGLE_SIDEBAR } from '../actions/types';
let sidebarname = "wrapper slide-menu";
export default function(state=sidebarname,action){
switch(action.type){
case TOGGLE_SIDEBAR:
console.log('reducer called');
console.log(state);
return{
...state,
sidebarname : action.payload
};
}
return state;
}
This is my Dispatch and MapStatetoProps Function.
const mapStatetoProps = state => ({
name : state.name
});
const mapDispatchtoProps = dispatch => ({
setName : (name) => {
dispatch({
type: "TOGGLE_SIDEBAR",
payload: name
})
}
})
export default connect(mapStatetoProps,mapDispatchtoProps)(App);
I successfully retrieved the State from the store , however when i dispatch i get back an Object.
sidebarReducer.js:13 reducer called
sidebarReducer.js:14 wrapper slide-menu
App.js:38 sidebarname is [object Object]
App.js:40 wrapper slide-menu
In handling your action, you are returning an object (check the curly braces):
return {
...state,
sidebarname: action.payload
};
Since your entire state is only the string sidebarname, you should return only the payload:
return action.payload
Alternatively, you can have your state be an object, and then your action return should work just fine:
let initialState = { sidebarmenu: "wrapper slide-menu" };
...
export default function(state=initialState,action){
...
}
your sidebarReducer just manages a string. just return action.payload on TOGGLE_SIDEBAR, not an object with a sidebarname property.

Redux not updating Component's props

I've tried all of the related questions here in Stack Overflow and still didn't find a solution to this problem.
I have a reducer called me and I'm trying to update an array of objects in it called folders, whenever I update the me reducer the component doesn't update.
Here's how I'm updating the reducer in my component:
class ComponentA extends Component {
...
updateUploadedFiles(file) {
console.log(this.props.store);
const newFolders = this.props.me.folders.map(
folder =>
folder._id === file.parent._id
? {
...folder,
files: [...folder.files, file.file]
}
: folder
);
this.props.updateMe({
...this.props.me,
folders: newFolders
});
}
...
}
function mapStateToProps(state) {
return {
me: state.me,
path: state.path,
filesToUpload: state.uploads
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(
{
updatePath,
updateMe,
updateUploads
},
dispatch
);
}
export default connect(mapStateToProps, mapDispatchToProps, null, {
pure: false
})(Upload);
this is my updateMe action's code:
export const updateMe = state => ({
type: "UPDATED_ME",
payload: state
});
And this is the me reducer's code:
export default function(state = "NOT_AUTHENTICATED", action) {
switch (action.type) {
case "UPDATED_ME":
return action.payload;
default:
return state;
}
}
Also here's how I'm combining the reducers:
import me from "./me";
...
import { combineReducers } from "redux";
const reducers = combineReducers({
me,
...
});
export default reducers;
This is not how redux works.
In order to update any part of your Redux store you must dispatch an action in order to let Redux "know" that the store changed and update any dependent component.
You state object must be immutable.

How to set multiple object values when using Immutable JS map

I am new to redux, Is this correct way of doing redux in following code, please?
This is a reducer method when action called to execute currentTime.
import { combineReducers } from 'redux';
import { UPDATE_TIME } from './actions';
import { Map } from 'immutable';
const initialState = Map({update:false, currentTime: ""});
function currentTime(state = initialState, action) {
switch (action.type) {
case UPDATE_TIME:
return {...state, update: true, currentTime: action.time };
default:
return state;
}
}
const currentTimeReducer = combineReducers({
currentTime
});
export default currentTimeReducer
There are multiple ways to do it
You can set the value using set() function
case UPDATE_TIME:
state = state.set('update', true);
return state.set('currentTime', action.time);
or even
case UPDATE_TIME:
return state.set('update', true)
.set('currentTime', action.time);
However this is not feasible when you have multiple changes
The other option is merge()
case UPDATE_TIME:
return state.merge({update: true, currentTime: action.time})
However in case of a nested state update you would need to do a deepMerge. See the details of mergeDeep
We use immutable JS to create new instance on each small change in the existing object. Immutable JS MAP has a set method to set attribute and return new instance of the object.
Here you can find api doc for MAP
import { combineReducers } from 'redux';
import { UPDATE_TIME } from './actions';
import { Map } from 'immutable';
const initialState = Map({update:false, currentTime: ""});
function currentTime(state = initialState, action) {
switch (action.type) {
case UPDATE_TIME:
let newState = state;
newState = newState.set('update', true );
newState = newState.set('currentTime', action.time);
return newState;
default:
return state;
}
}
const currentTimeReducer = combineReducers({
currentTime
});
export default currentTimeReducer
Look best practices in this doc

Categories

Resources