React Redux: Connected child component not receiving props - javascript

I can't seem to figure out what the issue is. I have a component called DisplayItems that gets some data from an API (a list of items).
I am using Redux actions and thunk middleware to get the items and data from the API. I save the items and other data to the redux store as an array of objects, and I name it entriesData. The actual array is in entriesData.data. I am using connect to connect the state and actions to DisplayItems' props.
Next, I have a child component called GalleryItemContentV1. GalleryItemContentV1 isn't connected to redux. It just receives the data from DisplayItems which loops through the array of items and passes each one to GalleryItemContentV1. So, again, DisplayItems loops through this.props.entriesData.data and passes each item to GalleryItemContentV1 as a prop called entry.
Finally, I have a child component in GalleryItemContentV1 called TestCom. That's where the issue is. I am also passing the item that I got from DisplyItem to TestCom. So, the data flow is DisplayItems (connected) > GalleryItemContentV1 (not connected) > TestCom (connected).
TestCom is connected to redux. It uses some actions from redux to update an item in entriesData.data. When I update this.props.entriesData.data in TestCom, GalleryItemContentV1 receives the updated data just fine, but TestCom doesn't receive anything. The item in TestCom is never updated.
I figured out that the issue is when I connect TestCom to redux. If TestCom is not connected to redux, it also receives the updated props, just like GalleryItemContentV1.
The components are below. I've tried to simplify them as much as possible.
DisplayItems
import React, {Component} from 'react';
import GalleryItemContentV1 from './GalleryItemContentV1';
import { connect } from 'react-redux';
import {getContestEntriesPageData} from '../../actions/contest';
class DisplayItems extends Component {
componentDidMount(){
this.props.getContestEntriesPageData(uri, search);
}
render(){
console.log('DisplayItems props', this.props);
return (
<div>
<div className="card-deck thumbnails" id="card-list">
{ this.props.entriesData.data instanceof Array ?
this.props.entriesData.data.map(function(object, i){
return <GalleryItemContentV1
entry={object} key={i}
arr_key={i}
/>;
}, this) : ''
}
</div>
</div>
)
}
}
const mapStateToProps = state => (
{
entriesData: state.entriesData,
}
)
const mapDispatchToProps = (dispatch) => {
return {
getContestEntriesPageData: (uri, search) => {
dispatch(getContestEntriesPageData(uri, search));
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(DisplayItems);
GalleryItemContentV1
import React, { Component } from 'react';
import TestCom from './TestCom';
class GalleryItemContentV1 extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<TestCom entry={this.props.entry} />
</div>
);
}
}
export default GalleryItemContentV1;
TestCom
I am outputting the results of this.props.entry.VotesCount (the item from DisplayItems) in TestCom. That's where the issue occurs. For some reason, when I connect TestCom to redux, its props aren't updated, but when it's disconnected from Redux, then its props are updated.
import React, {Component} from 'react';
import { connect } from 'react-redux';
class TestCom extends Component {
constructor(props) {
super(props);
}
render(){
console.log('TestCom props', this.props);
return (
<div>{this.props.entry.VotesCount}</div>
)
}
}
const mapStateToProps = state => (
{
user: state.user
}
)
export default connect(mapStateToProps)(TestCom);
Here's where I'm configuring redux, but I don't think it's relevant to know since GalleryItemContentV1 is seeing the updated data just fine.
import {createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import ContestReducer from '../reducers/contest';
export default function configureStore(initialState) {
return createStore(
ContestReducer,
initialState,
applyMiddleware(thunk)
);
}
So, to summarize.
DisplayItems (is connected, receives updated data from store okay) > GalleryItemContentV1 (is not connected, receives updates okay) > TestCom (is connected, does not receive updates, but receives updates if disconnected)
Here's my reducer, just in case it helps.
import * as ContestActionTypes from '../actiontypes/contest';
const initialState = {
contest: null,
subscriptions: [],
user: null,
page: null
}
export default function Contest(state=initialState, action) {
console.log('redux action reducer: ', action)
switch(action.type) {
case ContestActionTypes.HANDLE_VOTE:
const newState = { ...state };
if (newState.entriesData) {
const index = newState.entriesData.data.findIndex(x => x.id === action.entry.id)
newState.entriesData.data[index].VotesCount = action.vote;
} else {
newState.entry.VotesCount = action.vote;
}
return newState
default:
return state;
}
}
I also noticed the same thing happening with GalleryItemContentV1. If I connect it, then it stops receiving updates from DisplayItems/the redux store.
Any input is appreciated. Thanks!

You're mutating your state. You're making a shallow copy of the top-level state in that slice reducer, but you aren't making copies of all the nested levels inside. Then, your connected component is extracting state.entriesData, which hasn't changed by reference. So, the connect wrapper component thinks that the state hasn't changed at all, and doesn't re-render your own component.
The Redux docs page on "Immutable Update Patterns" specifically points this out as a common mistake.
If updating nested state immutably is too much of a pain, I'd suggest looking into the immer immutable update library. Also, our new redux-starter-kit package uses Immer internally to let you write simpler reducers.
Also, see my post Idiomatic Redux: The History and Implementation of React-Redux for background and details on how connect works internally.

Yes, I also think you are mutating your state. The following is the TOGGLE_TODO case from reducer. I hope this code snippet will work for your code.
case TOGGLE_TODO:
return Object.assign({}, state, {
todos: state.todos.map((todo, index) => {
if (index === action.index) {
return Object.assign({}, todo, {
completed: !todo.completed
})
}
return todo
})
})
So, in your case if id matches then update is with action.vote. Also read about deep and shallow object copies.

markerikson was right, but I am posting the specific answer that helped me, here.
The problem was that I was trying to update an array inside of an object (entriesData being the object and entriesData.data being the array), using the normal method of Object.assign({}, state, {}) when I should have been using JSON.parse(JSON.stringify(state)). I had to read Mozilla's explanation (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) to understand what was going on. I saw the JSON.parse solution before, but I didn't really understand shallow and deep cloning, and, whatever shallow cloning was, I didn't think I was guilty of it. Turns out, I was.
Here's my updated reducer that works perfectly.
case ContestActionTypes.HANDLE_VOTE:
const newState = JSON.parse(JSON.stringify(state));
if (newState.entriesData) {
const index = newState.entriesData.data.findIndex(x => x.id === action.entry.id)
newState.entriesData.data[index].VotesCount = action.vote;
} else {
newState.entry.VotesCount = action.vote;
}
return newState;

Related

How to access redux-toolkit reducer/action from a class component using react-redux connect()?

I have a redux-toolkit store at store.js.
import { configureStore } from '#reduxjs/toolkit';
import productCurrency from './stateSlices/productCurrency';
const Store = configureStore({
reducer: {
productCurrency: productCurrency,
},
})
export default Store;
The createslice() function itself is in a different file and looks like this below.
import { createSlice } from '#reduxjs/toolkit'
const initialState = {
value: '$',
}
export const productCurrency = createSlice({
name: 'productCurrency',
initialState,
reducers: {
setproductCurrency(state, newState) {
state.value = newState
},
},
})
export const { setproductCurrency } = productCurrency.actions;
export default productCurrency.reducer;
My issue is that I have a class component NavSection that needs to access the initial state and the reducer action setproductCurrency() to change the state. I am trying to use the react-redux connect() function to accomplish that.
const mapStateToProps = (state) => {
const { productCurrency } = state
return { productCurrency: productCurrency.value }
}
const mapDispatchToProps = (dispatch) => {
return {
setproductCurrency: () => dispatch(setproductCurrency()),
dispatch,
}
}
export default connect(mapStateToProps, mapDispatchToProps)(NavSection);
Now, I am able to access the state by ussing this.props.productCurrency. Yet, if I try to access the setproductCurrency() by ussing this.props.setproductCurrency()... Chrome console gives me an error that "this.props.setproductCurrency() is not a function".
Is there a way of fixing this, or am I trying to do something impossible?
UPDATE #1
I think I just moved the ball in the right direction. I changed the onClick function to be an arrow function as shown below.
onClick={() => this.props.setproductCurrency('A$')}
Now, setproductCurrency() is considered a function, but it returns a different error when I click the button...
Objects are not valid as a React child (found: object with keys {type, payload}). If you meant to render a collection of children, use an array instead.
Why would this function now return an object? It is supposed to change the state and trigger a re-render of the page so that the class component can access the newly changed state.
To be clear, RTK has nothing to do with React-Redux, connect, or mapDispatch :)
The current error of "Objects are not valid as a React child" is because your reducer is wrong. A reducer's signature is not (state, newState). It's (state, action). So, your line state.value = newStateis reallystate.value = action`, and that's assigning the entire Redux action object as a value into the state. That's definitely not correct conceptually.
Instead, you need state.value = action.payload.

Redux state update doesn't map to props immediately/synchronous. Best practice?

My issue is the following:
redux state updates won't be mapped to the props immediately. This does not refer to useState of react. I'm referring to react-redux, where the state updates should be synchronous, afaik. I know they could be batched, but not sure if that is relevant here. I assume the update is happening immediately, but the mapping is not. I don't know how to get around this. I tried to replicate the error in a "clean" environment. Here is the Component:
import React from 'react';
function MyComponent({ title, setTitle }) {
const handleclick = e => {
console.log(title);
setTitle("I don't get it.");
console.log(title);
};
return <p onClick={handleclick}>{title}</p>;
}
export default MyComponent;
Here is the ComponentContainer:
import React from 'react';
import { connect } from 'react-redux';
import { setTitle } from '../../reducers/customReducer/actions.js';
import MyComponent from './MyComponent.jsx';
export const MyComponentContainer = props => {
return <MyComponent {...props} />;
};
const mapStateToProps = state => {
return {
title: state.customData.title
};
};
const mapDispatchToProps = dispatch => {
return {
setTitle: newTitle => dispatch(setTitle(newTitle))
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(MyComponentContainer);
I expect the second console.log to output the value I don't get it. But it doesn't. Though, the render update happens properly. Meaning the innerHTML of the p element will be updated and rendered accordingly "a bit later".
How do I make it, that I can access the updated value instantly? This is causing an issue in my real world application. I'm not trying to simply print some value there, but rather, change a value and then call another function, that based on the updated value performs some action. Since the value isn't changed/mapped instantly, it won't work.
Am I doing something that shouldn't be done?

Render array of objects from Redux store as React components

I'm trying to display my initial state from my store. I know my code is not correct but i'm hoping to learn the most simple method for this. Can I simply receive data from the store with props? or do I need some lifecycle event to target data in the store?
Here is my current attempt: I have edited this to include my reducer and I have updated my component as per the comments below.
//store .js
import { createStore, applyMiddleware,compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers'; //the index.js file
const initialState = {
}
const middleware = [thunk]
const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__&& window.__REDUX_DEVTOOLS_EXTENSION__()
)
);
export default store;
Now when trying to map out my props in the below component, I get the error:
this.props.properties.map is not a function
//Properties component that renders a map of my props
import React, { Component } from 'react'
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {getListings} from '../actions/postActions';
class Properties extends Component {
componentDidMount(){
getListings()
}
render() {
const properties = this.props.properties.map(property => (
<div key = {property.id}>
<h3>{property.propertyName}</h3>
<div style={{width: '4em', height:'4em', backgroundColor: 'blue'}}>image</div>
<p>{property.footage}</p>
<p>{property.address}</p>
<p>{property.price}</p>
</div>
))
return (
<div>
<h1>Current Listings</h1>
{properties}
</div>
)
}
}
Properties.propTypes = {
properties: PropTypes.array.isRequired,
newListing: PropTypes.object
}
const mapStateToProps = state =>({
properties: state.properties.properties,
newListing: state.properties.newListing
});
export default connect(mapStateToProps)(Properties)
//my actionsHandler.js
import {GET_LISTINGS, NEW_LISTING} from './types';
export function newProperty(formdata){
return function(dispatch){
dispatch({
type: NEW_LISTING,
payload: formdata
})
}
}
export function getListings(form){
return function(dispatch){
dispatch({
type: GET_LISTINGS,
})
}
}
//my reducer
import {NEW_LISTING, GET_LISTINGS} from '../actions/types';
const initialState={
properties: [
{
id: 1,
propertyName: 'The boogaloo',
footage: '3500 sqft',
address: '123 hill bounty rd',
price:'$ 350,000.00'
}
],
newListing: {}
}
export default function(state=initialState, action){
switch(action.type){
case NEW_LISTING:
return{
...state,
properties:state.properties.push(action.payload)
}
case GET_LISTINGS:
return{
state
}
default:
return state
}
}
use a lifecycle method for updating the component
like shouldComponentUpdate method,
props change Doesn't cause rerender only state change cause rerender,
is such case store the props as state is also a solution (inappropriate)
Your general approach is viable in general, with certain corrections required though:
your state is expected to be an object (not an array as it is currently)
in order for your entire array of objects to appear as a bunch of React elements, you may want to wrap them into some parent component and map() your individual posts as small components that receive corresponding object as props from parent component
You may inquiry the following live-demo as a reference:
//dependencies
const { render } = ReactDOM,
{ createStore } = Redux,
{ connect, Provider } = ReactRedux
//initial state, doomy reducer and basic store (no middleware)
const initialState = {posts:[{id:'0',propertyName:'house',footage:'1800 sqft',address:'108 boogaloo dr. thisplace, CA',price:'$145,300.00'},{id:'1',propertyName:'closet',footage:'110 sqft',address:'23 dirt dr. badplace, NC',price:'$3'},{id:'2',propertyName:'garage',footage:'1000 sqft',address:'10 meadow st. decent, NY',price:'$100,000.00'},{id:'3',propertyName:'fishing shack',footage:'500 sqft',address:'108 fishy dr. forestgrove, NJ',price:'$200,300.00'},{id:'4',propertyName:'huge mansion',footage:'8000 sqft',address:'32 T-Pain st. onlytwentythree, WI',price:'$100.00'},{id:'5',propertyName:'dog house',footage:'200 sqft',address:'2367 goodboy dr. borks, KA',price:'$1000.00'},{id:'6',propertyName:'beehive',footage:'too big to measure',address:'108 stingya dr. Bidibi, NJ',price:'$300.00'},{id:'7',propertyName:'my house',footage:'4000 sqft',address:'42 treeface dr. bigwoods, FL',price:'$190,300.00'},]},
appReducer = (state=initialState, action) => state,
store = createStore(appReducer)
//post ui component
const Post = ({propertyName, footage, address, price}) => (
<div>
<h3>{propertyName}</h3>
<p>footage: {footage}</p>
<p>address: {address}</p>
<p>price: {price}</p>
</div>
)
//wrapper ui component relying on storedPosts property
//that will get connected to global state posts later on
const PostBoard = ({storedPosts}) => (
<div>
{storedPosts.map((post, key) => <Post key={key} {...post} />)}
</div>
)
//connect storedPosts to global state posts
const mapStateToProps = ({posts}) => ({storedPosts: posts}),
PostBoardComponent = connect(mapStateToProps)(PostBoard)
//render Redux Provider
render (
<Provider store={store}>
<PostBoardComponent />
</Provider>,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.1.3/react-redux.min.js"></script><div id="root"></div>
In redux, you will be defining an initial state in reducer and will do some updation or removal of the data from the state using actions and will store i.e. you are updating the object the initial state object will be updated..In few cases you need the initial state directly. So,you need to make a copy of initial state object and perform the tasks on it. When the new object updated the initial state will not have any effect.
You can update using components by directly dispatching an action from the component i.e. mapdispatchtoProps.
You can access the entire store data in component by connecting it i.e. connect(mapStatetoProps, mapDispacthtoProps).
mapSatettoProps will give you store data as state. You can modify it or you can do whatever you want. state access can be done in component also but you will get updated one.

How to update redux state using a react variable passed up to the component from a child

Im trying to update my Redux state in a component using a variable passed up to that component from a child, following a form submital callback. The form submits a users comment, which i want to store in my redux state. I'm unsure how to send this variable into the redux chain so i can use it in my action creator. I want to pass the newComment variable inside handleCommentSubmit into the this.props.getVideoComments() action creator. Here is the code:
CommentSection.js (where i want to update my state)
//redux stuff
import {connect} from 'react-redux'
import {getVideoComments} from '../actions'
class CommentsSection extends React.Component{
constructor(props){
super(props)
//this.state={comments:[], loading:false}
}
componentDidMount(){
this.props.getVideoComments()
}
handleCommentSubmit = (newComment) =>{
// call action creator to dist action to all reducers and update relevant states
this.props.getVideoComments()
//this.setState({comments: [...this.state.comments, newComment]})
//this.setState({comments: newComments},console.log('The current state is now',this.state.comments));
//comment is object with author and message. Add new comment to old comments
//this.setState({comments:[...this.state.comments,newComment]},console.log(this.state, 'state updated'))
}
//Comments are create in comment form, passed up then sent down through commentList to individual comment rendering inside comment.js
// comment form oncommentsubmit running everytime it renders, not only on submital
render(){
const {comments} = this.props
console.log({comments})
return(
<div>
<span><h4> Comments </h4></span>
<div className="ui grid">
<div className = "right floated eight wide column" >
<CommentList comments={comments}/>
</div>
<div className="left floated eight wide column">
<CommentForm onCommentSubmit={this.handleCommentSubmit}/>
</div>
</div>
</div>
)
}
}
//redux stuff
//called following state update
const mapStateToProps = (state) => {
return {comments:state.videoComments}
}
export default connect(mapStateToProps,{getVideoComments:getVideoComments})(CommentsSection)
index.js (for action creators)
import React from 'react'
export const getVideoComments= ()=> {
return (dispatch, getState)=> {
const videoComments = getState().videoComments
return ({
type: 'GET_VIDEO_COMMENTS',
payload: videoComments
})
}
}
videoCommentsReducer.js
import React from 'react'
const videoCommentsReducer=function(state= [], action){ // reads in previous state
switch (action.type){
case 'GET_VIDEO_COMMENTS':
return action.payload //reducer will update state to be payload.videoComments. Action only describes what happened
// reducer describes how what happened effects state. Could also use previous state and action to create new data
default:
return state
}
}
export default videoCommentsReducer
index.js (in reducer folder where they are combined)
import React from 'react'
import {combineReducers} from 'redux'
import videoCommentsReducer from './videoCommentsReducer'
export default combineReducers({
videoComments:videoCommentsReducer
})
From your action creator file, it seems that you are using the redux-thunk middleware, so make sure to import this library and apply it in the store. This codesandbox shows a complete example based on yours.
When using this thunk, make sure to always use the dispatch that it provides in order to send the action to the store. Don't return an object from the bound action creator:
export const getVideoComments = () => {
return (dispatch, getState) => {
const videoComments = getRandomComments();
dispatch({
type: "GET_VIDEO_COMMENTS",
payload: videoComments
});
};
};
Also, it doesn't make sense to use getState here to get the video comments. You would simply update the store with the same state over and over again. getState is useful when you want to interact with a different part of the state, that is outside the reducer that captures your action type.
Use mapDispatchToProps in your CommentSection.js and there's no need to use getState in your action creator.
Action Creator
const getVideoComments = (comments) => ({
type: 'GET_VIDEO_COMMENTS',
payload: comments,
});
CommentSection.js
// handleCommentSubmit
handleCommentSubmit = (newComment) => {
this.props.getVideoComments(newComment); //pass comment to action then access newComment in reducer then add it to your state
}
mapDispatchToProps = (state) => {
getVideoComments: (newComment) => dispatch(getVideoComments(newComment)),
}
export default connect(mapStateToProps, mapDispatchToProps)(CommentsSection);
Reducer.js
case 'GET_VIDEO_COMMENTS':
return [...state, action.payload];
Thought id post my solution as an answer as it combined parts of both answers, but needed bits of both to fully solve the issue.
By combining parts of both the above answers I was able to fully solve the problem. I removed getState() as both fctmolina and Rodrigo Amaral suggested. I also simplified the action creator to returning a javascript object, rather than a function, and so no longer needed to include a dispatch function, or use redux-thunk. I passed the newComment variable into my action creator, and then combined it with my old state inside the reducer. The solution only required a simply definition of the mapDispatchToProps as a JS object containing the action creator getVideoComments, which made it available as a prop to commentSection, and resulted in the action creator being dispatched when the this.props.getVideoComments() function call was made. Here is the altered code:
CommentSection.js
import React from 'react'
import CommentList from './CommentList'
import CommentForm from './CommentForm'
//redux stuff
import {connect} from 'react-redux'
import {getVideoComments} from '../actions'
class CommentsSection extends React.Component{
constructor(props){
super(props)
//this.state={comments:[], loading:false}
}
componentDidMount(){
console.log(this.props.comments)
}
handleCommentSubmit = (newComment) =>{
// call action creator to dist action to all reducers and update relevant states
this.props.getVideoComments(newComment)
}
//Comments are create in comment form, passed up then sent down through commentList to individual comment rendering inside comment.js
// comment form oncommentsubmit running everytime it renders, not only on submital
render(){
const {comments} = this.props
console.log({comments})
return(
<div>
<span><h4> Comments </h4></span>
<div className="ui grid">
<div className = "right floated eight wide column" >
<CommentList comments={comments}/>
</div>
<div className="left floated eight wide column">
<CommentForm onCommentSubmit={this.handleCommentSubmit}/>
</div>
</div>
</div>
)
}
}
//redux stuff
//called following state update
const mapStateToProps = (state) => {
return {comments:state.videoComments}
}
export default connect(mapStateToProps,{getVideoComments:getVideoComments})(CommentsSection)
videoCommentsReducer.js
import React from 'react'
const videoCommentsReducer=function(state= [], action){ // reads in previous state
switch (action.type){
case 'GET_VIDEO_COMMENTS':
return [...state, action.payload] //reducer will update state to be payload.videoComments. Action only describes what happened
// reducer describes how what happened effects state. Could also use previous state and action to create new data
default:
return state
}
}
export default videoCommentsReducer
index.js (for action creator)
import React from 'react'
export const getVideoComments = (newComment) => {
return({
type: 'GET_VIDEO_COMMENTS',
payload: newComment
})
};

Redux -- where should the heavy lifting happen -- reducer, action, container, or presentation component?

I've been trying to implement the container/presentational component paradigm that is taught here. I'm getting a little confused, however, on where some of my code should go.
Let's say I have a simple list of items. When one item is clicked on, it should be removed from the list. Should the code to modify the list go in my reducer, action creator, container component, or presentational component?
Reducer:
case 'REMOVE_ITEM':
return Object.assign({}, state, {items: action.value})
Action creator:
export function removeItem(items) {
return {
type: 'REMOVE_ITEM',
value: items
};
}
And now our container component:
import ItemsList from './ItemsList';
import { connect } from 'react-redux';
import * as actions from './actions';
var mapStateToProps = function(state) {
return {
items: state.itemsList.items
};
};
var mapDispatchToProps = function(dispatch) {
return {
onItemClicked: function(items) {
dispatch(actions.removeItem(items));
}
};
};
var ItemsListContainer = connect(
mapStateToProps,
mapDispatchToProps
)(ItemsList);
module.exports = ItemsListContainer;
And finally the presentational component:
import React from 'react';
module.exports = React.createClass({
showRows: function() {
return this.props.items.map(item => {
return (
<li key={item.id} onClick={this.props.onItemClicked}>{item.title}</li>
);
});
},
render: function() {
return (
<ul>
{this.showRows()}
</ul>
);
}
});
Then, at some point, we'll need some code to remove the list item. When the item is clicked on, we'll need to splice it from the list of items.
Where should that code go?
I could see it going in the presentational component and then calling the onItemClicked callback from the container component after it has modified the list.
I could see it going in the container component so the presentational component is as dumb as can be. I'd need a way to access the state, though (to get the items), and since I'm already passing the items as props into the presentational component, it would make more sense to me to do it there.
I could see it going in the action creator, with the removed item as the second parameter to the removeItem function.
It seems like a bad idea to put it in the reducer, since doing a bunch of calculations (modifying the array) seems like the thing that should happen before the action is dispatched, not after.
Where should the heavy lifting of modifying the array happen? It seems it should go in the presentational component, but I know those are supposed to be dumb components...
You should think about it this way:
DumbComponent (View/Accepts Input) -> SmartComponent (RespondsToInput by firing a dispatch) -> ActionCreator (Creates the necessary action, gives a payload if necessary - in this case, the index you need to filter out) -> Reducer (Responds to the action by modifying and returning a new state.)
In your reducer you'll do something like:
return Object.assign({}, state, { items: state.items.filter((item, index) => index !== payload) });
So do the calculations/heavy lifting in the reducer, if it modifies state, the code should be where the state is modified.

Categories

Resources