Created
May 4, 2020 23:33
-
-
Save chukwumaijem/33e5c00ca8ef3c093e42cac5149cd945 to your computer and use it in GitHub Desktop.
Todo App - Redux with GraphQL API
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import axios from 'axios'; | |
| const API_URL = process.env.REACT_APP_GRAPHQL_URL; | |
| export const apiRequest = async (query) => { | |
| try { | |
| const { data: { data } } = await axios.post(API_URL, query); | |
| return data; | |
| } catch (error) { | |
| return { error }; | |
| } | |
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import React from 'react'; | |
| import ReactDOM from 'react-dom'; | |
| import { Provider } from 'react-redux'; | |
| import * as serviceWorker from './serviceWorker'; | |
| import Todos from './Todos/Todos'; | |
| import { store } from './store'; | |
| ReactDOM.render( | |
| <Provider store={store}> | |
| <Todos /> | |
| </Provider>, | |
| document.getElementById('root') | |
| ); | |
| serviceWorker.unregister(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| export const READ_TODOS = ` | |
| query todos { | |
| todos { | |
| id | |
| text | |
| completed | |
| } | |
| } | |
| `; | |
| export const CREATE_TODO = (text) => ` | |
| mutation { | |
| createTodo(text: "${text}") { | |
| id | |
| text | |
| completed | |
| } | |
| } | |
| `; | |
| export const REMOVE_TODO = (id) => ` | |
| mutation { | |
| removeTodo(id: "${id}") | |
| } | |
| `; | |
| export const UPDATE_TODO = (id) => ` | |
| mutation { | |
| updateTodo(id: "${id}") | |
| } | |
| `; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { configureStore } from '@reduxjs/toolkit'; | |
| import todosReducer from './Todos/TodosSlice'; | |
| export const store = configureStore({ | |
| reducer: todosReducer, | |
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import React from 'react'; | |
| import { connect } from 'react-redux'; | |
| import { deleteTodo, updateTodo } from './TodosSlice'; | |
| import { REMOVE_TODO, UPDATE_TODO } from './queries'; | |
| function Todo({ todo, deleteTodoAction, updateTodoAction }) { | |
| const { id, text, completed } = todo; | |
| const handleTodoDelete = () => { | |
| deleteTodoAction(REMOVE_TODO(id)); | |
| }; | |
| const handleTodoUpdate = () => { | |
| updateTodoAction(UPDATE_TODO(id)); | |
| }; | |
| return ( | |
| <div | |
| className={` | |
| row | |
| card-body | |
| list-group-item-${completed ? 'success' : 'info'} | |
| `} | |
| > | |
| <span className="col-sm-8">{text}</span> | |
| <button | |
| onClick={handleTodoUpdate} | |
| className={`col-sm-2 btn btn-${completed ? 'success' : 'info'}`} | |
| type="button" | |
| > | |
| {completed ? 'Done' : 'Pending'} | |
| </button> | |
| <button | |
| onClick={handleTodoDelete} | |
| className="col-sm-2 btn btn-danger" | |
| type="button" | |
| > | |
| Remove | |
| </button> | |
| </div> | |
| ); | |
| } | |
| const mapDispatchToProps = { | |
| deleteTodoAction: deleteTodo, | |
| updateTodoAction: updateTodo, | |
| }; | |
| export default connect(null, mapDispatchToProps)(Todo); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { createSlice } from '@reduxjs/toolkit'; | |
| import { apiRequest } from '../api'; | |
| const todosSlice = createSlice({ | |
| name: 'todos', | |
| initialState: { | |
| todos: [], | |
| loading: 'idle', | |
| }, | |
| reducers: { | |
| createTodoRequest(state) { | |
| if (state.loading === 'idle') { | |
| state.loading = 'pending'; | |
| } | |
| }, | |
| createTodoResponse(state, action) { | |
| if (state.loading === 'pending') { | |
| state.loading = 'idle'; | |
| state.todos.push(action.payload); | |
| } | |
| }, | |
| fetchTodoRequest(state) { | |
| if (state.loading === 'idle') { | |
| state.loading = 'pending'; | |
| } | |
| }, | |
| fetchTodoResponse(state, action) { | |
| if (state.loading === 'pending') { | |
| state.loading = 'idle'; | |
| state.todos = action.payload; | |
| } | |
| }, | |
| updateTodoRequest(state) { | |
| if (state.loading === 'idle') { | |
| state.loading = 'pending'; | |
| } | |
| }, | |
| updateTodoResponse(state, action) { | |
| if (state.loading === 'pending') { | |
| state.loading = 'idle'; | |
| const updatedTodoIndex = state.todos.findIndex( | |
| (todo) => todo.id === action.payload | |
| ); | |
| const updatedTodo = state.todos[updatedTodoIndex]; | |
| state.todos[updatedTodoIndex].completed = !updatedTodo.completed; | |
| } | |
| }, | |
| deleteTodoRequest(state) { | |
| if (state.loading === 'idle') { | |
| state.loading = 'pending'; | |
| } | |
| }, | |
| deleteTodoResponse(state, action) { | |
| if (state.loading === 'pending') { | |
| state.loading = 'idle'; | |
| state.todos = state.todos.filter((todo) => todo.id !== action.payload); | |
| } | |
| }, | |
| }, | |
| }); | |
| const { | |
| fetchTodoRequest, | |
| fetchTodoResponse, | |
| createTodoRequest, | |
| createTodoResponse, | |
| updateTodoRequest, | |
| updateTodoResponse, | |
| deleteTodoRequest, | |
| deleteTodoResponse, | |
| } = todosSlice.actions; | |
| export default todosSlice.reducer; | |
| export const createTodo = (query) => async (dispatch) => { | |
| dispatch(createTodoRequest()); | |
| const body = { | |
| query, | |
| }; | |
| const { createTodo: newTodo } = await apiRequest(body); | |
| dispatch(createTodoResponse(newTodo)); | |
| }; | |
| export const fetchTodos = (query) => async (dispatch) => { | |
| dispatch(fetchTodoRequest()); | |
| const body = { | |
| query, | |
| }; | |
| const { todos } = await apiRequest(body); | |
| dispatch(fetchTodoResponse(todos)); | |
| }; | |
| export const updateTodo = (query, variables = {}) => async (dispatch) => { | |
| dispatch(updateTodoRequest()); | |
| const body = { | |
| query, | |
| }; | |
| const { updateTodo: updatedTodoId } = await apiRequest(body); | |
| dispatch(updateTodoResponse(updatedTodoId)); | |
| }; | |
| export const deleteTodo = (query) => async (dispatch) => { | |
| dispatch(deleteTodoRequest()); | |
| const body = { | |
| query, | |
| }; | |
| const { removeTodo: removedTodoId } = await apiRequest(body); | |
| dispatch(deleteTodoResponse(removedTodoId)); | |
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import React, { useEffect, useState } from 'react'; | |
| import { connect } from 'react-redux'; | |
| import { fetchTodos, createTodo } from './TodosSlice'; | |
| import { READ_TODOS, CREATE_TODO } from './queries'; | |
| import Todo from './Todo'; | |
| function Todos({ todos, fetchTodosActions, createTodoAction }) { | |
| const [newTodo, setNewTodo] = useState(''); | |
| useEffect(() => { | |
| fetchTodosActions(READ_TODOS); | |
| // eslint-disable-next-line | |
| }, []); | |
| const handleTodoSubmit = (e) => { | |
| e.preventDefault(); | |
| createTodoAction(CREATE_TODO(newTodo)); | |
| setNewTodo(''); | |
| }; | |
| const handleTodoChange = (e) => { | |
| setNewTodo(e.target.value); | |
| }; | |
| return ( | |
| <div className="container-sm"> | |
| <h4>Todo App</h4> | |
| <form onSubmit={handleTodoSubmit}> | |
| <div className="form-group row"> | |
| <input | |
| className="form-control col-sm-10" | |
| type="text" | |
| placeholder="Enter todo" | |
| value={newTodo} | |
| onChange={handleTodoChange} | |
| /> | |
| <button className="btn btn-primary col-sm-2" type="submit"> | |
| Submit | |
| </button> | |
| </div> | |
| </form> | |
| <div className="card"> | |
| {todos.map((todo) => ( | |
| <Todo todo={todo} key={todo.id} /> | |
| ))} | |
| </div> | |
| </div> | |
| ); | |
| } | |
| const mapStateToProps = (state) => { | |
| return { | |
| todos: state.todos, | |
| }; | |
| }; | |
| const mapDispatchToProps = { | |
| fetchTodosActions: fetchTodos, | |
| createTodoAction: createTodo, | |
| }; | |
| export default connect(mapStateToProps, mapDispatchToProps)(Todos); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment