flatten redux store

This commit is contained in:
2018-06-07 15:59:33 +03:00
parent e995b6cdbf
commit fe83020a2f
10 changed files with 118 additions and 133 deletions

View File

@@ -1,4 +1,5 @@
import { API_ROOT, getToken } from './util';
import { RECIEVE_TODOS } from './todos';
export const ADD_LIST = 'ADD_LIST';
export const REMOVE_LIST = 'REMOVE_LIST';
@@ -42,9 +43,6 @@ export function stopCreateList() {
export function stopEditList() {
return { type: STOP_EDIT_LIST };
}
function addListToState(list) {
return { type: ADD_LIST, list };
}
export function addList(name) {
return async dispatch => {
@@ -59,16 +57,12 @@ export function addList(name) {
});
const json = await response.json();
const list = json.data;
dispatch(addListToState(list));
dispatch({ type: ADD_LIST, list });
dispatch(changeList(list.id));
dispatch(validateLists());
};
}
function removeListFromState(id) {
return { type: REMOVE_LIST, id };
}
export function removeList() {
return async (dispatch, getState) => {
let state = getState();
@@ -83,7 +77,7 @@ export function removeList() {
});
const json = await response.json();
if (json.success) {
dispatch(removeListFromState(list));
dispatch({ type: REMOVE_LIST, list });
state = getState();
const lists = Object.values(state.lists.lists);
const newList = lists.length ? lists[lists.length - 1].id : '';
@@ -93,10 +87,6 @@ export function removeList() {
};
}
function editListNameInState(id, name) {
return { type: EDIT_LIST_NAME, id, name };
}
export function editList(name) {
return async (dispatch, getState) => {
const state = getState();
@@ -112,12 +102,25 @@ export function editList(name) {
});
const json = await response.json();
if (json.success) {
dispatch(editListNameInState(list, name));
dispatch({ type: EDIT_LIST_NAME, list, name });
}
dispatch(validateLists());
};
}
function normalizeTodos(lists) {
return lists.reduce((todos, list) => {
const listTodosObj = list.todos.reduce(
(listTodos, todo) => ({
...listTodos,
[todo.id]: { ...todo },
}),
{},
);
return { ...todos, ...listTodosObj };
}, {});
}
export function fetchLists() {
return async dispatch => {
dispatch(requestLists());
@@ -135,11 +138,12 @@ export function fetchLists() {
fetching: false,
editing: false,
...list,
todos: [...list.todos.reverse()],
todos: list.todos.map(todo => todo.id),
};
return newObj;
}, {});
dispatch({ type: RECIEVE_TODOS, todos: normalizeTodos(lists) });
dispatch(recieveLists(listsObj));
if (lists.length !== 0) {
dispatch(changeList(listsObj[Object.keys(listsObj)[0]].id));

View File

@@ -16,20 +16,10 @@ export const VisibilityFilters = {
SHOW_ACTIVE: 'SHOW_ACTIVE',
};
function toggleTodoInList(id) {
return { type: TOGGLE_TODO, id };
}
export function setVisibilityFilter(filter) {
return { type: SET_VISIBILITY_FILTER, filter };
}
function requestTodos(list) {
return { type: REQUEST_TODOS, list };
}
function recieveTodos(list, todos) {
return { type: RECIEVE_TODOS, list, todos };
}
function invalidateTodos() {
return { type: INVALIDATE_TODOS };
}
@@ -37,10 +27,6 @@ function validateTodos() {
return { type: VALIDATE_TODOS };
}
function addTodoToList(todo) {
return { type: ADD_TODO, todo };
}
export function addTodo(text) {
return async (dispatch, getState) => {
const state = getState();
@@ -57,16 +43,12 @@ export function addTodo(text) {
});
const json = await response.json();
const todo = json.data;
dispatch(addTodoToList(todo));
dispatch({ type: ADD_TODO, todo });
dispatch(validateTodos());
}
};
}
function removeTodoFromList(id) {
return { type: REMOVE_TODO, id };
}
export function removeTodo(id) {
return async dispatch => {
dispatch(invalidateTodos());
@@ -79,7 +61,7 @@ export function removeTodo(id) {
});
const json = await response.json();
if (json.success) {
dispatch(removeTodoFromList(id));
dispatch({ type: REMOVE_TODO, id });
}
dispatch(validateTodos());
};
@@ -89,8 +71,7 @@ export function toggleTodo(id) {
return async (dispatch, getState) => {
dispatch(invalidateTodos());
const state = getState();
const listObj = state.lists.lists[state.lists.list];
const todoObj = listObj.todos.find(todo => todo.id === id);
const todoObj = state.todos.todos[id];
const completed = !todoObj.completed;
const response = await fetch(`${API_ROOT}/todos/${id}`, {
body: JSON.stringify({ completed }),
@@ -102,16 +83,12 @@ export function toggleTodo(id) {
});
const json = await response.json();
if (json.success) {
dispatch(toggleTodoInList(id));
dispatch({ type: TOGGLE_TODO, id });
}
dispatch(validateTodos());
};
}
function editTodoInList(id, todo) {
return { type: EDIT_TODO, id, todo };
}
export function editTodo(id, text) {
return async dispatch => {
dispatch(invalidateTodos());
@@ -126,7 +103,7 @@ export function editTodo(id, text) {
const json = await response.json();
if (json.success) {
const todo = json.data;
dispatch(editTodoInList(id, todo));
dispatch({ type: EDIT_TODO, id, todo });
}
dispatch(validateTodos());
};
@@ -134,14 +111,14 @@ export function editTodo(id, text) {
export function fetchTodos(list) {
return async dispatch => {
dispatch(requestTodos(list));
const response = await fetch(`${API_ROOT}/lists/${list.id}/todos`, {
dispatch({ type: REQUEST_TODOS, list });
const response = await fetch(`${API_ROOT}/todos`, {
headers: {
Authorization: `Bearer ${getToken()}`,
},
});
const json = await response.json();
const todos = json.data;
dispatch(recieveTodos(list, todos));
dispatch({ type: RECIEVE_TODOS, todos });
};
}

View File

@@ -16,4 +16,7 @@ function mapDispatchToProps(dispatch) {
};
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
export default connect(
mapStateToProps,
mapDispatchToProps,
)(App);

View File

@@ -9,14 +9,10 @@ import {
} from '../actions/lists';
function mapStateToProps(state) {
const editing =
state.lists.list && !state.lists.dirty
? state.lists.lists[state.lists.list].editing
: false;
return {
list: state.lists.list,
creating: state.lists.creating,
editing,
editing: state.lists.editing,
};
}
function mapDispatchToProps(dispatch) {

View File

@@ -3,14 +3,10 @@ import Selector from '../components/Selector';
import { changeList, addList, editList } from '../actions/lists';
function mapStateToProps(state) {
const editing =
state.lists.list && !state.lists.dirty
? state.lists.lists[state.lists.list].editing
: false;
return {
lists: state.lists,
list: state.lists.list,
editing,
editing: state.lists.editing,
creating: state.lists.creating,
};
}
@@ -19,7 +15,7 @@ function mapDispatchToProps(dispatch) {
return {
onChange: list => dispatch(changeList(list)),
addList: name => dispatch(addList(name)),
editList: (id, name) => dispatch(editList(id, name)),
editList: name => dispatch(editList(name)),
};
}

View File

@@ -5,23 +5,17 @@ import { toggleTodo, removeTodo, editTodo } from '../actions/todos';
import getVisibleTodos from './getVisibleTodos';
function mapStateToProps(state) {
const { list } = state.lists;
try {
const listObj = state.lists.lists[list];
const listTodos = state.lists.lists[list].todos;
return {
list,
todos: getVisibleTodos(listTodos, state.visibilityFilter),
dirty: listObj.dirty,
};
} catch (e) {
return {
list: '',
todos: [],
dirty: true,
};
}
return {
todos: state.lists.list
? getVisibleTodos(
state.lists.lists[state.lists.list].todos.map(
id => state.todos.todos[id],
),
state.visibilityFilter,
)
: [],
dirty: state.todos.dirty,
};
}
function mapDispatchToProps(dispatch) {

View File

@@ -17,4 +17,9 @@ function mapDispatchToProps(dispatch) {
};
}
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Todos));
export default withRouter(
connect(
mapStateToProps,
mapDispatchToProps,
)(Todos),
);

View File

@@ -4,9 +4,11 @@ import { reducer as formReducer } from 'redux-form';
import lists from './lists';
import visibilityFilter from './visibilityFilter';
import user from './user';
import todos from './todos';
const todoApp = combineReducers({
lists,
todos,
visibilityFilter,
form: formReducer,
user,

View File

@@ -12,17 +12,7 @@ import {
STOP_CREATE_LIST,
STOP_EDIT_LIST,
} from '../actions/lists';
import {
ADD_TODO,
INVALIDATE_TODOS,
VALIDATE_TODOS,
REQUEST_TODOS,
RECIEVE_TODOS,
REMOVE_TODO,
TOGGLE_TODO,
EDIT_TODO,
} from '../actions/todos';
import list from './list';
import { REMOVE_TODO, ADD_TODO } from '../actions/todos';
export default function lists(
state = {
@@ -32,6 +22,7 @@ export default function lists(
loaded: false,
creating: false,
list: null,
editing: false,
},
action,
) {
@@ -64,45 +55,60 @@ export default function lists(
};
case REMOVE_LIST: {
const newLists = { ...state.lists };
delete newLists[action.id];
delete newLists[action.list];
return {
...state,
list: null,
lists: newLists,
};
}
case START_EDIT_LIST: {
return {
...state,
lists: {
...state.lists,
[state.list]: {
...state.lists[state.list],
editing: true,
},
},
editing: true,
};
}
case STOP_EDIT_LIST: {
return {
...state,
lists: {
...state.lists,
[state.list]: {
...state.lists[state.list],
editing: false,
},
},
editing: false,
};
}
case EDIT_LIST_NAME: {
return {
...state,
editing: false,
lists: {
...state.lists,
[action.id]: {
...state.lists[action.id],
[action.list]: {
...state.lists[action.list],
name: action.name,
editing: false,
},
},
};
}
case REMOVE_TODO: {
return {
...state,
lists: {
...state.lists,
[state.list]: {
...state.lists[state.list],
todos: state.lists[state.list].todos.filter(
todo => todo !== action.id,
),
},
},
};
}
case ADD_TODO: {
return {
...state,
lists: {
...state.lists,
[state.list]: {
...state.lists[state.list],
todos: [action.todo.id, ...state.lists[state.list].todos],
},
},
};
@@ -122,21 +128,6 @@ export default function lists(
...state,
fetching: true,
};
case RECIEVE_TODOS:
case ADD_TODO:
case EDIT_TODO:
case INVALIDATE_TODOS:
case VALIDATE_TODOS:
case REQUEST_TODOS:
case REMOVE_TODO:
case TOGGLE_TODO:
return {
...state,
lists: {
...state.lists,
[state.list]: list(state.lists[state.list], action),
},
};
default:
return state;
}

View File

@@ -8,13 +8,13 @@ import {
VALIDATE_TODOS,
EDIT_TODO,
} from '../actions/todos';
import { REMOVE_LIST } from '../actions/lists';
export default function todos(
state = {
dirty: true,
fetching: false,
todos: null,
editing: false,
},
action,
) {
@@ -29,7 +29,7 @@ export default function todos(
case ADD_TODO:
return {
...state,
todos: [action.todo, ...state.todos],
todos: { [action.todo.id]: action.todo, ...state.todos },
};
case INVALIDATE_TODOS:
return {
@@ -44,29 +44,46 @@ export default function todos(
case EDIT_TODO:
return {
...state,
todos: state.todos.map(
todo => (todo.id === action.id ? action.todo : todo),
),
todos: {
...state.todos,
[action.id]: action.todo,
},
};
case REQUEST_TODOS:
return {
...state,
fetching: true,
};
case REMOVE_TODO:
case REMOVE_TODO: {
const newTodos = { ...state.todos };
delete newTodos[action.id];
return {
...state,
todos: state.todos.filter(todo => todo.id !== action.id),
todos: newTodos,
};
}
case REMOVE_LIST: {
const newTodos = { ...state.todos };
Object.keys(newTodos).forEach(todoId => {
if (newTodos[todoId].list === action.list) {
delete newTodos[todoId];
}
});
return {
...state,
todos: newTodos,
};
}
case TOGGLE_TODO: {
return {
...state,
todos: state.todos.map(
todo =>
todo.id === action.id
? { ...todo, completed: !todo.completed }
: todo,
),
todos: {
...state.todos,
[action.id]: {
...state.todos[action.id],
completed: !state.todos[action.id].completed,
},
},
};
}
default: