creating a redux like stores using custom hooks - javascript

Here I implemented redux like store using custom hooks. everything goes well and code executed correctly but problem is that in reducer under switch statement "TOGGLE" there I return a updated state which is finally stored in globalstate but if I returned empty object {} instead of {products: updated} still globalstate updating correctly with a change that has been done in reducer...since i am not passing globalstate reference then how it is updated correctly
and what listeners exactly do in dispatch method in code
import MarkFavMulti from "./MarkFavMulti";
import classes from "./MarkFav.module.css";
import useStore from "../HookStore/Store";
import {reducer2} from "../SampleReducer";
const MarkFav = props => {
const [outfit, dispatch] = useStore(reducer2);
const onClicked = (id) => {
dispatch({type: "TOGGLE", id: id});
}
const element = outfit.products.map((item) => {
return <MarkFavMulti cloth={item.name}
favorite={item.favorite}
price={item.price}
key={item.id}
clicked={onClicked.bind(this, item.id)} />
});
return (
<main className={classes.Markfav}>
{element}
</main>
);
};
export default MarkFav;
import {useState, useEffect} from "react";
let globalState = {};
let listeners = [];
const useStore = (reducer) => {
const setState = useState(globalState)[1];
const dispatch = (action) => {
let curr = Object.assign({},globalState);
const newState = reducer({...curr}, action)
globalState = {...globalState,...newState};
for(let listener of listeners) {
listener(globalState);
}
};
useEffect(()=>{
listeners.push(setState);
return () => {
listeners.filter(item => item !==setState);
}
},[setState]);
return [globalState, dispatch];
};
export const initStore = (initialState) => {
if(initialState) {
globalState = {...globalState, ...initialState};
}
}
export default useStore;
let initialState = {
products: [
{ id: 1, name: "shirt", price: "$12", favorite: false },
{ id: 2, name: "jeans", price: "$42", favorite: false },
{ id: 3, name: "coat", price: "$55", favorite: false },
{ id: 4, name: "shoes", price: "$8", favorite: false },
]
}
const configureStore = () => {
initStore(initialState);
};
export default configureStore;
export const reducer2 = (state=initialState, action) => {
switch (action.type) {
case "TOGGLE":
let update = {...state};
let updated = [...update.products];
updated = updated.map(item => {
if(item.id === action.id) {
item.favorite = !item.favorite;
return item;
}
return item;
});
return {products: updated};
//if we return {} ...it will updated correctly in globalstate
default:
throw new Error("not reachable");
}
}

The behavior that you are describing is due to this object assignment right here:
item.favorite = !item.favorite;
Here you are directly mutating the properties of the item object. You probably thought that it would be fine since you are using a copy of the products array.
let update = {...state};
let updated = [...update.products];
What actually happens is that updated is a "shallow copy" of the original array. The array itself is a new array, but the items in that array are the same items as in the state. You can read more about that here.
You need to return a new item object instead of mutating it. Here's a concise way to write it using the ternary operator.
case "TOGGLE":
return {
...state, // not actually necessary since products is the only property
products: state.products.map((item) =>
item.id === action.id
? {
...item,
favorite: !item.favorite
}
: item
)
};

Related

Deleted item in redux store array not reflected correctly in react UI

I am having an issue with my input forms displaying the correct information after I delete one. The ComponentInput.js (basically a form) components manage the state of their child inputs/dropdown selections using a useReducer function. Within a useEffect call, the redux store is updated once the user has filled out all of the inputs. The forms are displayed (using a carousel) by mapping the components array in the redux store.
When I delete one of the forms ('case DELETE_COMPONENT' in reducer.js using a componentStateHandler function found in helpers.js), the correct item is removed from the components array in the redux store, however the UI does not correctly reflect that deletion and appears to remove the last item in the array. (I tried using object references rather than array indexing and was still experiencing the same issue).
Example of the issue below. I have attached the code for the redux store, helper functions (can view how state is acted upon in this file), and the Parent(Carousel.js)/Child(ComponentInputs.js) components in question.
//Issue
const componentsOnScreen = [<ComponentInput dataInReducer={{ name: 'Mike' }} />, <ComponentInput dataInReducer={{ name: 'Sarah' }} />, <ComponentInput dataInReducer={{ name: 'James' }} />]
const dataInStore = [{ name: 'Mike' }, { name: 'Sarah' }, { name: 'James' }]
//call delete function to remove Sarah using array index
const componentsOnScreen = [<ComponentInput dataInReducer={{ name: 'Mike' }} />, <ComponentInput dataInReducer={{ name: 'Sarah' }} />]
const dataInStore = [{ name: 'Mike' }, { name: 'James' }]
One fix was to set the initial useReducer state to the data stored within the components array in the redux store that is mapped over in order to display a form to the UI. The problem now is that the "update the store" function in the useEffect is called twice.
Thoughts? Thanks much.
Carousel.js
import React, { Children } from 'react'
import ComponentInput from './ComponentInput.component'
import { PlaceholderIcons } from './PlaceholderIcons.component';
import { ControlledCarousel } from '../common/ControlledCarousel.component';
import Box from '#material-ui/core/Box';
import { makeStyles } from '#material-ui/core/styles'
import { connect } from "react-redux";
import { getComponents } from '../../redux/selectors';
const useStyles = makeStyles(theme => ({
carousel: {
width: '800px'
}
}))
const MixComponentCarousel = ({ letter, newSlideIndex, components }) => {
const classes = useStyles()
return (
<Box className={classes.carousel}>
<ControlledCarousel
dots={false}
newSlideIndex={newSlideIndex}
>
{
components.length !== 0 ?
Children.toArray(components.map((componentProps, i) => {
return <ComponentInput
id={`solution_${i}`}
index={i}
letter={letter}
componentProps={componentProps}
/>
}
)) : <PlaceholderIcons />
}
</ControlledCarousel>
</Box>
)
}
const mapStateToProps = (state, ownProps) => {
const { solutionMixReducer } = state
const { letter } = ownProps
const components = getComponents(solutionMixReducer, letter)
return {
components
}
}
export default connect(mapStateToProps)(MixComponentCarousel)
ComponentInput.js
import React, { useReducer, useEffect, useState } from 'react'
import { BottomRightInputs } from './bottom_input_sections/bottom-right-inputs.section.jsx'
import { BottomLeftInputs } from './bottom_input_sections/bottom-left-inputs.section';
import Box from '#material-ui/core/Box';
import { connect } from "react-redux";
import { updateComponent } from '../../redux/actions/actions';
const ComponentInput = (props) => {
const {
index,
letter,
updateComponent,
componentProps
} = props
const [inputFocused, setInputFocused] = useState(false)
const [inputs, dispatch] = useReducer((state = componentProps, action) => {
return {
...state,
[action.name]: action.value,
}
})
const handleChange = (e) => {
const { name, value } = e.target
dispatch({
value: value,
name: name,
})
}
const handleInputFocusChange = () => {
setInputFocused(prev => !prev)
}
useEffect(() => {
if (inputs) {
const entries = Object.entries(inputs)
if (entries.length === 6) {
if (!inputFocused) {
updateComponent(letter, inputs, index)
}
}
}
}, [inputs, componentProps, updateComponent, letter, index])
return (
<Box
display='flex'
>
<BottomLeftInputs
handleChange={handleChange}
setInputFocused={handleInputFocusChange}
/>
<BottomRightInputs />
</Box>
)
}
export default connect(
null,
{ updateComponent }
)(ComponentInput)
reducer.js
import { ADD_MIX, DELETE_MIX, ADD_COMPONENT, DELETE_COMPONENT, UPDATE_COMPONENT } from '../actions/actionTypes'
import { addMix, deleteMix, componentStateHandler } from './helpers'
const randomColor = require('randomcolor');
const initialState = {
letterCount: 1,
solutionMixes: [
{
letter: 'A',
color: randomColor(),
components: []
}
]
}
const solutionMixReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_MIX:
return addMix(state)
case DELETE_MIX:
return deleteMix(state, action.payload)
case ADD_COMPONENT:
return componentStateHandler(state, action)
case DELETE_COMPONENT:
return componentStateHandler(state, action)
case UPDATE_COMPONENT:
return componentStateHandler(state, action)
default:
return state
}
}
export { solutionMixReducer }
helpers.js
import { ADD_COMPONENT, UPDATE_COMPONENT, DELETE_COMPONENT } from '../actions/actionTypes'
const randomColor = require('randomcolor');
const generateLetter = (n) => {
const chr = String.fromCharCode(97 + n)
return chr.toUpperCase()
}
const addMix = (state) => {
const newLetter = generateLetter(state.letterCount)
const newSolutionMix = {
title: `Master Mix ${newLetter}`,
letter: newLetter,
color: randomColor(),
components: []
}
return {
...state,
letterCount: state.letterCount + 1,
solutionMixes: [...state.solutionMixes, newSolutionMix]
}
}
const deleteMix = (state, letter) => {
if (state.solutionMixes.length !== 1) {
const filtered = state.solutionMixes.filter(item => item.letter !== letter)
return { ...state, solutionMixes: filtered }
}
return state
}
const componentActionHandler = (action, mix) => {
let modification = ''
switch (action.type) {
case ADD_COMPONENT:
modification = [...mix.components, {}]
break
case DELETE_COMPONENT:
modification = mix.components.filter((_, i) => i !== action.payload.index)
break
case UPDATE_COMPONENT:
modification = mix.components.map((comp, i) => i === action.payload.index ? action.payload.component : comp)
break
default:
break;
}
return modification
}
const componentStateHandler = (state, action) => {
return {
...state,
solutionMixes: state.solutionMixes.map(mix =>
mix.letter === action.payload.letter ?
{
...mix,
components: componentActionHandler(action, mix)
}
: mix
)
}
}
export { addMix, deleteMix, componentStateHandler }

How do I access specific items in an object of arrays in reducers

I want to toggle an item's completed property by with clicking. The problem is I have no idea how to do this within reducer state. The property is stored inside an object of arrays which makes it tricky to locate in reducer.
App.js
import React,{ useReducer,useState } from 'react';
import logo from './logo.svg';
import './App.css';
import {reducer, initialState} from "./reducers/reducer"
function App() {
const [item,setItem] = useState("")
const [state,dispatch] = useReducer(reducer,initialState)
const handleCompleted = () => {
dispatch({type:"TOGGLE_COMPLETED",payload:0})
console.log(state[0])
}
const handleChanges = e => {
setItem(e.target.value)
}
const addTodo = e => {
dispatch({type:"ADD_TODO",newItem:{item:item,id:Date.now(),completed:false}})
e.preventDefault()
console.log(state)
}
return (
<form onSubmit={addTodo}>
<button>submitTodo</button>
<input onChange={handleChanges} value={item} />
<div>
<button onClick={handleCompleted}>completed</button>
{state.list.map(i => <p key ={i.id}>{i.item}</p>)}
</div>
</form>
);
}
export default App;
Reducer.js
export const initialState = {
list :[{item: 'Learn about reducers',
completed: false,
id: 3892987589}]
}
export const reducer = (state,action) => {
switch(action.type){
case "TOGGLE_COMPLETED" :
return state.list[action.payload].completed = !state.list[action.payload].completed
case "ADD_TODO" :
return {...state,list:[...state.list,action.newItem]}
default:
return state}
}
you can use findIndex. In your case use the id to find the index in the array then change the status of completed
const initialState = {
list: [{
item: 'Learn about reducers',
completed: false,
id: 3892987589
}]
}
function changeStatus(id) {
let getIndex = initialState.list.findIndex(e => e.id === id);
initialState.list[getIndex].completed = true;
}
changeStatus(3892987589);
console.log(initialState.list)
You can modify the function as follows in your App.js
const handleCompleted = (id) => {
dispatch({type:"TOGGLE_COMPLETED",payload:id})
}
In the render function
change <button onClick={handleCompleted}>completed</button> to
{state.list.map(i => <p key ={i.id}>{i.item}<button onClick={this.handleCompleted.bind(this, i,id)}>completed</button></p>)}<button onClick={this.handleCompleted.bind(this, id)}>completed</button>
And in Reducers.js
Note: Usually, you would create a map of ids corresponding to the items for ease of updates. But this would also work for now.
export const reducer = (state,action) => {
switch(action.type){
case "TOGGLE_COMPLETED" :
const modifiedState = state.map(item=> {
if(item.id === action.payload.id){
return {
...item,
completed: true
}
}
return item
});
return modifiedState
case "ADD_TODO" :
return {...state,list:[...state.list,action.newItem]}
default:
return state}
}

Initial default state is not showing, displaying empty

This is from a tutorial assignment from Dave Ceddia's Redux course, I am trying to display the initial state, which contains an array of objects, however it is simply returning undefined and not displaying anything. I am new to React, and I have hit a wall on getting 1) my buttons to display the state, and 2) default state to appear initially.
I have tried to have my component Buttons as a class, and constant.
I have tried stating my initialReducer in the default: return state; in my reducer as well. I have also tried different syntax for my dispatch actions, but nothing seems to be getting to the reducer.
index.js
import React, { Fragment } from "react";
import ReactDOM from "react-dom";
import { getAllItems, addEventToBeginning, addEventToEnd } from "./actions";
import { connect, Provider } from "react-redux";
import { store } from "./reducers";
const Buttons = ({
state,
getAllItems,
addEventToBeginning,
addEventToEnd
}) => (
<React.Fragment>
<ul>{state ? state.actions.map(item => <li>{item}</li>) : []}</ul>
<button onClick={getAllItems}> Display items </button>
<button onClick={addEventToBeginning}> addEventToBeginning </button>
<button onClick={addEventToEnd}> addEventToEnd </button>
</React.Fragment>
);
const mapDispatchToProps = { getAllItems, addEventToBeginning, addEventToEnd };
const mapStateToProps = state => ({
actions: state.actions,
sum: state.sum
});
connect(
mapStateToProps,
mapDispatchToProps
)(Buttons);
reducers.js
const initialState = {
actions: [
{ id: 0, type: "SALE", value: 3.99 },
{ id: 1, type: "REFUND", value: -1.99 },
{ id: 2, type: "SALE", value: 17.49 }
],
sum: 0
};
const newUnit = { id: Math.random * 10, type: "SALE", value: Math.random * 25 };
function eventReducer(state = initialState, action) {
switch (action.type) {
case ADD_EVENT_TO_BEGINNING:
const copy = { ...state };
copy.actions.unshift(newUnit);
return copy;
case ADD_EVENT_TO_END:
const copy2 = { ...state };
copy2.actions.unshift(newUnit);
return copy2;
cut out for cleanliness
case GET_ITEMS:
return {
...state,
actions: state.actions,
sum: state.sum
};
default:
return state;
}
}
export const store = createStore(eventReducer);
example of actions.js (they all follow same format)
export const ADD_EVENT_TO_BEGINNING = "ADD_EVENT_TO_BEGINNING";
export function addEventToBeginning() {
return dispatch => {
dispatch({
type: ADD_EVENT_TO_BEGINNING
});
};
}
UPDATE:
Thank you #ravibagul91 and #Yurui_Zhang, I cut everything but getAllItems out, and changed the state to:
const initialState = {
itemsById: [
{ id: 0, type: "SALE", value: 3.99 },
{ id: 1, type: "REFUND", value: -1.99 },
{ id: 2, type: "SALE", value: 17.49 }
]
};
class Form extends React.Component {
render() {
return (
<div>
{this.props.itemsById
? this.props.itemsById.map(item => (
<li>
{item.id} {item.type} {item.value}
</li>
))
: []}
<button onClick={this.getAllItems}> Display items </button>
</div>
);
}
}
const mapDispatchToProps = { getAllItems };
function mapStateToProps(state) {
return {
itemsById: state.itemsById
};
}
export function getAllItems() {
return dispatch => ({
type: "GET_ITEMS"
});
}
There are multiple problems with your code:
const mapStateToProps = state => ({
actions: state.actions,
sum: state.sum
});
Here you have mapped redux state fields to props actions and sum - your component won't receive a state prop, instead it will receive actions and sum directly.
so your component really should be:
const Button = ({
actions,
sum,
}) => (
<>
<ul>{actions && actions.map(item => <li>{item}</li>)}</ul>
</>
);
your mapDispatchToProps function is not defined correctly. It should be something like this:
// ideally you don't want the function names in your component to be the same as the ones you imported so I'm renaming it here:
import { getAllItems as getAllItemsAction } from "./actions";
// you need to actually `dispatch` the action
const mapDispatchToProps = (dispatch) => ({
getAllItems: () => dispatch(getAllItemsAction()),
});
Your reducer doesn't seem to be defined correctly as well, however you should try to fix the problems I mentioned above first :)
Try not to do too much in one go when you are learning react/redux. I'd recommend reviewing the basics (how the data flow works, how to map state from the redux store to your component, what is an action-creator, etc.).
As you are destructuring the props,
const Buttons = ({
state,
getAllItems,
addEventToBeginning,
addEventToEnd
}) => ( ...
You don't have access to state, instead you need to directly use actions and sum like,
const Buttons = ({
actions, // get the actions directly
sum, // get the sum directly
getAllItems,
addEventToBeginning,
addEventToEnd
}) => (
<React.Fragment>
//You cannot print object directly, need to print some values like item.type / item.value
<ul>{actions && actions.length && actions.map(item => <li>{item.type} {item.value}</li>)}</ul>
<button onClick={getAllItems}> Display items </button>
<button onClick={addEventToBeginning}> addEventToBeginning </button>
<button onClick={addEventToEnd}> addEventToEnd </button>
</React.Fragment>
);
Your mapDispatchToProps should be,
const mapDispatchToProps = dispatch => {
return {
// dispatching actions returned by action creators
getAllItems : () => dispatch(getAllItems()),
addEventToBeginning : () => dispatch(addEventToBeginning()),
addEventToEnd : () => dispatch(addEventToEnd())
}
}
Or you can make use of bindActionCreators,
import { bindActionCreators } from 'redux'
function mapDispatchToProps(dispatch) {
return {
dispatch,
...bindActionCreators({ getAllItems, addEventToBeginning, addEventToEnd }, dispatch)
}
}
In reducer, ADD_EVENT_TO_END should add element to end of the array, but you are adding again at the beginning using unshift. You should use push which will add element at the end of array,
case ADD_EVENT_TO_END:
const copy2 = { ...state };
copy2.actions.push(newUnit); //Add element at the end
return copy2;
Also your GET_ITEMS should be as simple as,
case GET_ITEMS:
return state;

Delete Item from Array (React-Native + Redux)

I have the checklist with users and when I click on the checkbox user should add to the InputField or delete from InputField, if I check to it again.
For now works only ADD.
import ...
export default class NewEvent extends React.Component {
constructor(props) {
super(props);
this.onSelect = this.onSelect.bind(this);
}
onSelect = id => {
addMembers(id) }
findSelectedContacts = (contacts, membersArray) => {
const newArr = [];
contacts.forEach(item => {
if(membersArray.indexOf(item.id.toString()) > -1) {
newArr.push(` ${item.name}`)
}
});
return newArr;
}
render() {
const { navigation, members, location, contacts } = this.props;
const membersArray = members ? members.split(',') : [];
const selectedArray = this.findSelectedContacts(contacts, membersArray)
const inputFill = selectedArray.join().trim();
return (
<InputField
customStyle={[eventStyles.input]}
icon="addGuest"
placeholder="Add guests"
onGetText={texts => {
this.handlerChangeText(texts)
}}
value={inputFill}
/>
);
}
}
Also, I have reducer, which adds guests to input:
import { handleActions } from 'redux-actions';
import * as types from '../actions/actionTypes';
export const initialState = {
members: '',
};
const addMembers = (members, id) => {
const res = members ? `${members},${id}` : `${id}`;
return res;
}
export default handleActions(
{
[types.ADD_GUEST]: (state, action) => ({
...state,
members: addMembers(state.members, action.payload),
}),
},
initialState
);
Please advise, how I can change my reducer? I need to add or delete the user from InputFiled if I click on the ONE checkbox.
Currently, it appears that you are storing the members list as a comma-separated string. A better option would be to store the list as an actual array, and then convert that to a string when it's needed in that format, e.g. rendering.
The reducer for it might look something like this (trying to follow your existing code style:
export const initialState = {
// initialState changed to array
members: [],
};
const addMember = (members, id) => {
// add id to the end of the list
return members.concat(id);
}
const removeMember = (members, id) => {
// return a new list with all values, except the matched id
return members.filter(memberId => memberId !== id);
}
export default handleActions(
{
[types.ADD_GUEST]: (state, action) => ({
...state,
members: addMember(state.members, action.payload),
}),
[types.REMOVE_GUEST]: (state, action) => ({
...state,
members: removeMember(state.members, action.payload),
}),
},
initialState
);
And if you then need the list as a string, in your component render() method - or preferrably in your react-redux mapStateToProps selector you can convert it to a string:
memberList = state.members.join(',')

Redux Component will not update on store change

I'm trying to get to grips with Redux + React - I have hooked up the relevant bits of Redux with connect() for a small todo app but I cannot for the life of me get the component to update and show the reflected store changes. The store state does update however the component will not. Here are the relevant bits in my code:
actionTypes.js
export const ADD_TODO = "ADD_TODO";
export const DELETE_TODO = "DELETE_TODO";
export const CLEAR_TODO = "CLEAR_TODO";
export const COMPLETE_TODO = "COMPLETE_TODO";
reducers.js
import {ADD_TODO, COMPLETE_TODO, DELETE_TODO, CLEAR_TODO} from '../actions/actionTypes';
const todoApp = (state, action) => {
let updatedState;
switch (action.type) {
case ADD_TODO:
updatedState = Object.assign({}, state);
updatedState.todo.items.push({
text: action.text,
completed: false
});
return updatedState;
case COMPLETE_TODO:
updatedState = Object.assign({}, state);
updatedState.todo.items[action.index].completed = true;
return updatedState;
case DELETE_TODO:
const items = [].concat(state.todo.items);
items.splice(action.index, 1);
return Object.assign({}, state, {
todo: {
items: items
}
});
case CLEAR_TODO:
return Object.assign({}, state, {
todo: {
items: []
}
});
default:
return state;
}
};
export default todoApp;
actions.js
import {ADD_TODO, COMPLETE_TODO, DELETE_TODO, CLEAR_TODO} from './actionTypes.js';
export const addTodoCreator = (text) => {
return {
type: ADD_TODO,
text: text,
completed: false
}
};
export const completeTodo = (index) => {
return {
type: COMPLETE_TODO,
index: index
}
};
export const deleteTodo = (index) => {
return {
type: DELETE_TODO,
index: index
}
};
export const clearTodo = (index) => {
return {
type: CLEAR_TODO,
index: index
}
};
AddTodoContainer.js
import { connect } from 'react-redux';
import TodoList from '../components/TodoList';
const mapStateToProps = (state, ownProps) => {
return {
todo: state.todo
}
};
export default connect(mapStateToProps)(TodoList);
TodoListContainer.js
import { connect } from 'react-redux';
import {addTodoCreator} from '../actions/actions';
import AddTodo from '../components/AddTodo';
const mapStateToProps = (state) => {
console.log(state);
return {
todo: state.todo
}
};
const mapDispatchToProps = (dispatch) => {
return {
addTodo: (text) => {
const action = addTodoCreator(text);
dispatch(action);
},
}
};
export default connect(mapStateToProps, mapDispatchToProps)(AddTodo);
AddTodo.js
import React from 'react'
const handler = (addTodo) => {
const text = document.getElementById('textInput').value;
addTodo(text);
};
const AddTodo = ({addTodo}) => {
return (
<div>
<input id="textInput" type="text" className="textInput" />
<button onClick={(handler).bind(null, addTodo)}>Add</button>
</div>
)
}
export default AddTodo
TodoList.js
import React from 'react';
import AddTodoContainer from '../containers/AddTodoContainer';
class TodoList extends React.Component {
render () {
console.log(this.props);
return (
<div>
<ul>
{this.props.todo.items.map((item) => {
return <li>
{item.text}
</li>
})}
</ul>
<AddTodoContainer/>
</div>
)
}
}
export default TodoList;
I've tried all of the suggestions under Troubleshooting and as far as I can tell I am not mutating state. The reducer is firing and I can log out the states. The code is stored here under react-fulltodo http://gogs.dev.dylanscott.me/dylanrhysscott/learn-redux
Thanks
Dylan
You're passing todo to your component and while the todo object gets updated the actual todo object in redux state is the same exact object as it was before. So react does not see the object as changed. For example:
const a = { foo: 'bar' };
const b = a;
b.foo = 'I made a change';
console.log(a==b);
// logs true because a and b are the same object
// This is exactly what's happening in React.
// It sees the object as the same, so it does not update.
You need to clone the todo object so that react sees it as a changed/new object.
In your reducer:
switch (action.type) {
case ADD_TODO:
updatedState = Object.assign({}, state);
// Shallow clone updatedState.todo
updatedState.todo = Object.assign({}, updatedState.todo);
updatedState.todo.items.push({
text: action.text,
completed: false
});
return updatedState;
Meanwhile, if you passed state.todo.items to your component you would not have to clone todo but you would have to clone items. So in the future, if you have a component that directly mapStateToProps with state.todo.items, it will have the same problem because you are not cloning the items array in ADD_TODO like you are in the DELETE_TODO reducer.

Categories

Resources