Skip to content

Instantly share code, notes, and snippets.

@park-brian
Last active August 23, 2019 19:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save park-brian/ea5e27f9ba18dfb38afc7ece24858e95 to your computer and use it in GitHub Desktop.
Save park-brian/ea5e27f9ba18dfb38afc7ece24858e95 to your computer and use it in GitHub Desktop.
React Redux ES5 - TodoMVC
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>GistRun</title>
<!-- React/ReactDOM -->
<script src="https://cdn.jsdelivr.net/combine/npm/react@16.8.6/umd/react.production.min.js,npm/react-dom@16.8.6/umd/react-dom.production.min.js"></script>
<!-- Redux/Redux Thunk/React Redux -->
<script src="https://cdn.jsdelivr.net/combine/npm/redux@4.0.4/dist/redux.min.js,npm/redux-thunk@2.3.0/dist/redux-thunk.min.js,npm/react-redux@7.1.0/dist/react-redux.min.js"></script>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<main id="root" class="container">Loading...</main>
<script src="script.js"></script>
</body>
</html>
var c = React.createElement;
// create a redux store using a reducer and an initial state
var initialState = {
todos: [],
newTodo: {id: null, text: '', complete: false},
}
// include redux-thunk and devtools
var middleware = Redux.compose(
ReduxThunk.default,
__REDUX_DEVTOOLS_EXTENSION__ && __REDUX_DEVTOOLS_EXTENSION__()
);
// create store
var store = Redux.createStore(
reducer,
initialState,
Redux.applyMiddleware(middleware)
);
// create todos reducer
function reducer(state, action) {
switch(action.type) {
case 'UPDATE_NEW_TODO':
return Object.assign({}, state, {
newTodo: action.item,
});
case 'ADD_TODO':
return Object.assign({}, state, {
todos: state.todos.concat(
Object.assign({}, action.item, {
id: Math.random()
})
)
});
case 'REMOVE_TODO':
return Object.assign({}, state, {
todos: state.todos.filter(function(item) {
return item.id !== action.item.id;
})
});
case 'UPDATE_TODO':
return Object.assign({}, state, {
todos: state.todos.map(function(item) {
if (item.id === action.item.id)
return Object.assign({}, item, action.item)
return item;
})
});
default:
return state;
}
};
// define action creators
var actions = Redux.bindActionCreators({
updateNewTodo: function(item) {
return {type: 'UPDATE_NEW_TODO', item, item}
},
addTodo: function(item) {
return {type: 'ADD_TODO', item: item};
},
removeTodo: function(item) {
return {type: 'REMOVE_TODO', item: item};
},
updateTodo: function(item) {
return {type: 'UPDATE_TODO', item: item};
},
}, store.dispatch);
// create a presentational Counter component
function TodoItems(props) {
var todos = ReactRedux.useSelector(function(state) {
return state.todos;
});
return c(React.Fragment, null, todos.map(function(item) {
return c('div', {
className: 'flex w-100 mb-1 align-items-center',
}, [
c('input', {
className: 'input w-90',
value: item.text,
onChange: function(event) {
var value = event.target.value;
actions.updateTodo({
id: item.id,
text: value,
});
}
}),
c('button', {
className: 'button bg-secondary w-10',
onClick: function(event) {
actions.removeTodo(item)
}
}, 'x'),
]);
}));
}
function NewTodoItem(props) {
var newTodo = ReactRedux.useSelector(function(state) {
return state.newTodo;
});
return c('form', {
className: 'flex w-100 mb-1',
onSubmit: function(event) {
event.preventDefault();
if (!newTodo.text) return;
actions.addTodo(newTodo);
actions.updateNewTodo({ text: '' });
}
}, [
c('input', {
'aria-label': 'Enter a todo',
className: 'input w-90',
value: newTodo.text,
placeholder: 'Enter a todo item',
onChange: function(event) {
var value = event.target.value;
actions.updateNewTodo({ text: value });
}
}),
c('button', {
type: 'submit',
className: 'button bg-primary w-10',
}, '+')
])
}
function TodoApp(props) {
return c(React.Fragment, null, [
c('h1', null, 'Todos'),
c(NewTodoItem),
c('hr', {className: 'my-2'}),
c(TodoItems)
]);
}
// provide store to the root component
function App() {
return c(
ReactRedux.Provider,
{store: store},
c(TodoApp)
);
}
// render app
ReactDOM.render(App(), document.querySelector('#root'));
* {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
box-sizing: border-box;
}
hr {
border: none;
border-bottom: 1px solid #eee;
}
.w-10 { width: 10%; }
.w-20 { width: 20%; }
.w-30 { width: 30%; }
.w-40 { width: 40%; }
.w-50 { width: 50%; }
.w-60 { width: 60%; }
.w-70 { width: 70%; }
.w-80 { width: 80%; }
.w-90 { width: 90%; }
.w-100 { width: 100%; }
.m-0 { margin: 0; }
.p-0 { padding: 0; }
.my-2 { margin-top: 1rem; margin-bottom: 1rem; }
.mb-1 { margin-bottom: 0.5rem; }
.container {
min-width: 200px;
width: 400px;
max-width: 800px;
margin: 0 auto;
}
.flex {
display: flex;
}
.align-items-center {
align-items: center;
}
.align-items-stretch {
align-items: stretch;
}
.input, .button {
border: 1px solid #ddd;
}
.input + .button {
border-left: none;
}
.input {
padding: 0.5rem;
}
.button {
padding: 0.5rem 0.75rem;
cursor: pointer;
}
.bg-primary {
color: white;
background-color: steelblue;
border-color: steelblue;
}
.bg-secondary {
color: #444;
background-color: #eee;
border-color: #ddd;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment