Where does the reducer get the state from? - reactjs

I am trying to understand redux with the help of an online todo application resource.
However, I can't seem to figure out where does the 'todos' reducer get the initial state from ?
I've consoled the state but can't seem to wrap my head around it ?
After the initial render, state is consoled 3 times as,
[ ]
[ ]
[ state object ]
Link: 'https://codepen.io/iamrkcheers/pen/rNNoBvB'
Any help is appreciated.
Thank You.
// --------- actions start ----------
const ADD_TODO = "ADD_TODO";
const TOGGLE_TODO = "TOGGLE_TODO";
const SET_VISIBILITY_FILTER = "SET_VISIBILITY_FILTER";
const VisibilityFilters = {
SHOW_ALL: "SHOW_ALL",
SHOW_COMPLETED: "SHOW_COMPLETED",
SHOW_ACTIVE: "SHOW_ACTIVE"
};
let nextTodoId = 3;
function addTodo(text) {
return {
type: ADD_TODO,
id: nextTodoId++,
text
}
}
function toggleTodo(id) {
return {
type: TOGGLE_TODO,
id
}
}
function setVisibilityFilter(filter) {
return {
type: SET_VISIBILITY_FILTER,
filter
}
}
// --------- actions end ----------
// --------- reducers start ----------
function todos(state = [], action) {
console.log('state is:',state);
switch (action.type) {
case ADD_TODO: {
return [...state, {
text: action.text,
completed: false,
id: action.id
}];
}
case TOGGLE_TODO: {
return state.map((todo, id) => {
if (id === action.id) {
return Object.assign({}, todo, {
completed: !todo.completed
});
}
return todo;
});
}
default: {
return state;
}
}
}
function visibilityFilter(state = VisibilityFilters.SHOW_ALL, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER: {
return action.filter;
}
default: {
return state
}
}
}
const todoApp = Redux.combineReducers({
visibilityFilter,
todos
});
// --------- reducers end ----------
// --------- components start ----------
const App = () => {
const getDate = date => new Date(date);
const days = ["Воскресенье", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"];
return (
<div className="block">
<div className="info-date">
<div className="date">{ getDate(Date.now()).toLocaleDateString("ru") }</div>
<div className="day">{ days[getDate(Date.now()).getDay()] }</div>
</div>
<AddTodo />
<Footer />
<VisibleTodoList />
</div>
);
};
const Footer = () => {
return (
<div className="filters">
<FilterLink filter="SHOW_ALL">Все задачи</FilterLink>
<FilterLink filter="SHOW_ACTIVE">Активные</FilterLink>
<FilterLink filter="SHOW_COMPLETED">Завершенные</FilterLink>
</div>
);
};
const Link = ({ active, children, onClick }) => {
if (active) {
return <span className="filter-item non-active">{ children }</span>
}
return (
<a className="filter-item" href="#" onClick = { event => {
event.preventDefault();
onClick();
} }>{ children }</a>
);
};
const Todo = ({ onClick, completed, text }) => {
const styles = {
textDecoration: completed ? "line-through" : "none"
};
return (
<li onClick = { onClick } style = { styles }>
<a>{ text }</a>
</li>
);
};
const TodoList = ({ todos, onTodoClick }) => {
return (
<div className="list">
<ul>
{
todos.map(todo => <Todo
key = { todo.id } { ...todo }
onClick = { () => onTodoClick(todo.id) } />)
}
</ul>
</div>
);
};
// --------- components end ----------
// --------- containers start ----------
let AddTodo = ({ dispatch }) => {
let input;
return (
<div>
<form className="addForm" onSubmit = { event => {
event.preventDefault();
if (!input.value.trim()) {
return;
}
dispatch(addTodo(input.value));
input.value = "";
} }>
<input type="text" placeholder="Что нужно сделать?" ref = { node => input = node }/>
<button type="submit" className="btn"></button>
</form>
</div>
);
};
AddTodo = ReactRedux.connect()(AddTodo);
var mapStateToProps = (state, ownProps) => {
return {
active: ownProps.filter === state.visibilityFilter
};
};
var mapDispatchToProps = (dispatch, ownProps) => {
return {
onClick: () => {
dispatch(setVisibilityFilter(ownProps.filter));
}
};
};
const FilterLink = ReactRedux.connect(
mapStateToProps,
mapDispatchToProps
)(Link);
const getVisibleTodos = (todos, filter) => {
switch (filter) {
case "SHOW_ALL": {
return todos;
}
case "SHOW_COMPLETED": {
return todos.filter(todo => todo.completed);
}
case "SHOW_ACTIVE": {
return todos.filter(todo => !todo.completed);
}
default: {
return todos;
}
}
};
var mapStateToProps = state => {
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
};
var mapDispatchToProps = dispatch => {
return {
onTodoClick: id => {
dispatch(toggleTodo(id));
}
};
};
const VisibleTodoList = ReactRedux.connect(
mapStateToProps,
mapDispatchToProps
)(TodoList);
// --------- containers end ----------
// --------- application start ----------
const initialState = {
visibilityFilter: "SHOW_ALL",
todos: [
{
id: 0,
text: "Изучить React",
completed: true
},
{
id: 1,
text: "Изучить Redux",
completed: true
},
{
id: 2,
text: "Написать приложение \"Список задач\"",
completed: false
}
]
};
let store = Redux.createStore(todoApp, initialState);
ReactDOM.render(
<ReactRedux.Provider store = { store }>
<App />
</ReactRedux.Provider>,
document.querySelector("#root")
);
// --------- application end ----------

You are defining the initial state right here :
function todos(state = [], action) {
Generally, while defining reducers, we also define initialState(state = [] in your case) , which is the state that goes into the reducer till we populate it with data (from an external source like api, or user input).
You can read more on initial state here : https://redux.js.org/recipes/structuring-reducers/initializing-state#initializing-state

there are two ways where you can define initial state;
the first one is in your reducer where you did function
todos(state = [], action) and ,
the second is when you create the store, you can pass initial state as a second argument in your createStore function. In your case , you have a second argument when you create your store which is an array of three todos which you can see when you console log it. store = Redux.createStore(todoApp, initialState), here the reducer gets this initial state

Related

What is the best way to update item in React Redux

I'm learning React Redux and i'm writing todo-list app. The last missing piece is option to editting selected item (onClick). I have problem with doing that.
Could you tell me how i can achieve that?
I looking for solution like that:
Select item which you want to update (by clicking on edit icon)
Form should change into "editing mode" so user can pass new value into editing item.
When value is passed, then user click on submit button.
I don't know how to make editing mode so my input form can handle updating existing item.
// ACTION
export const editTask = (task) => ({
type: "EDIT_TASK",
payload: task,
});
// REDUCER
const INITIAL_STATE = {
tasks: [],
alert: {
show: false,
message: "",
type: "",
},
isEditing: false,
};
const reducer = (state, action) => {
switch (action.type) {
case "EDIT_TASK":
return {
...state,
isEditing: true,
// take current tasks and updatedTask
tasks: [...state.tasks, action.payload],
alert: setAlert(true, "Task has changed succesfull 🌈", "success"),
};
default:
return state;
On my todo.jsx
const TasksTodo = () => {
const [inputValue, setInputValue] = useState("");
const [state, dispatch] = useReducer(reducer, INITIAL_STATE);
const editTaskFromList = (item) => {
// match tasks from list with that selected
const taskToEdit = state.tasks.find(
(taskToFind) => taskToFind.id === item.id
);
const editedTask = {
taskToEdit,
title: inputValue,
};
dispatch(editTask([...state.tasks, editedTask]));
};
const handleSubmit = (e) => {
e.preventDefault();
if (inputValue) {
const newItem = {
id: new Date().getTime().toString(),
title: inputValue,
};
dispatch(addNewTask(newItem));
setInputValue("");
} else {
dispatch({ type: "NO_VALUE" });
}
};
const handleChange = (e) => {
setInputValue(e.target.value);
};
return (
<div className="tasks-list-section">
<h3 className="tasks__title">tasks List </h3>
{state.alert.show && (
<Alert
{...state.alert}
removeAlert={() => dispatch(changeAlertState())}
/>
)}
<form onSubmit={handleSubmit} className="tasks__form">
<input
type="text"
value={inputValue}
placeholder="e.g homework"
className="tasks__input"
onChange={handleChange}
/>
<button type="submit" className="tasks__submit-btn">
{state.isEditing ? "edit" : "submit"}
</button>
</form>
{state.tasks.length > 0 && (
<div className="tasks__container">
<Tasks
listItems={state.tasks}
removeTask={removeTaskFromList}
editTask={editTaskFromList}
/>
</div>
)}
</div>
);
};
TaskList Component:
const Tasks = ({ listItems, editTask, removeTask }) => {
return (
<div className="tasks-list">
{listItems.map((item) => {
const { id, title } = item;
return (
<div key={id} className="tasks__item">
<p className="tasks__item--title">{title}</p>
<div className="tasks__button-group">
<button
type="button"
className="tasks__button-group--edit"
onClick={() => editTask(item)}
>
<RiEditBoxLine className="size" />
</button>
<button
type="button"
className="tasks__button-group--delete"
onClick={() => removeTask(item)}
>
<RiDeleteBin6Line className="size" />
</button>
</div>
</div>
);
})}
</div>
);
};
Please find the simple workaround for this.
Page.js ( Main Component)
import React, { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { addTask, editTask, removeTask } from "./actions";
const TaskList = ({ tasks, removeTask, editTask }) => {
return (
<div>
{tasks.map((item) => (
<div key={item.id}>
<p>{item.title}</p>
<button type="button" onClick={(e) => editTask(item)}>
Edit
</button>
<button type="button" onClick={(e) => removeTask(item)}>
Remove
</button>
</div>
))}
</div>
);
};
function Page() {
const tasks = useSelector((state) => state.tasks);
const dispatch = useDispatch();
const [inputValue, setInputValue] = useState("");
const [editingTaskIndex, setEditingTaskIndex] = useState(null);
const editTaskFromList = (task) => {
const taskToEdit = tasks.find((item) => item.id === task.id);
const taskIndex = tasks.indexOf(taskToEdit);
setEditingTaskIndex(taskIndex);
setInputValue(taskToEdit.title);
};
const removeTaskFromList = (task) => {
const taskToDelete = tasks.find((item) => item.id === task.id);
const taskIndex = tasks.indexOf(taskToDelete);
dispatch(removeTask(taskIndex));
};
const handleSubmit = (e) => {
e.preventDefault();
if (editingTaskIndex === null) {
const newItem = {
id: new Date().getTime().toString(),
title: inputValue
};
dispatch(addTask(newItem));
} else {
const editingItem = tasks[editingTaskIndex];
editingItem.title = inputValue;
dispatch(editTask(editingTaskIndex, editingItem));
}
setInputValue("");
setEditingTaskIndex(null);
};
const handleChange = (e) => {
setInputValue(e.target.value);
};
return (
<div>
<h3>Task List</h3>
<form onSubmit={handleSubmit}>
<input
type="text"
value={inputValue}
placeholder="e.g: homework"
onChange={handleChange}
/>
<button>Submit</button>
</form>
<TaskList
tasks={tasks}
editTask={editTaskFromList}
removeTask={removeTaskFromList}
/>
</div>
);
}
export default Page;
reducer.js
const initialState = {
tasks: [],
isEditing: false
};
function taskReducer(state = initialState, action) {
switch (action.type) {
case "ADD_TASK":
return { ...state, tasks: [...state.tasks, action.payload] };
case "EDIT_TASK":
console.log(action.payload);
const currentTasks = Array.from(state.tasks);
currentTasks[action.payload.taskIndex] = action.payload.task;
return { ...state, tasks: currentTasks };
case "REMOVE_TASK":
const allTasks = Array.from(state.tasks);
allTasks.splice(action.payload, 1);
return {
...state,
tasks: allTasks
};
default:
return state;
}
}
export default taskReducer;
Checkout the sandbox link
I found different solution. Rate my code if you can.
On my reducer
case "EDIT_TASK":
return {
...state,
isEditing: true,
tasks: [
...state.tasks.filter((task) => task.id !== action.payload.id),
action.payload,
],
alert: setAlert(true, "Task has changed succesfull 🌈", "success"),
};
tasks.jsx
const editTaskFromList = (item) => {
const taskToEdit = state.tasks.find(
(taskToFind) => taskToFind.id === item.id
);
setEditingTaskID(taskToEdit.id);
setInputValue(taskToEdit.title);
};
const handleSubmit = (e) => {
e.preventDefault();
if (editingTaskID === null) {
if (inputValue) {
const newItem = {
id: new Date().getTime().toString(),
title: inputValue,
};
dispatch(addNewTask(newItem));
setInputValue("");
} else {
alert("Value cannot be empty!");
}
} else {
const editingItem = state.tasks.find((task) => task.id === editingTaskID);
const newItem = { ...editingItem, title: inputValue };
dispatch(editTask(newItem));
setInputValue("");
setEditingTaskID(null);
}
};

Reducer: Updating an array inside an array [duplicate]

This question already has answers here:
Update nested data arrays of object (Redux)
(3 answers)
Closed 3 years ago.
I'm new to Redux, how would I reference the state of a nested array in my reducer?
Here is my state:
const initState = {
tabs: [
{ arr: [] },
{ arr: [] }
]
};
I tried doing something like:
tabs[0].arr: //do stuff
inside my reducer, but I get a parsing error. What is the correct way to reference the array in the first tab?
Edit:
I am adding more code for clarity
function reducer(state = initState, action) {
if (action.type == 'ADD_ARR') {
return {
state.tabs[0].arr: [...state.tabs[0].arr, action.arr]
}
}
}
//I get this message: 'Parsing error: Unexpected token, expected ","' at the period between 'state' and 'tabs[0]'
The rest of my code
const arrAction = { type: "ADD_ARR", arr: "hello" };
store.dispatch(arrAction);
Try to avoid state mutation, by not updating the array directly. You also need to pass an index of the array you want to modify.
const arrAction = { type: "ADD_ARR", arr: "hello", index: 0 };
store.dispatch(arrAction);
function reducer(state = initState, action) {
if (action.type == 'ADD_ARR') {
const index = action.index;
const arr = [...state.tabs[index].arr, action.arr];
return {
...state,
tabs: [...state.tabs.slice(0, index),
Object.assign({}, state.tabs[index], { arr: [...arr] }),
...state.tabs.slice(index + 1)
]
};
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.1/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.0.3/react-redux.min.js"></script>
<script src="http://wzrd.in/standalone/uuid%2Fv1#latest"></script>
<div id="root"></div>
<script type="text/babel">
const { Provider, connect } = ReactRedux;
const { applyMiddleware, createStore, combineReducers } = Redux;
function addArr(payload) {
return { type: 'ADD_ARR', payload}
}
const initialState = {
tabs: [{ arr: [] }, { arr: [] }]
};
function rootReducer(state = initialState, action) {
if (action.type == 'ADD_ARR') {
const index = action.payload.index;
const arr = [...state.tabs[index].arr, action.payload.title];
return {
...state,
tabs: [
...state.tabs.slice(0, index),
Object.assign({}, state.tabs[index], { arr: [...arr] }),
...state.tabs.slice(index + 1)
]
};
}
return state;
}
const store = createStore(rootReducer);
const mapStateToProps = state => {
return { tabs: state.tabs };
};
function mapDispatchToProps(dispatch) {
return {
addExpense: expense => dispatch(addExpense(expense)),
addArr: e => dispatch(addArr(e))
};
}
const ConnectedList = ({ tabs, addExpense }) => {
const tab1 = tabs[0].arr;
const tab2 = tabs[1].arr;
return (
<div>
{tab1.length > 0 && <p>tabs [0] arr values</p>}
<ul className="list-group list-group-flush">
{tab1.map((el, index) => (
<li className="list-group-item" key={index}>
{el}
</li>
))}
</ul>
{tab2.length > 0 && <p>tabs [1] arr values</p>}
<ul className="list-group list-group-flush">
{tab2.map((el, index) => (
<li className="list-group-item" key={index}>
{el}
</li>
))}
</ul>
</div>
);
};
const List = connect(mapStateToProps)(ConnectedList);
class StuffForm extends React.Component {
state = {
title: '',
};
handleSubmit = (index) => {
if (!this.state.title) {
return;
}
const { title} = this.state;
this.props.addArr({
title,
index
});
this.setState({
title: '',
});
};
handleInput = e => {
this.setState({
[e.target.name]: e.target.value
});
};
render() {
return (
<div>
<input
name="title"
placeholder="title"
onChange={this.handleInput}
value={this.state.title}
/>
<button onClick={() => this.handleSubmit(0)}>Add to arr[0]</button>
<button onClick={() => this.handleSubmit(1)}>Add to arr[1]</button>
</div>
);
}
}
const Form = connect(
null,
mapDispatchToProps
)(StuffForm);
class App extends React.Component {
render() {
return (
<div>
<List />
<Form />
</div>
);
}
}
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
</script>
Your syntax is wrong for updating array.
function reducer(state = initState, action) {
if (action.type == 'ADD_ARR') {
return {
tabs: [ {
arr: action.arr
}, ...state.tabs]
}
}

react reducer initialState didnt show as expexted

var rootReducer = Redux.combineReducers({
reducreForButtonGroup,reducreForButtonGroup2
});
i did set the initialState into the reducre as below, but the Detail button didnt disabled when i load the page .
var initialState2 = {
disabled:false
}
function reducreForButtonGroup2(state = initialState2, action) {
}
var DetailButton = React.createClass({
clickDisable(event) {
this.props.dispatch(clickDisable());
} ,
render() {
const { disable } = this.props;
return (
<ButtonToolbar style={{width: 17+ 'em'}}>
<Button disabled={disable} style={{width: 5 + 'em'}}>Detail</Button>
<Button onClick={this.clickDisable} style={{width: 5 + 'em'}}>Close</Button>
</ButtonToolbar>)
}
}) ;
function select(state) {
return {
disabled: state.disabled
}
}
const NewDetailButton = connect(select)(DetailButton);
New reducer
New reducer i want to add
var initialState = {
value:15
}
Action creators
function clickAdd() {
return {
type: 'CLICK_ADD'
}
}
New reducre
function reducreForButtonGroup(state = initialState, action) {
if (typeof state === 'undefined') {
return 0
}
var value;
switch(action.type) {
case 'CLICK_ADD': {
console.log("2");
return {
value: state.value + 1
}
}
default :{
return state
}
}
}
Component
var ButtonGroup = React.createClass({
clickAdd(event) {
this.props.dispatch(clickAdd());
} ,
render() {
const { value } = this.props;
return (
<ButtonToolbar style={{width: 17+ 'em'}} >
<Button id="search" style={{width: 5 + 'em'}}>{value}</Button>
<Button onClick={this.clickAdd} style={{width: 5 + 'em'}}>Create</Button>
</ButtonToolbar>
);
}
});
Mapstatetoprops i did use the reducre name in the mapStateToProps
function select(state) {
return {
value: state.reducreForButtonGroup.value
}
}
const NewButtonGroup = connect(select)(ButtonGroup);
you have spelling issue, it should be const { disabled } = this.props; and disabled={disabled} and I believe it should work.
Also log out what you get from the state because I believe it should be:
function select(state) {
console.log(state);
return {
disabled: state.reducreForButtonGroup2.disabled
}
}
One more think it is called a reducer not a reducre. You have it misspelled a few times.

Condition on checkbox in React with Redux

This is surely very simple but I dont understand how it works. I try to bind checkbox with state and with state display different string. It is in React with Redux. The code below (bold font)
container:
class DropingList extends Component {
**conditionHandler() {
if(this.props.pet === 'cat'){
return "YEAH!!!"
}else {return null;}**
}
render() {
return (
<div>
<AddHimHer
click={this.props.onAddMan}
/>
{ this.props.pers.map(per =>(
<NewPerson
key={per.id}
click={() => this.props.onManDown(per.id)}
name={per.name}
age={per.age}
**animal={this.conditionHandler(this.props.pet)}**
/>
))
}
</div>
)
}
}
const mapStateToProps = state => {
return {
pers: state.persons
}
}
const mapDispatchToProps = dispatch => {
return {
onAddMan: (name,age,**pet**) => dispatch({type:actionTypes.ADD_MAN, data: {nam: name, ag: age, **superp: pet**}}),
onManDown: (id) => dispatch({type:actionTypes.MAN_DOWN, Id: id})
}
}
export default connect(mapStateToProps,mapDispatchToProps)(DropingList);
component:
const NewPerson = (props) => (
<div onClick={props.click}>
<h1>Is {props.name} a SUPERHERO? ? ???</h1>
<h2>He is {props.age} years old</h2>
**<h1>{props.animal}</h1>**
</div>
);
export default NewPerson;
reducer:
const initState = {
persons: []
}
const personReducer = (state = initState,action) => {
switch (action.type) {
case actionTypes.ADD_MAN:
const newMan = {
id: Math.random(),
name: action.data.nam,
age: action.data.ag,
**pet: action.data.superp**
};
return {
...state,
persons: state.persons.concat(newMan)
};
case actionTypes.MAN_DOWN:
return {
...state,
persons: state.persons.filter(person => person.id !== action.Id)
};
}
return state;
};
export default personReducer;
I am still newbe in React and Redux. I think I have ommited something.
Could you tell me whats wrong with my code?
Issue is pet is the part of the object (each object of the array), not a separate prop so you need to use per.pet in map callback function, like this:
{this.props.pers.map(per =>(
<NewPerson
key={per.id}
click={() => this.props.onManDown(per.id)}
name={per.name}
age={per.age}
animal={this.conditionHandler(per.pet)} // here
/>
))}
Now you are passing the pet value to function conditionHandler, so no need to use this.props.pet inside that directly use pet, like this:
conditionHandler(pet) {
if (pet === 'cat') {
return "YEAH!!!"
} else {
return null;
}
}

Objects in array turn into undefined after using a particular reducer/action

I'm working with a classic to do list project to learn Redux and I'm having a strange issue.
Basically, I have a to-do list with checkboxes and when the user clicks on a checkbox, an action gets dispatched which should mark that object's completed property as true and the component should update.
However... When this action fires, the object which is supposed to be marked as complete successfully returns with all of it's properties but the rest of the todo list (the other objects in the array) get corrupted, losing all their properties and they turn into 'undefined' thus causing problems in the render.
I've tried to include all the code that I think is relevant but I think that I'm doing something wrong in my reducer, but I can't find seem to find the issue.
Todo List Component
class TodoList extends Component {
render(){
const {todos, showCompleted, searchTerm} = this.props;
const renderTodos = () => {
if (todos.length === 0) {
return (
<p className="container__message">Nothing to do.</p>
);
}
return TodoAPI.filterTodos(todos, showCompleted, searchTerm).map((todo) => {
return (
<Todo key={todo.id} {...todo}/>
);
});
};
return (
<div>
{renderTodos()}
</div>
);
}
}
export default connect((state) => {
return state;
})(TodoList);
Todo Component
class Todo extends Component {
render() {
const {id, text, completed, createdAt, completedAt, dispatch} = this.props;
const todoClass = completed
? 'todo todo-completed'
: 'todo';
const renderDate = () => {
let displayMessage = 'Created ';
let timestamp = createdAt;
if (completed) {
displayMessage = 'Completed ';
timestamp = completedAt;
}
return displayMessage + moment.unix(timestamp).format('MMM Do YYYY # h:mm a');
};
return (
<div className={todoClass}
onClick={event => dispatch(actions.toggleTodo(id)) }>
<input type="checkbox" checked={completed} readOnly/>
<div>
<p>{text}</p>
<p className="todo__subtext">{renderDate()}</p>
</div>
</div>
);
}
}
export default connect()(Todo);
Action
export const toggleTodo = (id) => {
return {
type: 'TOGGLE_TODO',
id: id
};
};
Reducer
export const todosReducer = (state = [], action) => {
switch (action.type) {
case 'TOGGLE_TODO':
return state.map((todo) => {
if (todo.id === action.id) {
let nextCompleted = !todo.completed;
return {
...todo,
completed: nextCompleted,
completedAt: todo.completed ? moment().unix() : 0
};
}
});
default:
return state;
}
};
Issue is you are not returning anything if the condition todo.id === action.id fail. With the map if you don't return anything, by default it will return undefined, Try this:
return state.map((todo) => {
if (todo.id === action.id) {
let nextCompleted = !todo.completed;
return {
...todo,
completed: nextCompleted,
completedAt: todo.completed ? moment().unix() : 0
};
}else{
return todo;
}
});
Check this:
a=[1,2,3,4,5,6];
b = a.map ( i => { if(i % 2 == 0) return i;})
console.log(b);

Resources