!todo.completed).size}
12 | />
13 | );
14 | }
15 | }
16 |
17 | window.App.TodoHeaderContainer = connect(
18 | (state) => ({ todos: state.todos })
19 | )(TodoHeaderContainer);
20 |
--------------------------------------------------------------------------------
/level-08_dynamic-children/TodoItem.js:
--------------------------------------------------------------------------------
1 | class TodoItem extends React.Component {
2 | render() {
3 | const {
4 | title,
5 | completed
6 | } = this.props;
7 | return (
8 |
9 |
10 | {title}
11 |
12 |
13 | );
14 | }
15 | }
16 |
17 | TodoItem.propTypes = {
18 | title: React.PropTypes.string.isRequired,
19 | completed: React.PropTypes.bool.isRequired
20 | };
21 |
22 | window.App.TodoItem = TodoItem;
23 |
--------------------------------------------------------------------------------
/level-17_container-pattern/components/TodoApp.js:
--------------------------------------------------------------------------------
1 | const {
2 | TodoActions,
3 | CreateTodoFieldContainer,
4 | TodoHeaderContainer,
5 | TodoListContainer
6 | } = window.App;
7 |
8 | class TodoApp extends React.Component {
9 | componentDidMount() {
10 | TodoActions.loadTodos();
11 | }
12 |
13 | render() {
14 | return (
15 |
16 |
17 |
18 |
19 |
20 | );
21 | }
22 | }
23 |
24 | window.App.TodoApp = TodoApp;
25 |
--------------------------------------------------------------------------------
/level-20_redux-reducers/components/TodoApp.js:
--------------------------------------------------------------------------------
1 | const {
2 | TodoActions,
3 | CreateTodoFieldContainer,
4 | TodoHeaderContainer,
5 | TodoListContainer
6 | } = window.App;
7 |
8 | class TodoApp extends React.Component {
9 | componentDidMount() {
10 | TodoActions.loadTodos();
11 | }
12 |
13 | render() {
14 | return (
15 |
16 |
17 |
18 |
19 |
20 | );
21 | }
22 | }
23 |
24 | window.App.TodoApp = TodoApp;
25 |
--------------------------------------------------------------------------------
/level-23_redux-middlewares/components/TodoApp.js:
--------------------------------------------------------------------------------
1 | const {
2 | TodoActions,
3 | CreateTodoFieldContainer,
4 | TodoHeaderContainer,
5 | TodoListContainer
6 | } = window.App;
7 |
8 | class TodoApp extends React.Component {
9 | componentDidMount() {
10 | TodoActions.loadTodos();
11 | }
12 |
13 | render() {
14 | return (
15 |
16 |
17 |
18 |
19 |
20 | );
21 | }
22 | }
23 |
24 | window.App.TodoApp = TodoApp;
25 |
--------------------------------------------------------------------------------
/level-24_react-redux/components/CreateTodoFieldContainer.js:
--------------------------------------------------------------------------------
1 | const { connect } = ReactRedux;
2 |
3 | const {
4 | TodoActions,
5 | InputField
6 | } = window.App;
7 |
8 | class CreateTodoFieldContainer extends React.Component {
9 | render() {
10 | return (
11 |
15 | );
16 | }
17 | }
18 |
19 | window.App.CreateTodoFieldContainer = connect(undefined, {
20 | createTodo: TodoActions.createTodo
21 | })(CreateTodoFieldContainer);
22 |
--------------------------------------------------------------------------------
/level-25_immutablejs/components/CreateTodoFieldContainer.js:
--------------------------------------------------------------------------------
1 | const { connect } = ReactRedux;
2 |
3 | const {
4 | TodoActions,
5 | InputField
6 | } = window.App;
7 |
8 | class CreateTodoFieldContainer extends React.Component {
9 | render() {
10 | return (
11 |
15 | );
16 | }
17 | }
18 |
19 | window.App.CreateTodoFieldContainer = connect(undefined, {
20 | createTodo: TodoActions.createTodo
21 | })(CreateTodoFieldContainer);
22 |
--------------------------------------------------------------------------------
/level-07_prop-types-n-default-values/TodoItem.js:
--------------------------------------------------------------------------------
1 | class TodoItem extends React.Component {
2 | render() {
3 | const {
4 | title,
5 | completed
6 | } = this.props;
7 | return (
8 |
9 |
10 | {title}
11 |
12 |
13 | );
14 | }
15 | }
16 |
17 | TodoItem.propTypes = {
18 | title: React.PropTypes.string.isRequired,
19 | completed: React.PropTypes.bool.isRequired
20 | };
21 |
22 | window.App.TodoItem = TodoItem;
23 |
--------------------------------------------------------------------------------
/level-08_dynamic-children/TodoList.js:
--------------------------------------------------------------------------------
1 | const { TodoItem } = window.App;
2 |
3 | class TodoList extends React.Component {
4 | render() {
5 | const { todos } = this.props;
6 | const todoElements = todos.map((todo) => (
7 |
8 |
12 |
13 | ));
14 | return ;
15 | }
16 | }
17 |
18 | TodoList.propTypes = {
19 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired
20 | };
21 |
22 | window.App.TodoList = TodoList;
23 |
--------------------------------------------------------------------------------
/level-03_hello-react/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
18 |
19 |
--------------------------------------------------------------------------------
/level-23_redux-middlewares/main.js:
--------------------------------------------------------------------------------
1 | const { createStore, combineReducers, applyMiddleware } = Redux;
2 | const { TodoApp, reducers } = window.App;
3 |
4 | const thunkMiddleware = ({ dispatch, getState }) => {
5 | return (next) => (action) => {
6 | if (typeof action === 'function') {
7 | return action(dispatch, getState);
8 | }
9 | return next(action);
10 | };
11 | };
12 |
13 | const composedReducer = combineReducers(reducers);
14 | const store = createStore(
15 | composedReducer,
16 | applyMiddleware(thunkMiddleware)
17 | );
18 |
19 | ReactDOM.render(
20 | ,
21 | document.getElementById('app')
22 | );
23 |
--------------------------------------------------------------------------------
/level-24_react-redux/components/TodoApp.js:
--------------------------------------------------------------------------------
1 | const { connect } = ReactRedux;
2 | const {
3 | TodoActions,
4 | CreateTodoFieldContainer,
5 | TodoHeaderContainer,
6 | TodoListContainer
7 | } = window.App;
8 |
9 | class TodoApp extends React.Component {
10 | componentDidMount() {
11 | this.props.loadTodos();
12 | }
13 |
14 | render() {
15 | return (
16 |
17 |
18 |
19 |
20 |
21 | );
22 | }
23 | }
24 |
25 | window.App.TodoApp = connect(undefined, {
26 | loadTodos: TodoActions.loadTodos
27 | })(TodoApp);
28 |
--------------------------------------------------------------------------------
/level-25_immutablejs/components/TodoApp.js:
--------------------------------------------------------------------------------
1 | const { connect } = ReactRedux;
2 | const {
3 | TodoActions,
4 | CreateTodoFieldContainer,
5 | TodoHeaderContainer,
6 | TodoListContainer
7 | } = window.App;
8 |
9 | class TodoApp extends React.Component {
10 | componentDidMount() {
11 | this.props.loadTodos();
12 | }
13 |
14 | render() {
15 | return (
16 |
17 |
18 |
19 |
20 |
21 | );
22 | }
23 | }
24 |
25 | window.App.TodoApp = connect(undefined, {
26 | loadTodos: TodoActions.loadTodos
27 | })(TodoApp);
28 |
--------------------------------------------------------------------------------
/level-10_forms/TodoHeader.js:
--------------------------------------------------------------------------------
1 | class TodoHeader extends React.Component {
2 | render() {
3 | const {
4 | title,
5 | username,
6 | todoCount
7 | } = this.props;
8 | return (
9 |
10 |
{title}
11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項
12 |
13 | );
14 | }
15 | }
16 |
17 | TodoHeader.propTypes = {
18 | title: React.PropTypes.string,
19 | username: React.PropTypes.string,
20 | todoCount: React.PropTypes.number
21 | };
22 |
23 | TodoHeader.defaultProps = {
24 | title: '我的待辦清單',
25 | username: 'Guest',
26 | todoCount: 0
27 | };
28 |
29 | window.App.TodoHeader = TodoHeader;
30 |
--------------------------------------------------------------------------------
/level-06_transferring-props/TodoList.js:
--------------------------------------------------------------------------------
1 | const { TodoItem } = window.App;
2 |
3 | class TodoList extends React.Component {
4 | render() {
5 | return (
6 |
7 | -
8 |
12 |
13 | -
14 |
18 |
19 | -
20 |
24 |
25 |
26 | );
27 | }
28 | }
29 |
30 | window.App.TodoList = TodoList;
31 |
--------------------------------------------------------------------------------
/level-08_dynamic-children/TodoHeader.js:
--------------------------------------------------------------------------------
1 | class TodoHeader extends React.Component {
2 | render() {
3 | const {
4 | title,
5 | username,
6 | todoCount
7 | } = this.props;
8 | return (
9 |
10 |
{title}
11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項
12 |
13 | );
14 | }
15 | }
16 |
17 | TodoHeader.propTypes = {
18 | title: React.PropTypes.string,
19 | username: React.PropTypes.string,
20 | todoCount: React.PropTypes.number
21 | };
22 |
23 | TodoHeader.defaultProps = {
24 | title: '我的待辦清單',
25 | username: 'Guest',
26 | todoCount: 0
27 | };
28 |
29 | window.App.TodoHeader = TodoHeader;
30 |
--------------------------------------------------------------------------------
/level-09_stateful-component/TodoHeader.js:
--------------------------------------------------------------------------------
1 | class TodoHeader extends React.Component {
2 | render() {
3 | const {
4 | title,
5 | username,
6 | todoCount
7 | } = this.props;
8 | return (
9 |
10 |
{title}
11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項
12 |
13 | );
14 | }
15 | }
16 |
17 | TodoHeader.propTypes = {
18 | title: React.PropTypes.string,
19 | username: React.PropTypes.string,
20 | todoCount: React.PropTypes.number
21 | };
22 |
23 | TodoHeader.defaultProps = {
24 | title: '我的待辦清單',
25 | username: 'Guest',
26 | todoCount: 0
27 | };
28 |
29 | window.App.TodoHeader = TodoHeader;
30 |
--------------------------------------------------------------------------------
/level-11_component-lifecycle/TodoHeader.js:
--------------------------------------------------------------------------------
1 | class TodoHeader extends React.Component {
2 | render() {
3 | const {
4 | title,
5 | username,
6 | todoCount
7 | } = this.props;
8 | return (
9 |
10 |
{title}
11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項
12 |
13 | );
14 | }
15 | }
16 |
17 | TodoHeader.propTypes = {
18 | title: React.PropTypes.string,
19 | username: React.PropTypes.string,
20 | todoCount: React.PropTypes.number
21 | };
22 |
23 | TodoHeader.defaultProps = {
24 | title: '我的待辦清單',
25 | username: 'Guest',
26 | todoCount: 0
27 | };
28 |
29 | window.App.TodoHeader = TodoHeader;
30 |
--------------------------------------------------------------------------------
/level-14_flux-actions/components/TodoHeader.js:
--------------------------------------------------------------------------------
1 | class TodoHeader extends React.Component {
2 | render() {
3 | const {
4 | title,
5 | username,
6 | todoCount
7 | } = this.props;
8 | return (
9 |
10 |
{title}
11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項
12 |
13 | );
14 | }
15 | }
16 |
17 | TodoHeader.propTypes = {
18 | title: React.PropTypes.string,
19 | username: React.PropTypes.string,
20 | todoCount: React.PropTypes.number
21 | };
22 |
23 | TodoHeader.defaultProps = {
24 | title: '我的待辦清單',
25 | username: 'Guest',
26 | todoCount: 0
27 | };
28 |
29 | window.App.TodoHeader = TodoHeader;
30 |
--------------------------------------------------------------------------------
/level-15_flux-stores/components/TodoHeader.js:
--------------------------------------------------------------------------------
1 | class TodoHeader extends React.Component {
2 | render() {
3 | const {
4 | title,
5 | username,
6 | todoCount
7 | } = this.props;
8 | return (
9 |
10 |
{title}
11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項
12 |
13 | );
14 | }
15 | }
16 |
17 | TodoHeader.propTypes = {
18 | title: React.PropTypes.string,
19 | username: React.PropTypes.string,
20 | todoCount: React.PropTypes.number
21 | };
22 |
23 | TodoHeader.defaultProps = {
24 | title: '我的待辦清單',
25 | username: 'Guest',
26 | todoCount: 0
27 | };
28 |
29 | window.App.TodoHeader = TodoHeader;
30 |
--------------------------------------------------------------------------------
/level-18_flux-utils/components/TodoHeader.js:
--------------------------------------------------------------------------------
1 | class TodoHeader extends React.Component {
2 | render() {
3 | const {
4 | title,
5 | username,
6 | todoCount
7 | } = this.props;
8 | return (
9 |
10 |
{title}
11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項
12 |
13 | );
14 | }
15 | }
16 |
17 | TodoHeader.propTypes = {
18 | title: React.PropTypes.string,
19 | username: React.PropTypes.string,
20 | todoCount: React.PropTypes.number
21 | };
22 |
23 | TodoHeader.defaultProps = {
24 | title: '我的待辦清單',
25 | username: 'Guest',
26 | todoCount: 0
27 | };
28 |
29 | window.App.TodoHeader = TodoHeader;
30 |
--------------------------------------------------------------------------------
/level-21_redux-store/components/TodoHeader.js:
--------------------------------------------------------------------------------
1 | class TodoHeader extends React.Component {
2 | render() {
3 | const {
4 | title,
5 | username,
6 | todoCount
7 | } = this.props;
8 | return (
9 |
10 |
{title}
11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項
12 |
13 | );
14 | }
15 | }
16 |
17 | TodoHeader.propTypes = {
18 | title: React.PropTypes.string,
19 | username: React.PropTypes.string,
20 | todoCount: React.PropTypes.number
21 | };
22 |
23 | TodoHeader.defaultProps = {
24 | title: '我的待辦清單',
25 | username: 'Guest',
26 | todoCount: 0
27 | };
28 |
29 | window.App.TodoHeader = TodoHeader;
30 |
--------------------------------------------------------------------------------
/level-24_react-redux/components/TodoHeader.js:
--------------------------------------------------------------------------------
1 | class TodoHeader extends React.Component {
2 | render() {
3 | const {
4 | title,
5 | username,
6 | todoCount
7 | } = this.props;
8 | return (
9 |
10 |
{title}
11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項
12 |
13 | );
14 | }
15 | }
16 |
17 | TodoHeader.propTypes = {
18 | title: React.PropTypes.string,
19 | username: React.PropTypes.string,
20 | todoCount: React.PropTypes.number
21 | };
22 |
23 | TodoHeader.defaultProps = {
24 | title: '我的待辦清單',
25 | username: 'Guest',
26 | todoCount: 0
27 | };
28 |
29 | window.App.TodoHeader = TodoHeader;
30 |
--------------------------------------------------------------------------------
/level-25_immutablejs/components/TodoHeader.js:
--------------------------------------------------------------------------------
1 | class TodoHeader extends React.Component {
2 | render() {
3 | const {
4 | title,
5 | username,
6 | todoCount
7 | } = this.props;
8 | return (
9 |
10 |
{title}
11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項
12 |
13 | );
14 | }
15 | }
16 |
17 | TodoHeader.propTypes = {
18 | title: React.PropTypes.string,
19 | username: React.PropTypes.string,
20 | todoCount: React.PropTypes.number
21 | };
22 |
23 | TodoHeader.defaultProps = {
24 | title: '我的待辦清單',
25 | username: 'Guest',
26 | todoCount: 0
27 | };
28 |
29 | window.App.TodoHeader = TodoHeader;
30 |
--------------------------------------------------------------------------------
/level-07_prop-types-n-default-values/TodoHeader.js:
--------------------------------------------------------------------------------
1 | class TodoHeader extends React.Component {
2 | render() {
3 | const {
4 | title,
5 | username,
6 | todoCount
7 | } = this.props;
8 | return (
9 |
10 |
{title}
11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項
12 |
13 | );
14 | }
15 | }
16 |
17 | TodoHeader.propTypes = {
18 | title: React.PropTypes.string,
19 | username: React.PropTypes.string,
20 | todoCount: React.PropTypes.number
21 | };
22 |
23 | TodoHeader.defaultProps = {
24 | title: '我的待辦清單',
25 | username: 'Guest',
26 | todoCount: 0
27 | };
28 |
29 | window.App.TodoHeader = TodoHeader;
30 |
--------------------------------------------------------------------------------
/level-07_prop-types-n-default-values/TodoList.js:
--------------------------------------------------------------------------------
1 | const { TodoItem } = window.App;
2 |
3 | class TodoList extends React.Component {
4 | render() {
5 | return (
6 |
7 | -
8 |
12 |
13 | -
14 |
18 |
19 | -
20 |
24 |
25 |
26 | );
27 | }
28 | }
29 |
30 | window.App.TodoList = TodoList;
31 |
--------------------------------------------------------------------------------
/level-13_flux-dispatcher/components/TodoHeader.js:
--------------------------------------------------------------------------------
1 | class TodoHeader extends React.Component {
2 | render() {
3 | const {
4 | title,
5 | username,
6 | todoCount
7 | } = this.props;
8 | return (
9 |
10 |
{title}
11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項
12 |
13 | );
14 | }
15 | }
16 |
17 | TodoHeader.propTypes = {
18 | title: React.PropTypes.string,
19 | username: React.PropTypes.string,
20 | todoCount: React.PropTypes.number
21 | };
22 |
23 | TodoHeader.defaultProps = {
24 | title: '我的待辦清單',
25 | username: 'Guest',
26 | todoCount: 0
27 | };
28 |
29 | window.App.TodoHeader = TodoHeader;
30 |
--------------------------------------------------------------------------------
/level-17_container-pattern/components/TodoHeader.js:
--------------------------------------------------------------------------------
1 | class TodoHeader extends React.Component {
2 | render() {
3 | const {
4 | title,
5 | username,
6 | todoCount
7 | } = this.props;
8 | return (
9 |
10 |
{title}
11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項
12 |
13 | );
14 | }
15 | }
16 |
17 | TodoHeader.propTypes = {
18 | title: React.PropTypes.string,
19 | username: React.PropTypes.string,
20 | todoCount: React.PropTypes.number
21 | };
22 |
23 | TodoHeader.defaultProps = {
24 | title: '我的待辦清單',
25 | username: 'Guest',
26 | todoCount: 0
27 | };
28 |
29 | window.App.TodoHeader = TodoHeader;
30 |
--------------------------------------------------------------------------------
/level-20_redux-reducers/components/TodoHeader.js:
--------------------------------------------------------------------------------
1 | class TodoHeader extends React.Component {
2 | render() {
3 | const {
4 | title,
5 | username,
6 | todoCount
7 | } = this.props;
8 | return (
9 |
10 |
{title}
11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項
12 |
13 | );
14 | }
15 | }
16 |
17 | TodoHeader.propTypes = {
18 | title: React.PropTypes.string,
19 | username: React.PropTypes.string,
20 | todoCount: React.PropTypes.number
21 | };
22 |
23 | TodoHeader.defaultProps = {
24 | title: '我的待辦清單',
25 | username: 'Guest',
26 | todoCount: 0
27 | };
28 |
29 | window.App.TodoHeader = TodoHeader;
30 |
--------------------------------------------------------------------------------
/level-22_redux-actions/components/TodoHeader.js:
--------------------------------------------------------------------------------
1 | class TodoHeader extends React.Component {
2 | render() {
3 | const {
4 | title,
5 | username,
6 | todoCount
7 | } = this.props;
8 | return (
9 |
10 |
{title}
11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項
12 |
13 | );
14 | }
15 | }
16 |
17 | TodoHeader.propTypes = {
18 | title: React.PropTypes.string,
19 | username: React.PropTypes.string,
20 | todoCount: React.PropTypes.number
21 | };
22 |
23 | TodoHeader.defaultProps = {
24 | title: '我的待辦清單',
25 | username: 'Guest',
26 | todoCount: 0
27 | };
28 |
29 | window.App.TodoHeader = TodoHeader;
30 |
--------------------------------------------------------------------------------
/level-23_redux-middlewares/components/TodoHeader.js:
--------------------------------------------------------------------------------
1 | class TodoHeader extends React.Component {
2 | render() {
3 | const {
4 | title,
5 | username,
6 | todoCount
7 | } = this.props;
8 | return (
9 |
10 |
{title}
11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項
12 |
13 | );
14 | }
15 | }
16 |
17 | TodoHeader.propTypes = {
18 | title: React.PropTypes.string,
19 | username: React.PropTypes.string,
20 | todoCount: React.PropTypes.number
21 | };
22 |
23 | TodoHeader.defaultProps = {
24 | title: '我的待辦清單',
25 | username: 'Guest',
26 | todoCount: 0
27 | };
28 |
29 | window.App.TodoHeader = TodoHeader;
30 |
--------------------------------------------------------------------------------
/level-16_flux-controller-view/components/TodoHeader.js:
--------------------------------------------------------------------------------
1 | class TodoHeader extends React.Component {
2 | render() {
3 | const {
4 | title,
5 | username,
6 | todoCount
7 | } = this.props;
8 | return (
9 |
10 |
{title}
11 | 哈囉,{username}:你有 {todoCount} 項未完成待辦事項
12 |
13 | );
14 | }
15 | }
16 |
17 | TodoHeader.propTypes = {
18 | title: React.PropTypes.string,
19 | username: React.PropTypes.string,
20 | todoCount: React.PropTypes.number
21 | };
22 |
23 | TodoHeader.defaultProps = {
24 | title: '我的待辦清單',
25 | username: 'Guest',
26 | todoCount: 0
27 | };
28 |
29 | window.App.TodoHeader = TodoHeader;
30 |
--------------------------------------------------------------------------------
/level-24_react-redux/main.js:
--------------------------------------------------------------------------------
1 | const { createStore, combineReducers, applyMiddleware } = Redux;
2 | const { Provider } = ReactRedux;
3 | const { TodoApp, reducers } = window.App;
4 |
5 | const thunkMiddleware = ({ dispatch, getState }) => {
6 | return (next) => (action) => {
7 | if (typeof action === 'function') {
8 | return action(dispatch, getState);
9 | }
10 | return next(action);
11 | };
12 | };
13 |
14 | const composedReducer = combineReducers(reducers);
15 | const store = createStore(
16 | composedReducer,
17 | applyMiddleware(thunkMiddleware)
18 | );
19 |
20 | ReactDOM.render(
21 |
22 |
23 | ,
24 | document.getElementById('app')
25 | );
26 |
--------------------------------------------------------------------------------
/level-25_immutablejs/main.js:
--------------------------------------------------------------------------------
1 | const { createStore, combineReducers, applyMiddleware } = Redux;
2 | const { Provider } = ReactRedux;
3 | const { TodoApp, reducers } = window.App;
4 |
5 | const thunkMiddleware = ({ dispatch, getState }) => {
6 | return (next) => (action) => {
7 | if (typeof action === 'function') {
8 | return action(dispatch, getState);
9 | }
10 | return next(action);
11 | };
12 | };
13 |
14 | const composedReducer = combineReducers(reducers);
15 | const store = createStore(
16 | composedReducer,
17 | applyMiddleware(thunkMiddleware)
18 | );
19 |
20 | ReactDOM.render(
21 |
22 |
23 | ,
24 | document.getElementById('app')
25 | );
26 |
--------------------------------------------------------------------------------
/level-18_flux-utils/components/TodoHeaderContainer.js:
--------------------------------------------------------------------------------
1 | const { Container } = FluxUtils;
2 |
3 | const {
4 | TodoStore,
5 | TodoHeader
6 | } = window.App;
7 |
8 | class TodoHeaderContainer extends React.Component {
9 | static getStores() {
10 | return [ TodoStore ];
11 | }
12 |
13 | static calculateState(prevState) {
14 | return {
15 | todos: TodoStore.getState(),
16 | };
17 | }
18 |
19 | render() {
20 | return (
21 | !todo.completed).length}
25 | />
26 | );
27 | }
28 | }
29 |
30 | window.App.TodoHeaderContainer = Container.create(TodoHeaderContainer);
31 |
--------------------------------------------------------------------------------
/level-21_redux-store/components/TodoHeaderContainer.js:
--------------------------------------------------------------------------------
1 | const { Container } = FluxUtils;
2 |
3 | const {
4 | TodoStore,
5 | TodoHeader
6 | } = window.App;
7 |
8 | class TodoHeaderContainer extends React.Component {
9 | static getStores() {
10 | return [ TodoStore ];
11 | }
12 |
13 | static calculateState(prevState) {
14 | return {
15 | todos: TodoStore.getState(),
16 | };
17 | }
18 |
19 | render() {
20 | return (
21 | !todo.completed).length}
25 | />
26 | );
27 | }
28 | }
29 |
30 | window.App.TodoHeaderContainer = Container.create(TodoHeaderContainer);
31 |
--------------------------------------------------------------------------------
/level-20_redux-reducers/components/TodoHeaderContainer.js:
--------------------------------------------------------------------------------
1 | const { Container } = FluxUtils;
2 |
3 | const {
4 | TodoStore,
5 | TodoHeader
6 | } = window.App;
7 |
8 | class TodoHeaderContainer extends React.Component {
9 | static getStores() {
10 | return [ TodoStore ];
11 | }
12 |
13 | static calculateState(prevState) {
14 | return {
15 | todos: TodoStore.getState(),
16 | };
17 | }
18 |
19 | render() {
20 | return (
21 | !todo.completed).length}
25 | />
26 | );
27 | }
28 | }
29 |
30 | window.App.TodoHeaderContainer = Container.create(TodoHeaderContainer);
31 |
--------------------------------------------------------------------------------
/level-22_redux-actions/components/TodoHeaderContainer.js:
--------------------------------------------------------------------------------
1 | const { Container } = FluxUtils;
2 |
3 | const {
4 | TodoStore,
5 | TodoHeader
6 | } = window.App;
7 |
8 | class TodoHeaderContainer extends React.Component {
9 | static getStores() {
10 | return [ TodoStore ];
11 | }
12 |
13 | static calculateState(prevState) {
14 | return {
15 | todos: TodoStore.getState(),
16 | };
17 | }
18 |
19 | render() {
20 | return (
21 | !todo.completed).length}
25 | />
26 | );
27 | }
28 | }
29 |
30 | window.App.TodoHeaderContainer = Container.create(TodoHeaderContainer);
31 |
--------------------------------------------------------------------------------
/level-23_redux-middlewares/components/TodoHeaderContainer.js:
--------------------------------------------------------------------------------
1 | const { Container } = FluxUtils;
2 |
3 | const {
4 | TodoStore,
5 | TodoHeader
6 | } = window.App;
7 |
8 | class TodoHeaderContainer extends React.Component {
9 | static getStores() {
10 | return [ TodoStore ];
11 | }
12 |
13 | static calculateState(prevState) {
14 | return {
15 | todos: TodoStore.getState(),
16 | };
17 | }
18 |
19 | render() {
20 | return (
21 | !todo.completed).length}
25 | />
26 | );
27 | }
28 | }
29 |
30 | window.App.TodoHeaderContainer = Container.create(TodoHeaderContainer);
31 |
--------------------------------------------------------------------------------
/level-09_stateful-component/TodoList.js:
--------------------------------------------------------------------------------
1 | const { TodoItem } = window.App;
2 |
3 | class TodoList extends React.Component {
4 | render() {
5 | const {
6 | todos,
7 | onDeleteTodo
8 | } = this.props;
9 | const todoElements = todos.map((todo) => (
10 |
11 | onDeleteTodo && onDeleteTodo(todo.id)}
15 | />
16 |
17 | ));
18 | return ;
19 | }
20 | }
21 |
22 | TodoList.propTypes = {
23 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
24 | onDeleteTodo: React.PropTypes.func
25 | };
26 |
27 | window.App.TodoList = TodoList;
28 |
--------------------------------------------------------------------------------
/level-04_first-component/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
21 |
22 |
--------------------------------------------------------------------------------
/level-18_flux-utils/components/TodoListContainer.js:
--------------------------------------------------------------------------------
1 | const { Container } = FluxUtils;
2 |
3 | const {
4 | TodoActions,
5 | TodoStore,
6 | TodoList
7 | } = window.App;
8 |
9 | class TodoListContainer extends React.Component {
10 | static getStores() {
11 | return [ TodoStore ];
12 | }
13 |
14 | static calculateState(prevState) {
15 | return {
16 | todos: TodoStore.getState(),
17 | };
18 | }
19 |
20 | render() {
21 | return (
22 |
28 | );
29 | }
30 | }
31 |
32 | window.App.TodoListContainer = Container.create(TodoListContainer);
33 |
--------------------------------------------------------------------------------
/level-21_redux-store/components/TodoListContainer.js:
--------------------------------------------------------------------------------
1 | const { Container } = FluxUtils;
2 |
3 | const {
4 | TodoActions,
5 | TodoStore,
6 | TodoList
7 | } = window.App;
8 |
9 | class TodoListContainer extends React.Component {
10 | static getStores() {
11 | return [ TodoStore ];
12 | }
13 |
14 | static calculateState(prevState) {
15 | return {
16 | todos: TodoStore.getState(),
17 | };
18 | }
19 |
20 | render() {
21 | return (
22 |
28 | );
29 | }
30 | }
31 |
32 | window.App.TodoListContainer = Container.create(TodoListContainer);
33 |
--------------------------------------------------------------------------------
/level-22_redux-actions/components/TodoListContainer.js:
--------------------------------------------------------------------------------
1 | const { Container } = FluxUtils;
2 |
3 | const {
4 | TodoActions,
5 | TodoStore,
6 | TodoList
7 | } = window.App;
8 |
9 | class TodoListContainer extends React.Component {
10 | static getStores() {
11 | return [ TodoStore ];
12 | }
13 |
14 | static calculateState(prevState) {
15 | return {
16 | todos: TodoStore.getState(),
17 | };
18 | }
19 |
20 | render() {
21 | return (
22 |
28 | );
29 | }
30 | }
31 |
32 | window.App.TodoListContainer = Container.create(TodoListContainer);
33 |
--------------------------------------------------------------------------------
/level-20_redux-reducers/components/TodoListContainer.js:
--------------------------------------------------------------------------------
1 | const { Container } = FluxUtils;
2 |
3 | const {
4 | TodoActions,
5 | TodoStore,
6 | TodoList
7 | } = window.App;
8 |
9 | class TodoListContainer extends React.Component {
10 | static getStores() {
11 | return [ TodoStore ];
12 | }
13 |
14 | static calculateState(prevState) {
15 | return {
16 | todos: TodoStore.getState(),
17 | };
18 | }
19 |
20 | render() {
21 | return (
22 |
28 | );
29 | }
30 | }
31 |
32 | window.App.TodoListContainer = Container.create(TodoListContainer);
33 |
--------------------------------------------------------------------------------
/level-23_redux-middlewares/components/TodoListContainer.js:
--------------------------------------------------------------------------------
1 | const { Container } = FluxUtils;
2 |
3 | const {
4 | TodoActions,
5 | TodoStore,
6 | TodoList
7 | } = window.App;
8 |
9 | class TodoListContainer extends React.Component {
10 | static getStores() {
11 | return [ TodoStore ];
12 | }
13 |
14 | static calculateState(prevState) {
15 | return {
16 | todos: TodoStore.getState(),
17 | };
18 | }
19 |
20 | render() {
21 | return (
22 |
28 | );
29 | }
30 | }
31 |
32 | window.App.TodoListContainer = Container.create(TodoListContainer);
33 |
--------------------------------------------------------------------------------
/level-24_react-redux/components/TodoListContainer.js:
--------------------------------------------------------------------------------
1 | const { connect } = ReactRedux;
2 |
3 | const {
4 | TodoActions,
5 | TodoList
6 | } = window.App;
7 |
8 | class TodoListContainer extends React.Component {
9 | render() {
10 | const {
11 | todos,
12 | updateTodo,
13 | toggleTodo,
14 | deleteTodo
15 | } = this.props;
16 | return (
17 |
23 | );
24 | }
25 | }
26 |
27 | window.App.TodoListContainer = connect(
28 | (state) => ({ todos: state.todos }),
29 | {
30 | updateTodo: TodoActions.updateTodo,
31 | toggleTodo: TodoActions.toggleTodo,
32 | deleteTodo: TodoActions.deleteTodo
33 | }
34 | )(TodoListContainer);
35 |
--------------------------------------------------------------------------------
/level-25_immutablejs/components/TodoListContainer.js:
--------------------------------------------------------------------------------
1 | const { connect } = ReactRedux;
2 |
3 | const {
4 | TodoActions,
5 | TodoList
6 | } = window.App;
7 |
8 | class TodoListContainer extends React.Component {
9 | render() {
10 | const {
11 | todos,
12 | updateTodo,
13 | toggleTodo,
14 | deleteTodo
15 | } = this.props;
16 | return (
17 |
23 | );
24 | }
25 | }
26 |
27 | window.App.TodoListContainer = connect(
28 | (state) => ({ todos: state.todos }),
29 | {
30 | updateTodo: TodoActions.updateTodo,
31 | toggleTodo: TodoActions.toggleTodo,
32 | deleteTodo: TodoActions.deleteTodo
33 | }
34 | )(TodoListContainer);
35 |
--------------------------------------------------------------------------------
/level-08_dynamic-children/TodoApp.js:
--------------------------------------------------------------------------------
1 | const {
2 | InputField,
3 | TodoHeader,
4 | TodoList
5 | } = window.App;
6 |
7 | const todos = [
8 | {
9 | id: 0,
10 | title: 'Item 1',
11 | completed: false
12 | },
13 | {
14 | id: 1,
15 | title: 'Item 2',
16 | completed: false
17 | },
18 | {
19 | id: 2,
20 | title: 'Item 3',
21 | completed: false
22 | }
23 | ];
24 |
25 | class TodoApp extends React.Component {
26 | render() {
27 | return (
28 |
29 | !todo.completed).length}
33 | />
34 |
35 |
36 |
37 | );
38 | }
39 | }
40 |
41 | window.App.TodoApp = TodoApp;
42 |
--------------------------------------------------------------------------------
/level-17_container-pattern/components/TodoHeaderContainer.js:
--------------------------------------------------------------------------------
1 | const {
2 | TodoStore,
3 | TodoHeader
4 | } = window.App;
5 |
6 | class TodoHeaderContainer extends React.Component {
7 | constructor(props, context) {
8 | super(props, context);
9 | this.state = { todos: TodoStore.getAll() };
10 | }
11 |
12 | componentDidMount() {
13 | this._removeChangeListener = TodoStore.addChangeListener(
14 | () => this.setState({ todos: TodoStore.getAll() })
15 | );
16 | }
17 |
18 | componentWillUnmount() {
19 | this._removeChangeListener();
20 | }
21 |
22 | render() {
23 | return (
24 | !todo.completed).length}
28 | />
29 | );
30 | }
31 | }
32 |
33 | window.App.TodoHeaderContainer = TodoHeaderContainer;
34 |
--------------------------------------------------------------------------------
/level-17_container-pattern/components/TodoListContainer.js:
--------------------------------------------------------------------------------
1 | const {
2 | TodoActions,
3 | TodoStore,
4 | TodoList
5 | } = window.App;
6 |
7 | class TodoListContainer extends React.Component {
8 | constructor(props, context) {
9 | super(props, context);
10 | this.state = { todos: TodoStore.getAll() };
11 | }
12 |
13 | componentDidMount() {
14 | this._removeChangeListener = TodoStore.addChangeListener(
15 | () => this.setState({ todos: TodoStore.getAll() })
16 | );
17 | }
18 |
19 | componentWillUnmount() {
20 | this._removeChangeListener();
21 | }
22 |
23 | render() {
24 | return (
25 |
31 | );
32 | }
33 | }
34 |
35 | window.App.TodoListContainer = TodoListContainer;
36 |
--------------------------------------------------------------------------------
/level-24_react-redux/actions/TodoActions.js:
--------------------------------------------------------------------------------
1 | const { ActionTypes } = window.App;
2 |
3 | window.App.TodoActions = {
4 | loadTodos() {
5 | return (dispatch) => {
6 | fetch('./todos.json')
7 | .then((response) => response.json())
8 | .then((todos) => dispatch({
9 | type: ActionTypes.LOAD_TODOS_SUCCESS,
10 | todos
11 | }));
12 | };
13 | },
14 | createTodo(title) {
15 | return {
16 | type: ActionTypes.CREATE_TODO,
17 | title
18 | };
19 | },
20 | updateTodo(id, title) {
21 | return {
22 | type: ActionTypes.UPDATE_TODO,
23 | id,
24 | title
25 | };
26 | },
27 | toggleTodo(id, completed) {
28 | return {
29 | type: ActionTypes.TOGGLE_TODO,
30 | id,
31 | completed
32 | };
33 | },
34 | deleteTodo(id) {
35 | return {
36 | type: ActionTypes.DELETE_TODO,
37 | id
38 | };
39 | }
40 | };
41 |
--------------------------------------------------------------------------------
/level-25_immutablejs/actions/TodoActions.js:
--------------------------------------------------------------------------------
1 | const { ActionTypes } = window.App;
2 |
3 | window.App.TodoActions = {
4 | loadTodos() {
5 | return (dispatch) => {
6 | fetch('./todos.json')
7 | .then((response) => response.json())
8 | .then((todos) => dispatch({
9 | type: ActionTypes.LOAD_TODOS_SUCCESS,
10 | todos
11 | }));
12 | };
13 | },
14 | createTodo(title) {
15 | return {
16 | type: ActionTypes.CREATE_TODO,
17 | title
18 | };
19 | },
20 | updateTodo(id, title) {
21 | return {
22 | type: ActionTypes.UPDATE_TODO,
23 | id,
24 | title
25 | };
26 | },
27 | toggleTodo(id, completed) {
28 | return {
29 | type: ActionTypes.TOGGLE_TODO,
30 | id,
31 | completed
32 | };
33 | },
34 | deleteTodo(id) {
35 | return {
36 | type: ActionTypes.DELETE_TODO,
37 | id
38 | };
39 | }
40 | };
41 |
--------------------------------------------------------------------------------
/level-14_flux-actions/actions/TodoActions.js:
--------------------------------------------------------------------------------
1 | const {
2 | ActionTypes,
3 | AppDispatcher
4 | } = window.App;
5 |
6 | window.App.TodoActions = {
7 | loadTodos() {
8 | fetch('./todos.json')
9 | .then((response) => response.json())
10 | .then((todos) => AppDispatcher.dispatch({
11 | type: ActionTypes.LOAD_TODOS_SUCCESS,
12 | todos
13 | }));
14 | },
15 | createTodo(title) {
16 | AppDispatcher.dispatch({
17 | type: ActionTypes.CREATE_TODO,
18 | title
19 | });
20 | },
21 | updateTodo(id, title) {
22 | AppDispatcher.dispatch({
23 | type: ActionTypes.UPDATE_TODO,
24 | id,
25 | title
26 | });
27 | },
28 | toggleTodo(id, completed) {
29 | AppDispatcher.dispatch({
30 | type: ActionTypes.TOGGLE_TODO,
31 | id,
32 | completed
33 | });
34 | },
35 | deleteTodo(id) {
36 | AppDispatcher.dispatch({
37 | type: ActionTypes.DELETE_TODO,
38 | id
39 | });
40 | }
41 | };
42 |
--------------------------------------------------------------------------------
/level-15_flux-stores/actions/TodoActions.js:
--------------------------------------------------------------------------------
1 | const {
2 | ActionTypes,
3 | AppDispatcher
4 | } = window.App;
5 |
6 | window.App.TodoActions = {
7 | loadTodos() {
8 | fetch('./todos.json')
9 | .then((response) => response.json())
10 | .then((todos) => AppDispatcher.dispatch({
11 | type: ActionTypes.LOAD_TODOS_SUCCESS,
12 | todos
13 | }));
14 | },
15 | createTodo(title) {
16 | AppDispatcher.dispatch({
17 | type: ActionTypes.CREATE_TODO,
18 | title
19 | });
20 | },
21 | updateTodo(id, title) {
22 | AppDispatcher.dispatch({
23 | type: ActionTypes.UPDATE_TODO,
24 | id,
25 | title
26 | });
27 | },
28 | toggleTodo(id, completed) {
29 | AppDispatcher.dispatch({
30 | type: ActionTypes.TOGGLE_TODO,
31 | id,
32 | completed
33 | });
34 | },
35 | deleteTodo(id) {
36 | AppDispatcher.dispatch({
37 | type: ActionTypes.DELETE_TODO,
38 | id
39 | });
40 | }
41 | };
42 |
--------------------------------------------------------------------------------
/level-18_flux-utils/actions/TodoActions.js:
--------------------------------------------------------------------------------
1 | const {
2 | ActionTypes,
3 | AppDispatcher
4 | } = window.App;
5 |
6 | window.App.TodoActions = {
7 | loadTodos() {
8 | fetch('./todos.json')
9 | .then((response) => response.json())
10 | .then((todos) => AppDispatcher.dispatch({
11 | type: ActionTypes.LOAD_TODOS_SUCCESS,
12 | todos
13 | }));
14 | },
15 | createTodo(title) {
16 | AppDispatcher.dispatch({
17 | type: ActionTypes.CREATE_TODO,
18 | title
19 | });
20 | },
21 | updateTodo(id, title) {
22 | AppDispatcher.dispatch({
23 | type: ActionTypes.UPDATE_TODO,
24 | id,
25 | title
26 | });
27 | },
28 | toggleTodo(id, completed) {
29 | AppDispatcher.dispatch({
30 | type: ActionTypes.TOGGLE_TODO,
31 | id,
32 | completed
33 | });
34 | },
35 | deleteTodo(id) {
36 | AppDispatcher.dispatch({
37 | type: ActionTypes.DELETE_TODO,
38 | id
39 | });
40 | }
41 | };
42 |
--------------------------------------------------------------------------------
/level-20_redux-reducers/actions/TodoActions.js:
--------------------------------------------------------------------------------
1 | const {
2 | ActionTypes,
3 | AppDispatcher
4 | } = window.App;
5 |
6 | window.App.TodoActions = {
7 | loadTodos() {
8 | fetch('./todos.json')
9 | .then((response) => response.json())
10 | .then((todos) => AppDispatcher.dispatch({
11 | type: ActionTypes.LOAD_TODOS_SUCCESS,
12 | todos
13 | }));
14 | },
15 | createTodo(title) {
16 | AppDispatcher.dispatch({
17 | type: ActionTypes.CREATE_TODO,
18 | title
19 | });
20 | },
21 | updateTodo(id, title) {
22 | AppDispatcher.dispatch({
23 | type: ActionTypes.UPDATE_TODO,
24 | id,
25 | title
26 | });
27 | },
28 | toggleTodo(id, completed) {
29 | AppDispatcher.dispatch({
30 | type: ActionTypes.TOGGLE_TODO,
31 | id,
32 | completed
33 | });
34 | },
35 | deleteTodo(id) {
36 | AppDispatcher.dispatch({
37 | type: ActionTypes.DELETE_TODO,
38 | id
39 | });
40 | }
41 | };
42 |
--------------------------------------------------------------------------------
/level-21_redux-store/actions/TodoActions.js:
--------------------------------------------------------------------------------
1 | const {
2 | ActionTypes,
3 | AppDispatcher
4 | } = window.App;
5 |
6 | window.App.TodoActions = {
7 | loadTodos() {
8 | fetch('./todos.json')
9 | .then((response) => response.json())
10 | .then((todos) => AppDispatcher.dispatch({
11 | type: ActionTypes.LOAD_TODOS_SUCCESS,
12 | todos
13 | }));
14 | },
15 | createTodo(title) {
16 | AppDispatcher.dispatch({
17 | type: ActionTypes.CREATE_TODO,
18 | title
19 | });
20 | },
21 | updateTodo(id, title) {
22 | AppDispatcher.dispatch({
23 | type: ActionTypes.UPDATE_TODO,
24 | id,
25 | title
26 | });
27 | },
28 | toggleTodo(id, completed) {
29 | AppDispatcher.dispatch({
30 | type: ActionTypes.TOGGLE_TODO,
31 | id,
32 | completed
33 | });
34 | },
35 | deleteTodo(id) {
36 | AppDispatcher.dispatch({
37 | type: ActionTypes.DELETE_TODO,
38 | id
39 | });
40 | }
41 | };
42 |
--------------------------------------------------------------------------------
/level-17_container-pattern/actions/TodoActions.js:
--------------------------------------------------------------------------------
1 | const {
2 | ActionTypes,
3 | AppDispatcher
4 | } = window.App;
5 |
6 | window.App.TodoActions = {
7 | loadTodos() {
8 | fetch('./todos.json')
9 | .then((response) => response.json())
10 | .then((todos) => AppDispatcher.dispatch({
11 | type: ActionTypes.LOAD_TODOS_SUCCESS,
12 | todos
13 | }));
14 | },
15 | createTodo(title) {
16 | AppDispatcher.dispatch({
17 | type: ActionTypes.CREATE_TODO,
18 | title
19 | });
20 | },
21 | updateTodo(id, title) {
22 | AppDispatcher.dispatch({
23 | type: ActionTypes.UPDATE_TODO,
24 | id,
25 | title
26 | });
27 | },
28 | toggleTodo(id, completed) {
29 | AppDispatcher.dispatch({
30 | type: ActionTypes.TOGGLE_TODO,
31 | id,
32 | completed
33 | });
34 | },
35 | deleteTodo(id) {
36 | AppDispatcher.dispatch({
37 | type: ActionTypes.DELETE_TODO,
38 | id
39 | });
40 | }
41 | };
42 |
--------------------------------------------------------------------------------
/level-16_flux-controller-view/actions/TodoActions.js:
--------------------------------------------------------------------------------
1 | const {
2 | ActionTypes,
3 | AppDispatcher
4 | } = window.App;
5 |
6 | window.App.TodoActions = {
7 | loadTodos() {
8 | fetch('./todos.json')
9 | .then((response) => response.json())
10 | .then((todos) => AppDispatcher.dispatch({
11 | type: ActionTypes.LOAD_TODOS_SUCCESS,
12 | todos
13 | }));
14 | },
15 | createTodo(title) {
16 | AppDispatcher.dispatch({
17 | type: ActionTypes.CREATE_TODO,
18 | title
19 | });
20 | },
21 | updateTodo(id, title) {
22 | AppDispatcher.dispatch({
23 | type: ActionTypes.UPDATE_TODO,
24 | id,
25 | title
26 | });
27 | },
28 | toggleTodo(id, completed) {
29 | AppDispatcher.dispatch({
30 | type: ActionTypes.TOGGLE_TODO,
31 | id,
32 | completed
33 | });
34 | },
35 | deleteTodo(id) {
36 | AppDispatcher.dispatch({
37 | type: ActionTypes.DELETE_TODO,
38 | id
39 | });
40 | }
41 | };
42 |
--------------------------------------------------------------------------------
/level-10_forms/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
25 |
26 |
--------------------------------------------------------------------------------
/level-06_transferring-props/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
25 |
26 |
--------------------------------------------------------------------------------
/level-08_dynamic-children/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
25 |
26 |
--------------------------------------------------------------------------------
/level-09_stateful-component/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
25 |
26 |
--------------------------------------------------------------------------------
/level-05_component-composition/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
25 |
26 |
--------------------------------------------------------------------------------
/level-07_prop-types-n-default-values/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
25 |
26 |
--------------------------------------------------------------------------------
/level-25_immutablejs/components/TodoList.js:
--------------------------------------------------------------------------------
1 | const { TodoItem } = window.App;
2 |
3 | class TodoList extends React.Component {
4 | render() {
5 | const {
6 | todos,
7 | onUpdateTodo,
8 | onToggleTodo,
9 | onDeleteTodo
10 | } = this.props;
11 | const todoElements = todos.map((todo) => (
12 |
13 | onUpdateTodo && onUpdateTodo(todo.id, content)}
17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)}
18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)}
19 | />
20 |
21 | ));
22 | return ;
23 | }
24 | }
25 |
26 | TodoList.propTypes = {
27 | todos: React.PropTypes.object.isRequired,
28 | onUpdateTodo: React.PropTypes.func,
29 | onToggleTodo: React.PropTypes.func,
30 | onDeleteTodo: React.PropTypes.func
31 | };
32 |
33 | window.App.TodoList = TodoList;
34 |
--------------------------------------------------------------------------------
/level-10_forms/TodoList.js:
--------------------------------------------------------------------------------
1 | const { TodoItem } = window.App;
2 |
3 | class TodoList extends React.Component {
4 | render() {
5 | const {
6 | todos,
7 | onUpdateTodo,
8 | onToggleTodo,
9 | onDeleteTodo
10 | } = this.props;
11 | const todoElements = todos.map((todo) => (
12 |
13 | onUpdateTodo && onUpdateTodo(todo.id, content)}
17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)}
18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)}
19 | />
20 |
21 | ));
22 | return ;
23 | }
24 | }
25 |
26 | TodoList.propTypes = {
27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
28 | onUpdateTodo: React.PropTypes.func,
29 | onToggleTodo: React.PropTypes.func,
30 | onDeleteTodo: React.PropTypes.func
31 | };
32 |
33 | window.App.TodoList = TodoList;
34 |
--------------------------------------------------------------------------------
/level-11_component-lifecycle/TodoList.js:
--------------------------------------------------------------------------------
1 | const { TodoItem } = window.App;
2 |
3 | class TodoList extends React.Component {
4 | render() {
5 | const {
6 | todos,
7 | onUpdateTodo,
8 | onToggleTodo,
9 | onDeleteTodo
10 | } = this.props;
11 | const todoElements = todos.map((todo) => (
12 |
13 | onUpdateTodo && onUpdateTodo(todo.id, content)}
17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)}
18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)}
19 | />
20 |
21 | ));
22 | return ;
23 | }
24 | }
25 |
26 | TodoList.propTypes = {
27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
28 | onUpdateTodo: React.PropTypes.func,
29 | onToggleTodo: React.PropTypes.func,
30 | onDeleteTodo: React.PropTypes.func
31 | };
32 |
33 | window.App.TodoList = TodoList;
34 |
--------------------------------------------------------------------------------
/level-15_flux-stores/components/TodoList.js:
--------------------------------------------------------------------------------
1 | const { TodoItem } = window.App;
2 |
3 | class TodoList extends React.Component {
4 | render() {
5 | const {
6 | todos,
7 | onUpdateTodo,
8 | onToggleTodo,
9 | onDeleteTodo
10 | } = this.props;
11 | const todoElements = todos.map((todo) => (
12 |
13 | onUpdateTodo && onUpdateTodo(todo.id, content)}
17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)}
18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)}
19 | />
20 |
21 | ));
22 | return ;
23 | }
24 | }
25 |
26 | TodoList.propTypes = {
27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
28 | onUpdateTodo: React.PropTypes.func,
29 | onToggleTodo: React.PropTypes.func,
30 | onDeleteTodo: React.PropTypes.func
31 | };
32 |
33 | window.App.TodoList = TodoList;
34 |
--------------------------------------------------------------------------------
/level-18_flux-utils/components/TodoList.js:
--------------------------------------------------------------------------------
1 | const { TodoItem } = window.App;
2 |
3 | class TodoList extends React.Component {
4 | render() {
5 | const {
6 | todos,
7 | onUpdateTodo,
8 | onToggleTodo,
9 | onDeleteTodo
10 | } = this.props;
11 | const todoElements = todos.map((todo) => (
12 |
13 | onUpdateTodo && onUpdateTodo(todo.id, content)}
17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)}
18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)}
19 | />
20 |
21 | ));
22 | return ;
23 | }
24 | }
25 |
26 | TodoList.propTypes = {
27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
28 | onUpdateTodo: React.PropTypes.func,
29 | onToggleTodo: React.PropTypes.func,
30 | onDeleteTodo: React.PropTypes.func
31 | };
32 |
33 | window.App.TodoList = TodoList;
34 |
--------------------------------------------------------------------------------
/level-21_redux-store/components/TodoList.js:
--------------------------------------------------------------------------------
1 | const { TodoItem } = window.App;
2 |
3 | class TodoList extends React.Component {
4 | render() {
5 | const {
6 | todos,
7 | onUpdateTodo,
8 | onToggleTodo,
9 | onDeleteTodo
10 | } = this.props;
11 | const todoElements = todos.map((todo) => (
12 |
13 | onUpdateTodo && onUpdateTodo(todo.id, content)}
17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)}
18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)}
19 | />
20 |
21 | ));
22 | return ;
23 | }
24 | }
25 |
26 | TodoList.propTypes = {
27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
28 | onUpdateTodo: React.PropTypes.func,
29 | onToggleTodo: React.PropTypes.func,
30 | onDeleteTodo: React.PropTypes.func
31 | };
32 |
33 | window.App.TodoList = TodoList;
34 |
--------------------------------------------------------------------------------
/level-24_react-redux/components/TodoList.js:
--------------------------------------------------------------------------------
1 | const { TodoItem } = window.App;
2 |
3 | class TodoList extends React.Component {
4 | render() {
5 | const {
6 | todos,
7 | onUpdateTodo,
8 | onToggleTodo,
9 | onDeleteTodo
10 | } = this.props;
11 | const todoElements = todos.map((todo) => (
12 |
13 | onUpdateTodo && onUpdateTodo(todo.id, content)}
17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)}
18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)}
19 | />
20 |
21 | ));
22 | return ;
23 | }
24 | }
25 |
26 | TodoList.propTypes = {
27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
28 | onUpdateTodo: React.PropTypes.func,
29 | onToggleTodo: React.PropTypes.func,
30 | onDeleteTodo: React.PropTypes.func
31 | };
32 |
33 | window.App.TodoList = TodoList;
34 |
--------------------------------------------------------------------------------
/level-13_flux-dispatcher/components/TodoList.js:
--------------------------------------------------------------------------------
1 | const { TodoItem } = window.App;
2 |
3 | class TodoList extends React.Component {
4 | render() {
5 | const {
6 | todos,
7 | onUpdateTodo,
8 | onToggleTodo,
9 | onDeleteTodo
10 | } = this.props;
11 | const todoElements = todos.map((todo) => (
12 |
13 | onUpdateTodo && onUpdateTodo(todo.id, content)}
17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)}
18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)}
19 | />
20 |
21 | ));
22 | return ;
23 | }
24 | }
25 |
26 | TodoList.propTypes = {
27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
28 | onUpdateTodo: React.PropTypes.func,
29 | onToggleTodo: React.PropTypes.func,
30 | onDeleteTodo: React.PropTypes.func
31 | };
32 |
33 | window.App.TodoList = TodoList;
34 |
--------------------------------------------------------------------------------
/level-14_flux-actions/components/TodoList.js:
--------------------------------------------------------------------------------
1 | const { TodoItem } = window.App;
2 |
3 | class TodoList extends React.Component {
4 | render() {
5 | const {
6 | todos,
7 | onUpdateTodo,
8 | onToggleTodo,
9 | onDeleteTodo
10 | } = this.props;
11 | const todoElements = todos.map((todo) => (
12 |
13 | onUpdateTodo && onUpdateTodo(todo.id, content)}
17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)}
18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)}
19 | />
20 |
21 | ));
22 | return ;
23 | }
24 | }
25 |
26 | TodoList.propTypes = {
27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
28 | onUpdateTodo: React.PropTypes.func,
29 | onToggleTodo: React.PropTypes.func,
30 | onDeleteTodo: React.PropTypes.func
31 | };
32 |
33 | window.App.TodoList = TodoList;
34 |
--------------------------------------------------------------------------------
/level-20_redux-reducers/components/TodoList.js:
--------------------------------------------------------------------------------
1 | const { TodoItem } = window.App;
2 |
3 | class TodoList extends React.Component {
4 | render() {
5 | const {
6 | todos,
7 | onUpdateTodo,
8 | onToggleTodo,
9 | onDeleteTodo
10 | } = this.props;
11 | const todoElements = todos.map((todo) => (
12 |
13 | onUpdateTodo && onUpdateTodo(todo.id, content)}
17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)}
18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)}
19 | />
20 |
21 | ));
22 | return ;
23 | }
24 | }
25 |
26 | TodoList.propTypes = {
27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
28 | onUpdateTodo: React.PropTypes.func,
29 | onToggleTodo: React.PropTypes.func,
30 | onDeleteTodo: React.PropTypes.func
31 | };
32 |
33 | window.App.TodoList = TodoList;
34 |
--------------------------------------------------------------------------------
/level-22_redux-actions/components/TodoList.js:
--------------------------------------------------------------------------------
1 | const { TodoItem } = window.App;
2 |
3 | class TodoList extends React.Component {
4 | render() {
5 | const {
6 | todos,
7 | onUpdateTodo,
8 | onToggleTodo,
9 | onDeleteTodo
10 | } = this.props;
11 | const todoElements = todos.map((todo) => (
12 |
13 | onUpdateTodo && onUpdateTodo(todo.id, content)}
17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)}
18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)}
19 | />
20 |
21 | ));
22 | return ;
23 | }
24 | }
25 |
26 | TodoList.propTypes = {
27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
28 | onUpdateTodo: React.PropTypes.func,
29 | onToggleTodo: React.PropTypes.func,
30 | onDeleteTodo: React.PropTypes.func
31 | };
32 |
33 | window.App.TodoList = TodoList;
34 |
--------------------------------------------------------------------------------
/level-16_flux-controller-view/components/TodoList.js:
--------------------------------------------------------------------------------
1 | const { TodoItem } = window.App;
2 |
3 | class TodoList extends React.Component {
4 | render() {
5 | const {
6 | todos,
7 | onUpdateTodo,
8 | onToggleTodo,
9 | onDeleteTodo
10 | } = this.props;
11 | const todoElements = todos.map((todo) => (
12 |
13 | onUpdateTodo && onUpdateTodo(todo.id, content)}
17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)}
18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)}
19 | />
20 |
21 | ));
22 | return ;
23 | }
24 | }
25 |
26 | TodoList.propTypes = {
27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
28 | onUpdateTodo: React.PropTypes.func,
29 | onToggleTodo: React.PropTypes.func,
30 | onDeleteTodo: React.PropTypes.func
31 | };
32 |
33 | window.App.TodoList = TodoList;
34 |
--------------------------------------------------------------------------------
/level-17_container-pattern/components/TodoList.js:
--------------------------------------------------------------------------------
1 | const { TodoItem } = window.App;
2 |
3 | class TodoList extends React.Component {
4 | render() {
5 | const {
6 | todos,
7 | onUpdateTodo,
8 | onToggleTodo,
9 | onDeleteTodo
10 | } = this.props;
11 | const todoElements = todos.map((todo) => (
12 |
13 | onUpdateTodo && onUpdateTodo(todo.id, content)}
17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)}
18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)}
19 | />
20 |
21 | ));
22 | return ;
23 | }
24 | }
25 |
26 | TodoList.propTypes = {
27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
28 | onUpdateTodo: React.PropTypes.func,
29 | onToggleTodo: React.PropTypes.func,
30 | onDeleteTodo: React.PropTypes.func
31 | };
32 |
33 | window.App.TodoList = TodoList;
34 |
--------------------------------------------------------------------------------
/level-23_redux-middlewares/components/TodoList.js:
--------------------------------------------------------------------------------
1 | const { TodoItem } = window.App;
2 |
3 | class TodoList extends React.Component {
4 | render() {
5 | const {
6 | todos,
7 | onUpdateTodo,
8 | onToggleTodo,
9 | onDeleteTodo
10 | } = this.props;
11 | const todoElements = todos.map((todo) => (
12 |
13 | onUpdateTodo && onUpdateTodo(todo.id, content)}
17 | onToggle={(completed) => onToggleTodo && onToggleTodo(todo.id, completed)}
18 | onDelete={() => onDeleteTodo && onDeleteTodo(todo.id)}
19 | />
20 |
21 | ));
22 | return ;
23 | }
24 | }
25 |
26 | TodoList.propTypes = {
27 | todos: React.PropTypes.arrayOf(React.PropTypes.object).isRequired,
28 | onUpdateTodo: React.PropTypes.func,
29 | onToggleTodo: React.PropTypes.func,
30 | onDeleteTodo: React.PropTypes.func
31 | };
32 |
33 | window.App.TodoList = TodoList;
34 |
--------------------------------------------------------------------------------
/level-11_component-lifecycle/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
26 |
27 |
--------------------------------------------------------------------------------
/level-10_forms/InputField.js:
--------------------------------------------------------------------------------
1 | class InputField extends React.Component {
2 | constructor(props, context) {
3 | super(props, context);
4 | this.state = { value: props.value || '' };
5 | this.handleChange = this.handleChange.bind(this);
6 | this.handleKeyDown = this.handleKeyDown.bind(this);
7 | }
8 |
9 | handleChange(e) {
10 | this.setState({ value: e.target.value });
11 | }
12 |
13 | handleKeyDown(e) {
14 | const {
15 | onKeyDown,
16 | onSubmitEditing
17 | } = this.props;
18 | const { value } = this.state;
19 | switch (e.keyCode) {
20 | case 13:
21 | if (value.trim()) {
22 | onSubmitEditing && onSubmitEditing(value);
23 | }
24 | this.setState({ value: '' });
25 | break;
26 | }
27 | onKeyDown && onKeyDown(e);
28 | }
29 |
30 | render() {
31 | return (
32 |
39 | );
40 | }
41 | }
42 |
43 | InputField.propTypes = {
44 | onSubmitEditing: React.PropTypes.func
45 | };
46 |
47 | window.App.InputField = InputField;
48 |
--------------------------------------------------------------------------------
/level-11_component-lifecycle/InputField.js:
--------------------------------------------------------------------------------
1 | class InputField extends React.Component {
2 | constructor(props, context) {
3 | super(props, context);
4 | this.state = { value: props.value || '' };
5 | this.handleChange = this.handleChange.bind(this);
6 | this.handleKeyDown = this.handleKeyDown.bind(this);
7 | }
8 |
9 | handleChange(e) {
10 | this.setState({ value: e.target.value });
11 | }
12 |
13 | handleKeyDown(e) {
14 | const {
15 | onKeyDown,
16 | onSubmitEditing
17 | } = this.props;
18 | const { value } = this.state;
19 | switch (e.keyCode) {
20 | case 13:
21 | if (value.trim()) {
22 | onSubmitEditing && onSubmitEditing(value);
23 | }
24 | this.setState({ value: '' });
25 | break;
26 | }
27 | onKeyDown && onKeyDown(e);
28 | }
29 |
30 | render() {
31 | return (
32 |
39 | );
40 | }
41 | }
42 |
43 | InputField.propTypes = {
44 | onSubmitEditing: React.PropTypes.func
45 | };
46 |
47 | window.App.InputField = InputField;
48 |
--------------------------------------------------------------------------------
/level-15_flux-stores/components/InputField.js:
--------------------------------------------------------------------------------
1 | class InputField extends React.Component {
2 | constructor(props, context) {
3 | super(props, context);
4 | this.state = { value: props.value || '' };
5 | this.handleChange = this.handleChange.bind(this);
6 | this.handleKeyDown = this.handleKeyDown.bind(this);
7 | }
8 |
9 | handleChange(e) {
10 | this.setState({ value: e.target.value });
11 | }
12 |
13 | handleKeyDown(e) {
14 | const {
15 | onKeyDown,
16 | onSubmitEditing
17 | } = this.props;
18 | const { value } = this.state;
19 | switch (e.keyCode) {
20 | case 13:
21 | if (value.trim()) {
22 | onSubmitEditing && onSubmitEditing(value);
23 | }
24 | this.setState({ value: '' });
25 | break;
26 | }
27 | onKeyDown && onKeyDown(e);
28 | }
29 |
30 | render() {
31 | return (
32 |
39 | );
40 | }
41 | }
42 |
43 | InputField.propTypes = {
44 | onSubmitEditing: React.PropTypes.func
45 | };
46 |
47 | window.App.InputField = InputField;
48 |
--------------------------------------------------------------------------------
/level-18_flux-utils/components/InputField.js:
--------------------------------------------------------------------------------
1 | class InputField extends React.Component {
2 | constructor(props, context) {
3 | super(props, context);
4 | this.state = { value: props.value || '' };
5 | this.handleChange = this.handleChange.bind(this);
6 | this.handleKeyDown = this.handleKeyDown.bind(this);
7 | }
8 |
9 | handleChange(e) {
10 | this.setState({ value: e.target.value });
11 | }
12 |
13 | handleKeyDown(e) {
14 | const {
15 | onKeyDown,
16 | onSubmitEditing
17 | } = this.props;
18 | const { value } = this.state;
19 | switch (e.keyCode) {
20 | case 13:
21 | if (value.trim()) {
22 | onSubmitEditing && onSubmitEditing(value);
23 | }
24 | this.setState({ value: '' });
25 | break;
26 | }
27 | onKeyDown && onKeyDown(e);
28 | }
29 |
30 | render() {
31 | return (
32 |
39 | );
40 | }
41 | }
42 |
43 | InputField.propTypes = {
44 | onSubmitEditing: React.PropTypes.func
45 | };
46 |
47 | window.App.InputField = InputField;
48 |
--------------------------------------------------------------------------------
/level-21_redux-store/components/InputField.js:
--------------------------------------------------------------------------------
1 | class InputField extends React.Component {
2 | constructor(props, context) {
3 | super(props, context);
4 | this.state = { value: props.value || '' };
5 | this.handleChange = this.handleChange.bind(this);
6 | this.handleKeyDown = this.handleKeyDown.bind(this);
7 | }
8 |
9 | handleChange(e) {
10 | this.setState({ value: e.target.value });
11 | }
12 |
13 | handleKeyDown(e) {
14 | const {
15 | onKeyDown,
16 | onSubmitEditing
17 | } = this.props;
18 | const { value } = this.state;
19 | switch (e.keyCode) {
20 | case 13:
21 | if (value.trim()) {
22 | onSubmitEditing && onSubmitEditing(value);
23 | }
24 | this.setState({ value: '' });
25 | break;
26 | }
27 | onKeyDown && onKeyDown(e);
28 | }
29 |
30 | render() {
31 | return (
32 |
39 | );
40 | }
41 | }
42 |
43 | InputField.propTypes = {
44 | onSubmitEditing: React.PropTypes.func
45 | };
46 |
47 | window.App.InputField = InputField;
48 |
--------------------------------------------------------------------------------
/level-24_react-redux/components/InputField.js:
--------------------------------------------------------------------------------
1 | class InputField extends React.Component {
2 | constructor(props, context) {
3 | super(props, context);
4 | this.state = { value: props.value || '' };
5 | this.handleChange = this.handleChange.bind(this);
6 | this.handleKeyDown = this.handleKeyDown.bind(this);
7 | }
8 |
9 | handleChange(e) {
10 | this.setState({ value: e.target.value });
11 | }
12 |
13 | handleKeyDown(e) {
14 | const {
15 | onKeyDown,
16 | onSubmitEditing
17 | } = this.props;
18 | const { value } = this.state;
19 | switch (e.keyCode) {
20 | case 13:
21 | if (value.trim()) {
22 | onSubmitEditing && onSubmitEditing(value);
23 | }
24 | this.setState({ value: '' });
25 | break;
26 | }
27 | onKeyDown && onKeyDown(e);
28 | }
29 |
30 | render() {
31 | return (
32 |
39 | );
40 | }
41 | }
42 |
43 | InputField.propTypes = {
44 | onSubmitEditing: React.PropTypes.func
45 | };
46 |
47 | window.App.InputField = InputField;
48 |
--------------------------------------------------------------------------------
/level-25_immutablejs/components/InputField.js:
--------------------------------------------------------------------------------
1 | class InputField extends React.Component {
2 | constructor(props, context) {
3 | super(props, context);
4 | this.state = { value: props.value || '' };
5 | this.handleChange = this.handleChange.bind(this);
6 | this.handleKeyDown = this.handleKeyDown.bind(this);
7 | }
8 |
9 | handleChange(e) {
10 | this.setState({ value: e.target.value });
11 | }
12 |
13 | handleKeyDown(e) {
14 | const {
15 | onKeyDown,
16 | onSubmitEditing
17 | } = this.props;
18 | const { value } = this.state;
19 | switch (e.keyCode) {
20 | case 13:
21 | if (value.trim()) {
22 | onSubmitEditing && onSubmitEditing(value);
23 | }
24 | this.setState({ value: '' });
25 | break;
26 | }
27 | onKeyDown && onKeyDown(e);
28 | }
29 |
30 | render() {
31 | return (
32 |
39 | );
40 | }
41 | }
42 |
43 | InputField.propTypes = {
44 | onSubmitEditing: React.PropTypes.func
45 | };
46 |
47 | window.App.InputField = InputField;
48 |
--------------------------------------------------------------------------------
/level-13_flux-dispatcher/components/InputField.js:
--------------------------------------------------------------------------------
1 | class InputField extends React.Component {
2 | constructor(props, context) {
3 | super(props, context);
4 | this.state = { value: props.value || '' };
5 | this.handleChange = this.handleChange.bind(this);
6 | this.handleKeyDown = this.handleKeyDown.bind(this);
7 | }
8 |
9 | handleChange(e) {
10 | this.setState({ value: e.target.value });
11 | }
12 |
13 | handleKeyDown(e) {
14 | const {
15 | onKeyDown,
16 | onSubmitEditing
17 | } = this.props;
18 | const { value } = this.state;
19 | switch (e.keyCode) {
20 | case 13:
21 | if (value.trim()) {
22 | onSubmitEditing && onSubmitEditing(value);
23 | }
24 | this.setState({ value: '' });
25 | break;
26 | }
27 | onKeyDown && onKeyDown(e);
28 | }
29 |
30 | render() {
31 | return (
32 |
39 | );
40 | }
41 | }
42 |
43 | InputField.propTypes = {
44 | onSubmitEditing: React.PropTypes.func
45 | };
46 |
47 | window.App.InputField = InputField;
48 |
--------------------------------------------------------------------------------
/level-14_flux-actions/components/InputField.js:
--------------------------------------------------------------------------------
1 | class InputField extends React.Component {
2 | constructor(props, context) {
3 | super(props, context);
4 | this.state = { value: props.value || '' };
5 | this.handleChange = this.handleChange.bind(this);
6 | this.handleKeyDown = this.handleKeyDown.bind(this);
7 | }
8 |
9 | handleChange(e) {
10 | this.setState({ value: e.target.value });
11 | }
12 |
13 | handleKeyDown(e) {
14 | const {
15 | onKeyDown,
16 | onSubmitEditing
17 | } = this.props;
18 | const { value } = this.state;
19 | switch (e.keyCode) {
20 | case 13:
21 | if (value.trim()) {
22 | onSubmitEditing && onSubmitEditing(value);
23 | }
24 | this.setState({ value: '' });
25 | break;
26 | }
27 | onKeyDown && onKeyDown(e);
28 | }
29 |
30 | render() {
31 | return (
32 |
39 | );
40 | }
41 | }
42 |
43 | InputField.propTypes = {
44 | onSubmitEditing: React.PropTypes.func
45 | };
46 |
47 | window.App.InputField = InputField;
48 |
--------------------------------------------------------------------------------
/level-20_redux-reducers/components/InputField.js:
--------------------------------------------------------------------------------
1 | class InputField extends React.Component {
2 | constructor(props, context) {
3 | super(props, context);
4 | this.state = { value: props.value || '' };
5 | this.handleChange = this.handleChange.bind(this);
6 | this.handleKeyDown = this.handleKeyDown.bind(this);
7 | }
8 |
9 | handleChange(e) {
10 | this.setState({ value: e.target.value });
11 | }
12 |
13 | handleKeyDown(e) {
14 | const {
15 | onKeyDown,
16 | onSubmitEditing
17 | } = this.props;
18 | const { value } = this.state;
19 | switch (e.keyCode) {
20 | case 13:
21 | if (value.trim()) {
22 | onSubmitEditing && onSubmitEditing(value);
23 | }
24 | this.setState({ value: '' });
25 | break;
26 | }
27 | onKeyDown && onKeyDown(e);
28 | }
29 |
30 | render() {
31 | return (
32 |
39 | );
40 | }
41 | }
42 |
43 | InputField.propTypes = {
44 | onSubmitEditing: React.PropTypes.func
45 | };
46 |
47 | window.App.InputField = InputField;
48 |
--------------------------------------------------------------------------------
/level-22_redux-actions/components/InputField.js:
--------------------------------------------------------------------------------
1 | class InputField extends React.Component {
2 | constructor(props, context) {
3 | super(props, context);
4 | this.state = { value: props.value || '' };
5 | this.handleChange = this.handleChange.bind(this);
6 | this.handleKeyDown = this.handleKeyDown.bind(this);
7 | }
8 |
9 | handleChange(e) {
10 | this.setState({ value: e.target.value });
11 | }
12 |
13 | handleKeyDown(e) {
14 | const {
15 | onKeyDown,
16 | onSubmitEditing
17 | } = this.props;
18 | const { value } = this.state;
19 | switch (e.keyCode) {
20 | case 13:
21 | if (value.trim()) {
22 | onSubmitEditing && onSubmitEditing(value);
23 | }
24 | this.setState({ value: '' });
25 | break;
26 | }
27 | onKeyDown && onKeyDown(e);
28 | }
29 |
30 | render() {
31 | return (
32 |
39 | );
40 | }
41 | }
42 |
43 | InputField.propTypes = {
44 | onSubmitEditing: React.PropTypes.func
45 | };
46 |
47 | window.App.InputField = InputField;
48 |
--------------------------------------------------------------------------------
/level-16_flux-controller-view/components/InputField.js:
--------------------------------------------------------------------------------
1 | class InputField extends React.Component {
2 | constructor(props, context) {
3 | super(props, context);
4 | this.state = { value: props.value || '' };
5 | this.handleChange = this.handleChange.bind(this);
6 | this.handleKeyDown = this.handleKeyDown.bind(this);
7 | }
8 |
9 | handleChange(e) {
10 | this.setState({ value: e.target.value });
11 | }
12 |
13 | handleKeyDown(e) {
14 | const {
15 | onKeyDown,
16 | onSubmitEditing
17 | } = this.props;
18 | const { value } = this.state;
19 | switch (e.keyCode) {
20 | case 13:
21 | if (value.trim()) {
22 | onSubmitEditing && onSubmitEditing(value);
23 | }
24 | this.setState({ value: '' });
25 | break;
26 | }
27 | onKeyDown && onKeyDown(e);
28 | }
29 |
30 | render() {
31 | return (
32 |
39 | );
40 | }
41 | }
42 |
43 | InputField.propTypes = {
44 | onSubmitEditing: React.PropTypes.func
45 | };
46 |
47 | window.App.InputField = InputField;
48 |
--------------------------------------------------------------------------------
/level-17_container-pattern/components/InputField.js:
--------------------------------------------------------------------------------
1 | class InputField extends React.Component {
2 | constructor(props, context) {
3 | super(props, context);
4 | this.state = { value: props.value || '' };
5 | this.handleChange = this.handleChange.bind(this);
6 | this.handleKeyDown = this.handleKeyDown.bind(this);
7 | }
8 |
9 | handleChange(e) {
10 | this.setState({ value: e.target.value });
11 | }
12 |
13 | handleKeyDown(e) {
14 | const {
15 | onKeyDown,
16 | onSubmitEditing
17 | } = this.props;
18 | const { value } = this.state;
19 | switch (e.keyCode) {
20 | case 13:
21 | if (value.trim()) {
22 | onSubmitEditing && onSubmitEditing(value);
23 | }
24 | this.setState({ value: '' });
25 | break;
26 | }
27 | onKeyDown && onKeyDown(e);
28 | }
29 |
30 | render() {
31 | return (
32 |
39 | );
40 | }
41 | }
42 |
43 | InputField.propTypes = {
44 | onSubmitEditing: React.PropTypes.func
45 | };
46 |
47 | window.App.InputField = InputField;
48 |
--------------------------------------------------------------------------------
/level-23_redux-middlewares/components/InputField.js:
--------------------------------------------------------------------------------
1 | class InputField extends React.Component {
2 | constructor(props, context) {
3 | super(props, context);
4 | this.state = { value: props.value || '' };
5 | this.handleChange = this.handleChange.bind(this);
6 | this.handleKeyDown = this.handleKeyDown.bind(this);
7 | }
8 |
9 | handleChange(e) {
10 | this.setState({ value: e.target.value });
11 | }
12 |
13 | handleKeyDown(e) {
14 | const {
15 | onKeyDown,
16 | onSubmitEditing
17 | } = this.props;
18 | const { value } = this.state;
19 | switch (e.keyCode) {
20 | case 13:
21 | if (value.trim()) {
22 | onSubmitEditing && onSubmitEditing(value);
23 | }
24 | this.setState({ value: '' });
25 | break;
26 | }
27 | onKeyDown && onKeyDown(e);
28 | }
29 |
30 | render() {
31 | return (
32 |
39 | );
40 | }
41 | }
42 |
43 | InputField.propTypes = {
44 | onSubmitEditing: React.PropTypes.func
45 | };
46 |
47 | window.App.InputField = InputField;
48 |
--------------------------------------------------------------------------------
/level-01_react/README.md:
--------------------------------------------------------------------------------
1 | # Level 1. 用元件思維設計應用程式
2 |
3 | 歡迎來到「24 小時,React 快速入門」系列教學 :mortar_board: Level 1 ~!
4 | > :bowtie::Wish you have a happy learning!
5 |
6 |
7 | ## :checkered_flag: 關卡目標
8 |
9 | 1. 完成主線任務:
10 | 1. 列出 TodoApp 的功能清單
11 | 2. 使用「元件思維」設計 TodoApp
12 | 2. 習得心法:打通任督二脈,了解「**元件思維**」
13 |
14 |
15 | ## :triangular_flag_on_post: 主線任務
16 |
17 | ### 1. 列出 TodoApp 的功能清單
18 |
19 | 相信大家都有用過市面上的 TodoApp,一個簡易的 TodoApp 會有以下功能:
20 |
21 | 1. 列出所有待辦項目
22 | 2. 提示待辦數量
23 | 3. 新增待辦項目
24 | 4. 編輯待辦項目
25 | 5. 刪除待辦項目
26 | 6. 切換項目處理狀態
27 |
28 | 根據以上功能,我們畫出第一版 wireframe:
29 |
30 | 
31 |
32 | ### 2. 使用元件思維,設計 TodoApp
33 |
34 | > :neckbeard::少年,我看你天資聰穎,是萬中選一的前端人,我這有一篇...[祕笈](https://medium.com/p/ab93203f6c53),替你打通元件思維的任督二脈,你斟酌看看 :lollipop:
35 |
36 | 將上一步的 UI 劃分成多個元件,並且替它們取上名稱:
37 |
38 | 
39 |
40 | > :bowtie::在這一階段我們必須盡可能得讓元件可以**重複利用**;思考邏輯是這樣的 - 因為 TodoList 中每一行待辦項目的 UI 都是相同的,僅顯示的資料不一樣,所以我們將待辦項目拉成一個 TodoItem 元件。
41 |
42 |
43 | ## :rocket:
44 |
45 | | [主頁](../../../) | [下一關. 建置簡易的開發環境](../level-02_initial-project) |
46 |
47 | | :raising_hand: [我要提問](https://github.com/shiningjason1989/react-quick-tutorial/issues/new) |
48 |
49 |
50 | 
51 |
--------------------------------------------------------------------------------
/level-13_flux-dispatcher/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
28 |
29 |
--------------------------------------------------------------------------------
/level-16_flux-controller-view/components/TodoApp.js:
--------------------------------------------------------------------------------
1 | const {
2 | TodoActions,
3 | TodoStore,
4 | InputField,
5 | TodoHeader,
6 | TodoList
7 | } = window.App;
8 |
9 | class TodoApp extends React.Component {
10 | constructor(props, context) {
11 | super(props, context);
12 | this.state = {
13 | todos: TodoStore.getAll()
14 | };
15 | }
16 |
17 | componentDidMount() {
18 | TodoActions.loadTodos();
19 | this._removeChangeListener = TodoStore.addChangeListener(
20 | () => this.setState({ todos: TodoStore.getAll() })
21 | );
22 | }
23 |
24 | componentWillUnmount() {
25 | this._removeChangeListener();
26 | }
27 |
28 | render() {
29 | const { todos } = this.state;
30 | return (
31 |
32 | !todo.completed).length}
36 | />
37 |
41 |
47 |
48 | );
49 | }
50 | }
51 |
52 | window.App.TodoApp = TodoApp;
53 |
--------------------------------------------------------------------------------
/level-09_stateful-component/TodoApp.js:
--------------------------------------------------------------------------------
1 | const {
2 | InputField,
3 | TodoHeader,
4 | TodoList
5 | } = window.App;
6 |
7 | const _deleteTodo = (todos, id) => {
8 | const idx = todos.findIndex((todo) => todo.id === id);
9 | if (idx !== -1) todos.splice(idx, 1);
10 | return todos;
11 | };
12 |
13 | class TodoApp extends React.Component {
14 | constructor(props, context) {
15 | super(props, context);
16 | this.state = {
17 | todos: [
18 | {
19 | id: 0,
20 | title: 'Item 1',
21 | completed: false
22 | },
23 | {
24 | id: 1,
25 | title: 'Item 2',
26 | completed: false
27 | },
28 | {
29 | id: 2,
30 | title: 'Item 3',
31 | completed: false
32 | }
33 | ]
34 | };
35 | }
36 |
37 | render() {
38 | const { todos } = this.state;
39 | return (
40 |
41 | !todo.completed).length}
45 | />
46 |
47 | this.setState({
51 | todos: _deleteTodo(todos, ...args)
52 | })
53 | }
54 | />
55 |
56 | );
57 | }
58 | }
59 |
60 | window.App.TodoApp = TodoApp;
61 |
--------------------------------------------------------------------------------
/level-14_flux-actions/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
30 |
31 |
--------------------------------------------------------------------------------
/level-25_immutablejs/reducers/todos.js:
--------------------------------------------------------------------------------
1 | const { List, Record } = Immutable;
2 |
3 | const { ActionTypes } = window.App;
4 |
5 | const TodoRecord = Record({
6 | id: undefined,
7 | title: undefined,
8 | completed: false
9 | });
10 |
11 | const _findIdxById = (todos, id) => todos.findIndex((todo) => todo.id === id);
12 |
13 | const _createTodo = (todos, title) =>
14 | todos.push(new TodoRecord({
15 | id: todos.last().id + 1,
16 | title,
17 | completed: false
18 | }));
19 |
20 | const _updateTodo = (todos, id, title) =>
21 | todos.setIn([ _findIdxById(todos, id), 'title' ], title);
22 |
23 | const _toggleTodo = (todos, id, completed) =>
24 | todos.setIn([ _findIdxById(todos, id), 'completed' ], completed);
25 |
26 | const _deleteTodo = (todos, id) =>
27 | todos.delete(_findIdxById(todos, id));
28 |
29 | window.App.reducers.todos = (state = new List(), action) => {
30 | switch (action.type) {
31 | case ActionTypes.LOAD_TODOS_SUCCESS:
32 | return new List(action.todos).map((todo) => new TodoRecord(todo));
33 | case ActionTypes.CREATE_TODO:
34 | return _createTodo(state, action.title);
35 | case ActionTypes.UPDATE_TODO:
36 | return _updateTodo(state, action.id, action.title);
37 | case ActionTypes.TOGGLE_TODO:
38 | return _toggleTodo(state, action.id, action.completed);
39 | case ActionTypes.DELETE_TODO:
40 | return _deleteTodo(state, action.id);
41 | default:
42 | return state;
43 | }
44 | };
45 |
--------------------------------------------------------------------------------
/level-09_stateful-component/TodoItem.js:
--------------------------------------------------------------------------------
1 | const { InputField } = window.App;
2 |
3 | class TodoItem extends React.Component {
4 | constructor(props, context) {
5 | super(props, context);
6 | this.state = { editable: false };
7 | this.toggleEditMode = this.toggleEditMode.bind(this);
8 | }
9 |
10 | toggleEditMode() {
11 | this.setState({ editable: !this.state.editable });
12 | }
13 |
14 | renderViewMode() {
15 | const {
16 | title,
17 | completed,
18 | onDelete
19 | } = this.props;
20 | return (
21 |
22 |
23 | {title}
24 |
25 |
26 | );
27 | }
28 |
29 | renderEditMode() {
30 | const { title } = this.props;
31 | return (
32 | {
38 | if (e.keyCode === 27) {
39 | e.preventDefault();
40 | this.toggleEditMode();
41 | }
42 | }}
43 | />
44 | );
45 | }
46 |
47 | render() {
48 | return this.state.editable ?
49 | this.renderEditMode() :
50 | this.renderViewMode();
51 | }
52 | }
53 |
54 | TodoItem.propTypes = {
55 | title: React.PropTypes.string.isRequired,
56 | completed: React.PropTypes.bool.isRequired,
57 | onDelete: React.PropTypes.func
58 | };
59 |
60 | window.App.TodoItem = TodoItem;
61 |
--------------------------------------------------------------------------------
/level-15_flux-stores/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
32 |
33 |
--------------------------------------------------------------------------------
/level-16_flux-controller-view/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
32 |
33 |
--------------------------------------------------------------------------------
/level-12_flux/README.md:
--------------------------------------------------------------------------------
1 | # Level 12. 深入淺出 Flux
2 |
3 | 歡迎來到「24 小時,React 快速入門」系列教學 :mortar_board: Level 12 ~!
4 | > :bowtie::Wish you have a happy learning!
5 |
6 |
7 | ## :checkered_flag: 關卡目標
8 |
9 | 1. 習得心法:
10 | 1. 理解 Flux 是什麼,如何用一句話定義 Flux
11 | 2. 清楚 Flux 中每個角色所扮演的職責和互動方式
12 | 3. 認識 Flux 的運作流程
13 |
14 |
15 | ## :triangular_flag_on_post: 打通 Flux 的任督二脈
16 |
17 | > :neckbeard::少年,我看你天資聰穎,是萬中選一的前端人,我這有一篇...[祕笈](https://medium.com/p/44a48c320e11),幫你深入淺出 Flux 的武功心法,你斟酌看看 :lollipop:
18 |
19 | 
20 |
21 | 上方是 Facebook 提供的 Flux 架構圖,要確認你是否吸收了 Flux 精髓,你可以詢問自己以下幾個問題:
22 |
23 | 1. ***Flux 是什麼,可以幫助你什麼(試著用一句話定義)***
24 | 2. ***Flux 主要的四個角色是誰,他們的職責是什麼***
25 | 3. ***看著圖,簡述 Flux 的運作流程***
26 |
27 | 如果你認為自己的回答還有地方卡卡的,可以
28 |
29 | 1. 再讀一次[祕笈](https://medium.com/p/44a48c320e11)
30 | 2. 閱讀下方其他大神的見解
31 | 3. 直接進入下一關,從 code 學習
32 |
33 | 最後,再次回答上面的問題,驗證所學:)
34 |
35 | > :bowtie::Good Luck! :four_leaf_clover: 也非常歡迎你開 issue 直接詢問我 ^ ^
36 |
37 | ###### 參考連結
38 |
39 | - [Flux For Stupid People](http://blog.andrewray.me/flux-for-stupid-people/)
40 | - [A cartoon guide to Flux](https://code-cartoons.com/a-cartoon-guide-to-flux-6157355ab207)
41 | - [The Case for Flux](https://medium.com/swlh/the-case-for-flux-379b7d1982c6)
42 | - [從 Flux 與 MVC 的差異來簡介 Flux](http://blog.techbridge.cc/2016/04/29/introduce-flux-from-flux-and-mvc/)
43 |
44 |
45 | ## :rocket:
46 |
47 | | [主頁](../../../) | [上一關](../level-11_component-lifecycle) | [下一關. 完成 Dispatcher:Flux 最重要的角色](../level-13_flux-dispatcher) |
48 |
49 | | :raising_hand: [我要提問](https://github.com/shiningjason1989/react-quick-tutorial/issues/new) |
50 |
51 |
52 | 
53 |
--------------------------------------------------------------------------------
/level-20_redux-reducers/reducers/todos.js:
--------------------------------------------------------------------------------
1 | const { ActionTypes } = window.App;
2 |
3 | const _createTodo = (todos, title) => {
4 | return [
5 | ...todos,
6 | {
7 | id: todos[todos.length - 1].id + 1,
8 | title,
9 | completed: false
10 | }
11 | ];
12 | };
13 |
14 | const _updateTodo = (todos, id, title) => {
15 | const idx = todos.findIndex((todo) => todo.id === id);
16 | if (idx === -1) return todos;
17 |
18 | const newTodos = [ ...todos ];
19 | newTodos[idx] = {
20 | ...todos[idx],
21 | title
22 | };
23 | return newTodos;
24 | };
25 |
26 | const _toggleTodo = (todos, id, completed) => {
27 | const idx = todos.findIndex((todo) => todo.id === id);
28 | if (idx === -1) return todos;
29 |
30 | const newTodos = [ ...todos ];
31 | newTodos[idx] = {
32 | ...todos[idx],
33 | completed
34 | };
35 | return newTodos;
36 | };
37 |
38 | const _deleteTodo = (todos, id) => {
39 | const idx = todos.findIndex((todo) => todo.id === id);
40 | if (idx === -1) return todos;
41 |
42 | const newTodos = [ ...todos ];
43 | newTodos.splice(idx, 1);
44 | return newTodos;
45 | };
46 |
47 | window.App.reducers.todos = (state = [], action) => {
48 | switch (action.type) {
49 | case ActionTypes.LOAD_TODOS_SUCCESS:
50 | return action.todos;
51 | case ActionTypes.CREATE_TODO:
52 | return _createTodo(state, action.title);
53 | case ActionTypes.UPDATE_TODO:
54 | return _updateTodo(state, action.id, action.title);
55 | case ActionTypes.TOGGLE_TODO:
56 | return _toggleTodo(state, action.id, action.completed);
57 | case ActionTypes.DELETE_TODO:
58 | return _deleteTodo(state, action.id);
59 | default:
60 | return state;
61 | }
62 | };
63 |
--------------------------------------------------------------------------------
/level-21_redux-store/reducers/todos.js:
--------------------------------------------------------------------------------
1 | const { ActionTypes } = window.App;
2 |
3 | const _createTodo = (todos, title) => {
4 | return [
5 | ...todos,
6 | {
7 | id: todos[todos.length - 1].id + 1,
8 | title,
9 | completed: false
10 | }
11 | ];
12 | };
13 |
14 | const _updateTodo = (todos, id, title) => {
15 | const idx = todos.findIndex((todo) => todo.id === id);
16 | if (idx === -1) return todos;
17 |
18 | const newTodos = [ ...todos ];
19 | newTodos[idx] = {
20 | ...todos[idx],
21 | title
22 | };
23 | return newTodos;
24 | };
25 |
26 | const _toggleTodo = (todos, id, completed) => {
27 | const idx = todos.findIndex((todo) => todo.id === id);
28 | if (idx === -1) return todos;
29 |
30 | const newTodos = [ ...todos ];
31 | newTodos[idx] = {
32 | ...todos[idx],
33 | completed
34 | };
35 | return newTodos;
36 | };
37 |
38 | const _deleteTodo = (todos, id) => {
39 | const idx = todos.findIndex((todo) => todo.id === id);
40 | if (idx === -1) return todos;
41 |
42 | const newTodos = [ ...todos ];
43 | newTodos.splice(idx, 1);
44 | return newTodos;
45 | };
46 |
47 | window.App.reducers.todos = (state = [], action) => {
48 | switch (action.type) {
49 | case ActionTypes.LOAD_TODOS_SUCCESS:
50 | return action.todos;
51 | case ActionTypes.CREATE_TODO:
52 | return _createTodo(state, action.title);
53 | case ActionTypes.UPDATE_TODO:
54 | return _updateTodo(state, action.id, action.title);
55 | case ActionTypes.TOGGLE_TODO:
56 | return _toggleTodo(state, action.id, action.completed);
57 | case ActionTypes.DELETE_TODO:
58 | return _deleteTodo(state, action.id);
59 | default:
60 | return state;
61 | }
62 | };
63 |
--------------------------------------------------------------------------------
/level-22_redux-actions/reducers/todos.js:
--------------------------------------------------------------------------------
1 | const { ActionTypes } = window.App;
2 |
3 | const _createTodo = (todos, title) => {
4 | return [
5 | ...todos,
6 | {
7 | id: todos[todos.length - 1].id + 1,
8 | title,
9 | completed: false
10 | }
11 | ];
12 | };
13 |
14 | const _updateTodo = (todos, id, title) => {
15 | const idx = todos.findIndex((todo) => todo.id === id);
16 | if (idx === -1) return todos;
17 |
18 | const newTodos = [ ...todos ];
19 | newTodos[idx] = {
20 | ...todos[idx],
21 | title
22 | };
23 | return newTodos;
24 | };
25 |
26 | const _toggleTodo = (todos, id, completed) => {
27 | const idx = todos.findIndex((todo) => todo.id === id);
28 | if (idx === -1) return todos;
29 |
30 | const newTodos = [ ...todos ];
31 | newTodos[idx] = {
32 | ...todos[idx],
33 | completed
34 | };
35 | return newTodos;
36 | };
37 |
38 | const _deleteTodo = (todos, id) => {
39 | const idx = todos.findIndex((todo) => todo.id === id);
40 | if (idx === -1) return todos;
41 |
42 | const newTodos = [ ...todos ];
43 | newTodos.splice(idx, 1);
44 | return newTodos;
45 | };
46 |
47 | window.App.reducers.todos = (state = [], action) => {
48 | switch (action.type) {
49 | case ActionTypes.LOAD_TODOS_SUCCESS:
50 | return action.todos;
51 | case ActionTypes.CREATE_TODO:
52 | return _createTodo(state, action.title);
53 | case ActionTypes.UPDATE_TODO:
54 | return _updateTodo(state, action.id, action.title);
55 | case ActionTypes.TOGGLE_TODO:
56 | return _toggleTodo(state, action.id, action.completed);
57 | case ActionTypes.DELETE_TODO:
58 | return _deleteTodo(state, action.id);
59 | default:
60 | return state;
61 | }
62 | };
63 |
--------------------------------------------------------------------------------
/level-24_react-redux/reducers/todos.js:
--------------------------------------------------------------------------------
1 | const { ActionTypes } = window.App;
2 |
3 | const _createTodo = (todos, title) => {
4 | return [
5 | ...todos,
6 | {
7 | id: todos[todos.length - 1].id + 1,
8 | title,
9 | completed: false
10 | }
11 | ];
12 | };
13 |
14 | const _updateTodo = (todos, id, title) => {
15 | const idx = todos.findIndex((todo) => todo.id === id);
16 | if (idx === -1) return todos;
17 |
18 | const newTodos = [ ...todos ];
19 | newTodos[idx] = {
20 | ...todos[idx],
21 | title
22 | };
23 | return newTodos;
24 | };
25 |
26 | const _toggleTodo = (todos, id, completed) => {
27 | const idx = todos.findIndex((todo) => todo.id === id);
28 | if (idx === -1) return todos;
29 |
30 | const newTodos = [ ...todos ];
31 | newTodos[idx] = {
32 | ...todos[idx],
33 | completed
34 | };
35 | return newTodos;
36 | };
37 |
38 | const _deleteTodo = (todos, id) => {
39 | const idx = todos.findIndex((todo) => todo.id === id);
40 | if (idx === -1) return todos;
41 |
42 | const newTodos = [ ...todos ];
43 | newTodos.splice(idx, 1);
44 | return newTodos;
45 | };
46 |
47 | window.App.reducers.todos = (state = [], action) => {
48 | switch (action.type) {
49 | case ActionTypes.LOAD_TODOS_SUCCESS:
50 | return action.todos;
51 | case ActionTypes.CREATE_TODO:
52 | return _createTodo(state, action.title);
53 | case ActionTypes.UPDATE_TODO:
54 | return _updateTodo(state, action.id, action.title);
55 | case ActionTypes.TOGGLE_TODO:
56 | return _toggleTodo(state, action.id, action.completed);
57 | case ActionTypes.DELETE_TODO:
58 | return _deleteTodo(state, action.id);
59 | default:
60 | return state;
61 | }
62 | };
63 |
--------------------------------------------------------------------------------
/level-23_redux-middlewares/reducers/todos.js:
--------------------------------------------------------------------------------
1 | const { ActionTypes } = window.App;
2 |
3 | const _createTodo = (todos, title) => {
4 | return [
5 | ...todos,
6 | {
7 | id: todos[todos.length - 1].id + 1,
8 | title,
9 | completed: false
10 | }
11 | ];
12 | };
13 |
14 | const _updateTodo = (todos, id, title) => {
15 | const idx = todos.findIndex((todo) => todo.id === id);
16 | if (idx === -1) return todos;
17 |
18 | const newTodos = [ ...todos ];
19 | newTodos[idx] = {
20 | ...todos[idx],
21 | title
22 | };
23 | return newTodos;
24 | };
25 |
26 | const _toggleTodo = (todos, id, completed) => {
27 | const idx = todos.findIndex((todo) => todo.id === id);
28 | if (idx === -1) return todos;
29 |
30 | const newTodos = [ ...todos ];
31 | newTodos[idx] = {
32 | ...todos[idx],
33 | completed
34 | };
35 | return newTodos;
36 | };
37 |
38 | const _deleteTodo = (todos, id) => {
39 | const idx = todos.findIndex((todo) => todo.id === id);
40 | if (idx === -1) return todos;
41 |
42 | const newTodos = [ ...todos ];
43 | newTodos.splice(idx, 1);
44 | return newTodos;
45 | };
46 |
47 | window.App.reducers.todos = (state = [], action) => {
48 | switch (action.type) {
49 | case ActionTypes.LOAD_TODOS_SUCCESS:
50 | return action.todos;
51 | case ActionTypes.CREATE_TODO:
52 | return _createTodo(state, action.title);
53 | case ActionTypes.UPDATE_TODO:
54 | return _updateTodo(state, action.id, action.title);
55 | case ActionTypes.TOGGLE_TODO:
56 | return _toggleTodo(state, action.id, action.completed);
57 | case ActionTypes.DELETE_TODO:
58 | return _deleteTodo(state, action.id);
59 | default:
60 | return state;
61 | }
62 | };
63 |
--------------------------------------------------------------------------------
/level-24_react-redux/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/level-25_immutablejs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/level-22_redux-actions/actions/TodoActions.js:
--------------------------------------------------------------------------------
1 | const {
2 | ActionTypes,
3 | AppDispatcher
4 | } = window.App;
5 |
6 | window.App.TodoActions = {
7 | loadTodos() {
8 | fetch('./todos.json')
9 | .then((response) => response.json())
10 | .then((todos) => AppDispatcher.dispatch({
11 | type: ActionTypes.LOAD_TODOS_SUCCESS,
12 | todos
13 | }));
14 | },
15 | createTodo(title) {
16 | AppDispatcher.dispatch({
17 | type: ActionTypes.CREATE_TODO,
18 | title
19 | });
20 | },
21 | updateTodo(id, title) {
22 | AppDispatcher.dispatch({
23 | type: ActionTypes.UPDATE_TODO,
24 | id,
25 | title
26 | });
27 | },
28 | toggleTodo(id, completed) {
29 | AppDispatcher.dispatch({
30 | type: ActionTypes.TOGGLE_TODO,
31 | id,
32 | completed
33 | });
34 | },
35 | deleteTodo(id) {
36 | AppDispatcher.dispatch({
37 | type: ActionTypes.DELETE_TODO,
38 | id
39 | });
40 | }
41 | };
42 |
43 | window.App.TodoReduxActions = {
44 | loadTodos() {
45 | return (dispatch) => {
46 | fetch('./todos.json')
47 | .then((response) => response.json())
48 | .then((todos) => dispatch({
49 | type: ActionTypes.LOAD_TODOS_SUCCESS,
50 | todos
51 | }));
52 | };
53 | },
54 | createTodo(title) {
55 | return {
56 | type: ActionTypes.CREATE_TODO,
57 | title
58 | };
59 | },
60 | updateTodo(id, title) {
61 | return {
62 | type: ActionTypes.UPDATE_TODO,
63 | id,
64 | title
65 | };
66 | },
67 | toggleTodo(id, completed) {
68 | return {
69 | type: ActionTypes.TOGGLE_TODO,
70 | id,
71 | completed
72 | };
73 | },
74 | deleteTodo(id) {
75 | return {
76 | type: ActionTypes.DELETE_TODO,
77 | id
78 | };
79 | }
80 | };
81 |
--------------------------------------------------------------------------------
/level-23_redux-middlewares/actions/TodoActions.js:
--------------------------------------------------------------------------------
1 | const {
2 | ActionTypes,
3 | AppDispatcher
4 | } = window.App;
5 |
6 | window.App.TodoActions = {
7 | loadTodos() {
8 | fetch('./todos.json')
9 | .then((response) => response.json())
10 | .then((todos) => AppDispatcher.dispatch({
11 | type: ActionTypes.LOAD_TODOS_SUCCESS,
12 | todos
13 | }));
14 | },
15 | createTodo(title) {
16 | AppDispatcher.dispatch({
17 | type: ActionTypes.CREATE_TODO,
18 | title
19 | });
20 | },
21 | updateTodo(id, title) {
22 | AppDispatcher.dispatch({
23 | type: ActionTypes.UPDATE_TODO,
24 | id,
25 | title
26 | });
27 | },
28 | toggleTodo(id, completed) {
29 | AppDispatcher.dispatch({
30 | type: ActionTypes.TOGGLE_TODO,
31 | id,
32 | completed
33 | });
34 | },
35 | deleteTodo(id) {
36 | AppDispatcher.dispatch({
37 | type: ActionTypes.DELETE_TODO,
38 | id
39 | });
40 | }
41 | };
42 |
43 | window.App.TodoReduxActions = {
44 | loadTodos() {
45 | return (dispatch) => {
46 | fetch('./todos.json')
47 | .then((response) => response.json())
48 | .then((todos) => dispatch({
49 | type: ActionTypes.LOAD_TODOS_SUCCESS,
50 | todos
51 | }));
52 | };
53 | },
54 | createTodo(title) {
55 | return {
56 | type: ActionTypes.CREATE_TODO,
57 | title
58 | };
59 | },
60 | updateTodo(id, title) {
61 | return {
62 | type: ActionTypes.UPDATE_TODO,
63 | id,
64 | title
65 | };
66 | },
67 | toggleTodo(id, completed) {
68 | return {
69 | type: ActionTypes.TOGGLE_TODO,
70 | id,
71 | completed
72 | };
73 | },
74 | deleteTodo(id) {
75 | return {
76 | type: ActionTypes.DELETE_TODO,
77 | id
78 | };
79 | }
80 | };
81 |
--------------------------------------------------------------------------------
/level-17_container-pattern/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
35 |
36 |
--------------------------------------------------------------------------------
/level-10_forms/TodoItem.js:
--------------------------------------------------------------------------------
1 | const { InputField } = window.App;
2 |
3 | class TodoItem extends React.Component {
4 | constructor(props, context) {
5 | super(props, context);
6 | this.state = { editable: false };
7 | this.toggleEditMode = this.toggleEditMode.bind(this);
8 | }
9 |
10 | toggleEditMode() {
11 | this.setState({ editable: !this.state.editable });
12 | }
13 |
14 | renderViewMode() {
15 | const {
16 | title,
17 | completed,
18 | onToggle,
19 | onDelete
20 | } = this.props;
21 | return (
22 |
23 | onToggle && onToggle(!completed)}
27 | />
28 | {title}
29 |
30 |
31 | );
32 | }
33 |
34 | renderEditMode() {
35 | const { title, onUpdate } = this.props;
36 | return (
37 | {
43 | if (e.keyCode === 27) {
44 | e.preventDefault();
45 | this.toggleEditMode();
46 | }
47 | }}
48 | onSubmitEditing={(content) => {
49 | onUpdate && onUpdate(content);
50 | this.toggleEditMode();
51 | }}
52 | />
53 | );
54 | }
55 |
56 | render() {
57 | return this.state.editable ?
58 | this.renderEditMode() :
59 | this.renderViewMode();
60 | }
61 | }
62 |
63 | TodoItem.propTypes = {
64 | title: React.PropTypes.string.isRequired,
65 | completed: React.PropTypes.bool.isRequired,
66 | onUpdate: React.PropTypes.func,
67 | onToggle: React.PropTypes.func,
68 | onDelete: React.PropTypes.func
69 | };
70 |
71 | window.App.TodoItem = TodoItem;
72 |
--------------------------------------------------------------------------------
/level-11_component-lifecycle/TodoItem.js:
--------------------------------------------------------------------------------
1 | const { InputField } = window.App;
2 |
3 | class TodoItem extends React.Component {
4 | constructor(props, context) {
5 | super(props, context);
6 | this.state = { editable: false };
7 | this.toggleEditMode = this.toggleEditMode.bind(this);
8 | }
9 |
10 | toggleEditMode() {
11 | this.setState({ editable: !this.state.editable });
12 | }
13 |
14 | renderViewMode() {
15 | const {
16 | title,
17 | completed,
18 | onToggle,
19 | onDelete
20 | } = this.props;
21 | return (
22 |
23 | onToggle && onToggle(!completed)}
27 | />
28 | {title}
29 |
30 |
31 | );
32 | }
33 |
34 | renderEditMode() {
35 | const { title, onUpdate } = this.props;
36 | return (
37 | {
43 | if (e.keyCode === 27) {
44 | e.preventDefault();
45 | this.toggleEditMode();
46 | }
47 | }}
48 | onSubmitEditing={(content) => {
49 | onUpdate && onUpdate(content);
50 | this.toggleEditMode();
51 | }}
52 | />
53 | );
54 | }
55 |
56 | render() {
57 | return this.state.editable ?
58 | this.renderEditMode() :
59 | this.renderViewMode();
60 | }
61 | }
62 |
63 | TodoItem.propTypes = {
64 | title: React.PropTypes.string.isRequired,
65 | completed: React.PropTypes.bool.isRequired,
66 | onUpdate: React.PropTypes.func,
67 | onToggle: React.PropTypes.func,
68 | onDelete: React.PropTypes.func
69 | };
70 |
71 | window.App.TodoItem = TodoItem;
72 |
--------------------------------------------------------------------------------
/level-14_flux-actions/components/TodoItem.js:
--------------------------------------------------------------------------------
1 | const { InputField } = window.App;
2 |
3 | class TodoItem extends React.Component {
4 | constructor(props, context) {
5 | super(props, context);
6 | this.state = { editable: false };
7 | this.toggleEditMode = this.toggleEditMode.bind(this);
8 | }
9 |
10 | toggleEditMode() {
11 | this.setState({ editable: !this.state.editable });
12 | }
13 |
14 | renderViewMode() {
15 | const {
16 | title,
17 | completed,
18 | onToggle,
19 | onDelete
20 | } = this.props;
21 | return (
22 |
23 | onToggle && onToggle(!completed)}
27 | />
28 | {title}
29 |
30 |
31 | );
32 | }
33 |
34 | renderEditMode() {
35 | const { title, onUpdate } = this.props;
36 | return (
37 | {
43 | if (e.keyCode === 27) {
44 | e.preventDefault();
45 | this.toggleEditMode();
46 | }
47 | }}
48 | onSubmitEditing={(content) => {
49 | onUpdate && onUpdate(content);
50 | this.toggleEditMode();
51 | }}
52 | />
53 | );
54 | }
55 |
56 | render() {
57 | return this.state.editable ?
58 | this.renderEditMode() :
59 | this.renderViewMode();
60 | }
61 | }
62 |
63 | TodoItem.propTypes = {
64 | title: React.PropTypes.string.isRequired,
65 | completed: React.PropTypes.bool.isRequired,
66 | onUpdate: React.PropTypes.func,
67 | onToggle: React.PropTypes.func,
68 | onDelete: React.PropTypes.func
69 | };
70 |
71 | window.App.TodoItem = TodoItem;
72 |
--------------------------------------------------------------------------------
/level-15_flux-stores/components/TodoItem.js:
--------------------------------------------------------------------------------
1 | const { InputField } = window.App;
2 |
3 | class TodoItem extends React.Component {
4 | constructor(props, context) {
5 | super(props, context);
6 | this.state = { editable: false };
7 | this.toggleEditMode = this.toggleEditMode.bind(this);
8 | }
9 |
10 | toggleEditMode() {
11 | this.setState({ editable: !this.state.editable });
12 | }
13 |
14 | renderViewMode() {
15 | const {
16 | title,
17 | completed,
18 | onToggle,
19 | onDelete
20 | } = this.props;
21 | return (
22 |
23 | onToggle && onToggle(!completed)}
27 | />
28 | {title}
29 |
30 |
31 | );
32 | }
33 |
34 | renderEditMode() {
35 | const { title, onUpdate } = this.props;
36 | return (
37 | {
43 | if (e.keyCode === 27) {
44 | e.preventDefault();
45 | this.toggleEditMode();
46 | }
47 | }}
48 | onSubmitEditing={(content) => {
49 | onUpdate && onUpdate(content);
50 | this.toggleEditMode();
51 | }}
52 | />
53 | );
54 | }
55 |
56 | render() {
57 | return this.state.editable ?
58 | this.renderEditMode() :
59 | this.renderViewMode();
60 | }
61 | }
62 |
63 | TodoItem.propTypes = {
64 | title: React.PropTypes.string.isRequired,
65 | completed: React.PropTypes.bool.isRequired,
66 | onUpdate: React.PropTypes.func,
67 | onToggle: React.PropTypes.func,
68 | onDelete: React.PropTypes.func
69 | };
70 |
71 | window.App.TodoItem = TodoItem;
72 |
--------------------------------------------------------------------------------
/level-18_flux-utils/components/TodoItem.js:
--------------------------------------------------------------------------------
1 | const { InputField } = window.App;
2 |
3 | class TodoItem extends React.Component {
4 | constructor(props, context) {
5 | super(props, context);
6 | this.state = { editable: false };
7 | this.toggleEditMode = this.toggleEditMode.bind(this);
8 | }
9 |
10 | toggleEditMode() {
11 | this.setState({ editable: !this.state.editable });
12 | }
13 |
14 | renderViewMode() {
15 | const {
16 | title,
17 | completed,
18 | onToggle,
19 | onDelete
20 | } = this.props;
21 | return (
22 |
23 | onToggle && onToggle(!completed)}
27 | />
28 | {title}
29 |
30 |
31 | );
32 | }
33 |
34 | renderEditMode() {
35 | const { title, onUpdate } = this.props;
36 | return (
37 | {
43 | if (e.keyCode === 27) {
44 | e.preventDefault();
45 | this.toggleEditMode();
46 | }
47 | }}
48 | onSubmitEditing={(content) => {
49 | onUpdate && onUpdate(content);
50 | this.toggleEditMode();
51 | }}
52 | />
53 | );
54 | }
55 |
56 | render() {
57 | return this.state.editable ?
58 | this.renderEditMode() :
59 | this.renderViewMode();
60 | }
61 | }
62 |
63 | TodoItem.propTypes = {
64 | title: React.PropTypes.string.isRequired,
65 | completed: React.PropTypes.bool.isRequired,
66 | onUpdate: React.PropTypes.func,
67 | onToggle: React.PropTypes.func,
68 | onDelete: React.PropTypes.func
69 | };
70 |
71 | window.App.TodoItem = TodoItem;
72 |
--------------------------------------------------------------------------------
/level-21_redux-store/components/TodoItem.js:
--------------------------------------------------------------------------------
1 | const { InputField } = window.App;
2 |
3 | class TodoItem extends React.Component {
4 | constructor(props, context) {
5 | super(props, context);
6 | this.state = { editable: false };
7 | this.toggleEditMode = this.toggleEditMode.bind(this);
8 | }
9 |
10 | toggleEditMode() {
11 | this.setState({ editable: !this.state.editable });
12 | }
13 |
14 | renderViewMode() {
15 | const {
16 | title,
17 | completed,
18 | onToggle,
19 | onDelete
20 | } = this.props;
21 | return (
22 |
23 | onToggle && onToggle(!completed)}
27 | />
28 | {title}
29 |
30 |
31 | );
32 | }
33 |
34 | renderEditMode() {
35 | const { title, onUpdate } = this.props;
36 | return (
37 | {
43 | if (e.keyCode === 27) {
44 | e.preventDefault();
45 | this.toggleEditMode();
46 | }
47 | }}
48 | onSubmitEditing={(content) => {
49 | onUpdate && onUpdate(content);
50 | this.toggleEditMode();
51 | }}
52 | />
53 | );
54 | }
55 |
56 | render() {
57 | return this.state.editable ?
58 | this.renderEditMode() :
59 | this.renderViewMode();
60 | }
61 | }
62 |
63 | TodoItem.propTypes = {
64 | title: React.PropTypes.string.isRequired,
65 | completed: React.PropTypes.bool.isRequired,
66 | onUpdate: React.PropTypes.func,
67 | onToggle: React.PropTypes.func,
68 | onDelete: React.PropTypes.func
69 | };
70 |
71 | window.App.TodoItem = TodoItem;
72 |
--------------------------------------------------------------------------------
/level-22_redux-actions/components/TodoItem.js:
--------------------------------------------------------------------------------
1 | const { InputField } = window.App;
2 |
3 | class TodoItem extends React.Component {
4 | constructor(props, context) {
5 | super(props, context);
6 | this.state = { editable: false };
7 | this.toggleEditMode = this.toggleEditMode.bind(this);
8 | }
9 |
10 | toggleEditMode() {
11 | this.setState({ editable: !this.state.editable });
12 | }
13 |
14 | renderViewMode() {
15 | const {
16 | title,
17 | completed,
18 | onToggle,
19 | onDelete
20 | } = this.props;
21 | return (
22 |
23 | onToggle && onToggle(!completed)}
27 | />
28 | {title}
29 |
30 |
31 | );
32 | }
33 |
34 | renderEditMode() {
35 | const { title, onUpdate } = this.props;
36 | return (
37 | {
43 | if (e.keyCode === 27) {
44 | e.preventDefault();
45 | this.toggleEditMode();
46 | }
47 | }}
48 | onSubmitEditing={(content) => {
49 | onUpdate && onUpdate(content);
50 | this.toggleEditMode();
51 | }}
52 | />
53 | );
54 | }
55 |
56 | render() {
57 | return this.state.editable ?
58 | this.renderEditMode() :
59 | this.renderViewMode();
60 | }
61 | }
62 |
63 | TodoItem.propTypes = {
64 | title: React.PropTypes.string.isRequired,
65 | completed: React.PropTypes.bool.isRequired,
66 | onUpdate: React.PropTypes.func,
67 | onToggle: React.PropTypes.func,
68 | onDelete: React.PropTypes.func
69 | };
70 |
71 | window.App.TodoItem = TodoItem;
72 |
--------------------------------------------------------------------------------
/level-24_react-redux/components/TodoItem.js:
--------------------------------------------------------------------------------
1 | const { InputField } = window.App;
2 |
3 | class TodoItem extends React.Component {
4 | constructor(props, context) {
5 | super(props, context);
6 | this.state = { editable: false };
7 | this.toggleEditMode = this.toggleEditMode.bind(this);
8 | }
9 |
10 | toggleEditMode() {
11 | this.setState({ editable: !this.state.editable });
12 | }
13 |
14 | renderViewMode() {
15 | const {
16 | title,
17 | completed,
18 | onToggle,
19 | onDelete
20 | } = this.props;
21 | return (
22 |
23 | onToggle && onToggle(!completed)}
27 | />
28 | {title}
29 |
30 |
31 | );
32 | }
33 |
34 | renderEditMode() {
35 | const { title, onUpdate } = this.props;
36 | return (
37 | {
43 | if (e.keyCode === 27) {
44 | e.preventDefault();
45 | this.toggleEditMode();
46 | }
47 | }}
48 | onSubmitEditing={(content) => {
49 | onUpdate && onUpdate(content);
50 | this.toggleEditMode();
51 | }}
52 | />
53 | );
54 | }
55 |
56 | render() {
57 | return this.state.editable ?
58 | this.renderEditMode() :
59 | this.renderViewMode();
60 | }
61 | }
62 |
63 | TodoItem.propTypes = {
64 | title: React.PropTypes.string.isRequired,
65 | completed: React.PropTypes.bool.isRequired,
66 | onUpdate: React.PropTypes.func,
67 | onToggle: React.PropTypes.func,
68 | onDelete: React.PropTypes.func
69 | };
70 |
71 | window.App.TodoItem = TodoItem;
72 |
--------------------------------------------------------------------------------
/level-25_immutablejs/components/TodoItem.js:
--------------------------------------------------------------------------------
1 | const { InputField } = window.App;
2 |
3 | class TodoItem extends React.Component {
4 | constructor(props, context) {
5 | super(props, context);
6 | this.state = { editable: false };
7 | this.toggleEditMode = this.toggleEditMode.bind(this);
8 | }
9 |
10 | toggleEditMode() {
11 | this.setState({ editable: !this.state.editable });
12 | }
13 |
14 | renderViewMode() {
15 | const {
16 | title,
17 | completed,
18 | onToggle,
19 | onDelete
20 | } = this.props;
21 | return (
22 |
23 | onToggle && onToggle(!completed)}
27 | />
28 | {title}
29 |
30 |
31 | );
32 | }
33 |
34 | renderEditMode() {
35 | const { title, onUpdate } = this.props;
36 | return (
37 | {
43 | if (e.keyCode === 27) {
44 | e.preventDefault();
45 | this.toggleEditMode();
46 | }
47 | }}
48 | onSubmitEditing={(content) => {
49 | onUpdate && onUpdate(content);
50 | this.toggleEditMode();
51 | }}
52 | />
53 | );
54 | }
55 |
56 | render() {
57 | return this.state.editable ?
58 | this.renderEditMode() :
59 | this.renderViewMode();
60 | }
61 | }
62 |
63 | TodoItem.propTypes = {
64 | title: React.PropTypes.string.isRequired,
65 | completed: React.PropTypes.bool.isRequired,
66 | onUpdate: React.PropTypes.func,
67 | onToggle: React.PropTypes.func,
68 | onDelete: React.PropTypes.func
69 | };
70 |
71 | window.App.TodoItem = TodoItem;
72 |
--------------------------------------------------------------------------------
/level-13_flux-dispatcher/components/TodoItem.js:
--------------------------------------------------------------------------------
1 | const { InputField } = window.App;
2 |
3 | class TodoItem extends React.Component {
4 | constructor(props, context) {
5 | super(props, context);
6 | this.state = { editable: false };
7 | this.toggleEditMode = this.toggleEditMode.bind(this);
8 | }
9 |
10 | toggleEditMode() {
11 | this.setState({ editable: !this.state.editable });
12 | }
13 |
14 | renderViewMode() {
15 | const {
16 | title,
17 | completed,
18 | onToggle,
19 | onDelete
20 | } = this.props;
21 | return (
22 |
23 | onToggle && onToggle(!completed)}
27 | />
28 | {title}
29 |
30 |
31 | );
32 | }
33 |
34 | renderEditMode() {
35 | const { title, onUpdate } = this.props;
36 | return (
37 | {
43 | if (e.keyCode === 27) {
44 | e.preventDefault();
45 | this.toggleEditMode();
46 | }
47 | }}
48 | onSubmitEditing={(content) => {
49 | onUpdate && onUpdate(content);
50 | this.toggleEditMode();
51 | }}
52 | />
53 | );
54 | }
55 |
56 | render() {
57 | return this.state.editable ?
58 | this.renderEditMode() :
59 | this.renderViewMode();
60 | }
61 | }
62 |
63 | TodoItem.propTypes = {
64 | title: React.PropTypes.string.isRequired,
65 | completed: React.PropTypes.bool.isRequired,
66 | onUpdate: React.PropTypes.func,
67 | onToggle: React.PropTypes.func,
68 | onDelete: React.PropTypes.func
69 | };
70 |
71 | window.App.TodoItem = TodoItem;
72 |
--------------------------------------------------------------------------------
/level-17_container-pattern/components/TodoItem.js:
--------------------------------------------------------------------------------
1 | const { InputField } = window.App;
2 |
3 | class TodoItem extends React.Component {
4 | constructor(props, context) {
5 | super(props, context);
6 | this.state = { editable: false };
7 | this.toggleEditMode = this.toggleEditMode.bind(this);
8 | }
9 |
10 | toggleEditMode() {
11 | this.setState({ editable: !this.state.editable });
12 | }
13 |
14 | renderViewMode() {
15 | const {
16 | title,
17 | completed,
18 | onToggle,
19 | onDelete
20 | } = this.props;
21 | return (
22 |
23 | onToggle && onToggle(!completed)}
27 | />
28 | {title}
29 |
30 |
31 | );
32 | }
33 |
34 | renderEditMode() {
35 | const { title, onUpdate } = this.props;
36 | return (
37 | {
43 | if (e.keyCode === 27) {
44 | e.preventDefault();
45 | this.toggleEditMode();
46 | }
47 | }}
48 | onSubmitEditing={(content) => {
49 | onUpdate && onUpdate(content);
50 | this.toggleEditMode();
51 | }}
52 | />
53 | );
54 | }
55 |
56 | render() {
57 | return this.state.editable ?
58 | this.renderEditMode() :
59 | this.renderViewMode();
60 | }
61 | }
62 |
63 | TodoItem.propTypes = {
64 | title: React.PropTypes.string.isRequired,
65 | completed: React.PropTypes.bool.isRequired,
66 | onUpdate: React.PropTypes.func,
67 | onToggle: React.PropTypes.func,
68 | onDelete: React.PropTypes.func
69 | };
70 |
71 | window.App.TodoItem = TodoItem;
72 |
--------------------------------------------------------------------------------
/level-20_redux-reducers/components/TodoItem.js:
--------------------------------------------------------------------------------
1 | const { InputField } = window.App;
2 |
3 | class TodoItem extends React.Component {
4 | constructor(props, context) {
5 | super(props, context);
6 | this.state = { editable: false };
7 | this.toggleEditMode = this.toggleEditMode.bind(this);
8 | }
9 |
10 | toggleEditMode() {
11 | this.setState({ editable: !this.state.editable });
12 | }
13 |
14 | renderViewMode() {
15 | const {
16 | title,
17 | completed,
18 | onToggle,
19 | onDelete
20 | } = this.props;
21 | return (
22 |
23 | onToggle && onToggle(!completed)}
27 | />
28 | {title}
29 |
30 |
31 | );
32 | }
33 |
34 | renderEditMode() {
35 | const { title, onUpdate } = this.props;
36 | return (
37 | {
43 | if (e.keyCode === 27) {
44 | e.preventDefault();
45 | this.toggleEditMode();
46 | }
47 | }}
48 | onSubmitEditing={(content) => {
49 | onUpdate && onUpdate(content);
50 | this.toggleEditMode();
51 | }}
52 | />
53 | );
54 | }
55 |
56 | render() {
57 | return this.state.editable ?
58 | this.renderEditMode() :
59 | this.renderViewMode();
60 | }
61 | }
62 |
63 | TodoItem.propTypes = {
64 | title: React.PropTypes.string.isRequired,
65 | completed: React.PropTypes.bool.isRequired,
66 | onUpdate: React.PropTypes.func,
67 | onToggle: React.PropTypes.func,
68 | onDelete: React.PropTypes.func
69 | };
70 |
71 | window.App.TodoItem = TodoItem;
72 |
--------------------------------------------------------------------------------
/level-23_redux-middlewares/components/TodoItem.js:
--------------------------------------------------------------------------------
1 | const { InputField } = window.App;
2 |
3 | class TodoItem extends React.Component {
4 | constructor(props, context) {
5 | super(props, context);
6 | this.state = { editable: false };
7 | this.toggleEditMode = this.toggleEditMode.bind(this);
8 | }
9 |
10 | toggleEditMode() {
11 | this.setState({ editable: !this.state.editable });
12 | }
13 |
14 | renderViewMode() {
15 | const {
16 | title,
17 | completed,
18 | onToggle,
19 | onDelete
20 | } = this.props;
21 | return (
22 |
23 | onToggle && onToggle(!completed)}
27 | />
28 | {title}
29 |
30 |
31 | );
32 | }
33 |
34 | renderEditMode() {
35 | const { title, onUpdate } = this.props;
36 | return (
37 | {
43 | if (e.keyCode === 27) {
44 | e.preventDefault();
45 | this.toggleEditMode();
46 | }
47 | }}
48 | onSubmitEditing={(content) => {
49 | onUpdate && onUpdate(content);
50 | this.toggleEditMode();
51 | }}
52 | />
53 | );
54 | }
55 |
56 | render() {
57 | return this.state.editable ?
58 | this.renderEditMode() :
59 | this.renderViewMode();
60 | }
61 | }
62 |
63 | TodoItem.propTypes = {
64 | title: React.PropTypes.string.isRequired,
65 | completed: React.PropTypes.bool.isRequired,
66 | onUpdate: React.PropTypes.func,
67 | onToggle: React.PropTypes.func,
68 | onDelete: React.PropTypes.func
69 | };
70 |
71 | window.App.TodoItem = TodoItem;
72 |
--------------------------------------------------------------------------------
/level-16_flux-controller-view/components/TodoItem.js:
--------------------------------------------------------------------------------
1 | const { InputField } = window.App;
2 |
3 | class TodoItem extends React.Component {
4 | constructor(props, context) {
5 | super(props, context);
6 | this.state = { editable: false };
7 | this.toggleEditMode = this.toggleEditMode.bind(this);
8 | }
9 |
10 | toggleEditMode() {
11 | this.setState({ editable: !this.state.editable });
12 | }
13 |
14 | renderViewMode() {
15 | const {
16 | title,
17 | completed,
18 | onToggle,
19 | onDelete
20 | } = this.props;
21 | return (
22 |
23 | onToggle && onToggle(!completed)}
27 | />
28 | {title}
29 |
30 |
31 | );
32 | }
33 |
34 | renderEditMode() {
35 | const { title, onUpdate } = this.props;
36 | return (
37 | {
43 | if (e.keyCode === 27) {
44 | e.preventDefault();
45 | this.toggleEditMode();
46 | }
47 | }}
48 | onSubmitEditing={(content) => {
49 | onUpdate && onUpdate(content);
50 | this.toggleEditMode();
51 | }}
52 | />
53 | );
54 | }
55 |
56 | render() {
57 | return this.state.editable ?
58 | this.renderEditMode() :
59 | this.renderViewMode();
60 | }
61 | }
62 |
63 | TodoItem.propTypes = {
64 | title: React.PropTypes.string.isRequired,
65 | completed: React.PropTypes.bool.isRequired,
66 | onUpdate: React.PropTypes.func,
67 | onToggle: React.PropTypes.func,
68 | onDelete: React.PropTypes.func
69 | };
70 |
71 | window.App.TodoItem = TodoItem;
72 |
--------------------------------------------------------------------------------
/level-18_flux-utils/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
36 |
37 |
--------------------------------------------------------------------------------
/level-18_flux-utils/stores/TodoStore.js:
--------------------------------------------------------------------------------
1 | const { ReduceStore } = FluxUtils;
2 |
3 | const {
4 | ActionTypes,
5 | AppDispatcher
6 | } = window.App;
7 |
8 | const _createTodo = (todos, title) => {
9 | return [
10 | ...todos,
11 | {
12 | id: todos[todos.length - 1].id + 1,
13 | title,
14 | completed: false
15 | }
16 | ];
17 | };
18 |
19 | const _updateTodo = (todos, id, title) => {
20 | const idx = todos.findIndex((todo) => todo.id === id);
21 | if (idx === -1) return todos;
22 |
23 | const newTodos = [ ...todos ];
24 | newTodos[idx] = {
25 | ...todos[idx],
26 | title
27 | };
28 | return newTodos;
29 | };
30 |
31 | const _toggleTodo = (todos, id, completed) => {
32 | const idx = todos.findIndex((todo) => todo.id === id);
33 | if (idx === -1) return todos;
34 |
35 | const newTodos = [ ...todos ];
36 | newTodos[idx] = {
37 | ...todos[idx],
38 | completed
39 | };
40 | return newTodos;
41 | };
42 |
43 | const _deleteTodo = (todos, id) => {
44 | const idx = todos.findIndex((todo) => todo.id === id);
45 | if (idx === -1) return todos;
46 |
47 | const newTodos = [ ...todos ];
48 | newTodos.splice(idx, 1);
49 | return newTodos;
50 | };
51 |
52 | class TodoStore extends ReduceStore {
53 | getInitialState() {
54 | return [];
55 | }
56 |
57 | reduce(state, action) {
58 | switch (action.type) {
59 | case ActionTypes.LOAD_TODOS_SUCCESS:
60 | return action.todos;
61 | case ActionTypes.CREATE_TODO:
62 | return _createTodo(state, action.title);
63 | case ActionTypes.UPDATE_TODO:
64 | return _updateTodo(state, action.id, action.title);
65 | case ActionTypes.TOGGLE_TODO:
66 | return _toggleTodo(state, action.id, action.completed);
67 | case ActionTypes.DELETE_TODO:
68 | return _deleteTodo(state, action.id);
69 | default:
70 | return state;
71 | }
72 | }
73 | }
74 |
75 | window.App.TodoStore = new TodoStore(AppDispatcher);
76 |
--------------------------------------------------------------------------------
/level-19_redux/README.md:
--------------------------------------------------------------------------------
1 | # Level 19. 深入淺出 Redux
2 |
3 | 歡迎來到「24 小時,React 快速入門」系列教學 :mortar_board: Level 19 ~!
4 | > :bowtie::Wish you have a happy learning!
5 |
6 |
7 | ## :checkered_flag: 關卡目標
8 |
9 | 1. 習得心法:
10 | 1. 理解 Redux 是什麼,如何用一句話定義 Redux
11 | 2. 清楚 Redux 和 Flux 的差別
12 | 3. 認識 Redux 的運作流程
13 |
14 |
15 | ## :triangular_flag_on_post: 打通 Redux 的任督二脈
16 |
17 | > :neckbeard::少年,我看你天資聰穎,是萬中選一的前端人,我這有一篇...[祕笈](https://medium.com/p/7b08403c4957),幫你深入淺出 Redux 的武功心法,你斟酌看看 :lollipop:
18 |
19 | 
20 |
21 | 上方是 Redux 架構圖,要確認你是否吸收了 Redux 精髓,你可以詢問自己以下幾個問題:
22 |
23 | 1. ***Redux 是什麼,可以幫助你什麼(試著用一句話定義)***
24 | 2. ***Redux 和 Flux 的差別是什麼***
25 | 3. ***看著圖,簡述 Redux 的運作流程***
26 |
27 | 如果你認為自己的回答還有地方卡卡的,可以
28 |
29 | 1. 再讀一次[祕笈](https://medium.com/p/7b08403c4957)
30 | 2. 閱讀下方其他大神的見解
31 | 3. 直接進入下一關,從 code 學習
32 |
33 | 最後,再次回答上面的問題,驗證所學:)
34 |
35 | > :bowtie::Good Luck! :four_leaf_clover: 也非常歡迎你開 issue 直接詢問我 ^ ^
36 |
37 | ###### 參考連結
38 |
39 | - [An Introduction To Redux](https://www.smashingmagazine.com/2016/06/an-introduction-to-redux/)
40 | - [A Cartoon Intro To Redux](https://code-cartoons.com/a-cartoon-intro-to-redux-3afb775501a6)
41 | - [Redux 初探](https://www.facebook.com/notes/%E9%99%B8%E6%8C%AF%E6%81%A9/redux%E5%88%9D%E6%8E%A2/1025850617451561)
42 | - [Dan Abramov 解釋 Predictable State Container](https://hashnode.com/post/how-do-you-explain-the-term-predictable-state-container-in-simple-words-ciizdac5300wege53dogz8aqk)
43 | - [Dan Abramov’s React Europe 2015 talk](https://www.youtube.com/watch?v=xsSnOQynTHs)
44 | - [Dan Abramov’s React Europe 2016 talk](https://www.youtube.com/watch?v=uvAXVMwHJXU)
45 |
46 |
47 | ## :rocket:
48 |
49 | | [主頁](../../../) | [上一關](../level-18_flux-utils) | [下一關. 完成 Reducers:讓狀態的改變可預測化](../level-20_redux-reducers) |
50 |
51 | | :raising_hand: [我要提問](https://github.com/shiningjason1989/react-quick-tutorial/issues/new) |
52 |
53 |
54 | 
55 |
--------------------------------------------------------------------------------
/level-20_redux-reducers/stores/TodoStore.js:
--------------------------------------------------------------------------------
1 | const { ReduceStore } = FluxUtils;
2 |
3 | const {
4 | ActionTypes,
5 | AppDispatcher
6 | } = window.App;
7 |
8 | const _createTodo = (todos, title) => {
9 | return [
10 | ...todos,
11 | {
12 | id: todos[todos.length - 1].id + 1,
13 | title,
14 | completed: false
15 | }
16 | ];
17 | };
18 |
19 | const _updateTodo = (todos, id, title) => {
20 | const idx = todos.findIndex((todo) => todo.id === id);
21 | if (idx === -1) return todos;
22 |
23 | const newTodos = [ ...todos ];
24 | newTodos[idx] = {
25 | ...todos[idx],
26 | title
27 | };
28 | return newTodos;
29 | };
30 |
31 | const _toggleTodo = (todos, id, completed) => {
32 | const idx = todos.findIndex((todo) => todo.id === id);
33 | if (idx === -1) return todos;
34 |
35 | const newTodos = [ ...todos ];
36 | newTodos[idx] = {
37 | ...todos[idx],
38 | completed
39 | };
40 | return newTodos;
41 | };
42 |
43 | const _deleteTodo = (todos, id) => {
44 | const idx = todos.findIndex((todo) => todo.id === id);
45 | if (idx === -1) return todos;
46 |
47 | const newTodos = [ ...todos ];
48 | newTodos.splice(idx, 1);
49 | return newTodos;
50 | };
51 |
52 | class TodoStore extends ReduceStore {
53 | getInitialState() {
54 | return [];
55 | }
56 |
57 | reduce(state, action) {
58 | switch (action.type) {
59 | case ActionTypes.LOAD_TODOS_SUCCESS:
60 | return action.todos;
61 | case ActionTypes.CREATE_TODO:
62 | return _createTodo(state, action.title);
63 | case ActionTypes.UPDATE_TODO:
64 | return _updateTodo(state, action.id, action.title);
65 | case ActionTypes.TOGGLE_TODO:
66 | return _toggleTodo(state, action.id, action.completed);
67 | case ActionTypes.DELETE_TODO:
68 | return _deleteTodo(state, action.id);
69 | default:
70 | return state;
71 | }
72 | }
73 | }
74 |
75 | window.App.TodoStore = new TodoStore(AppDispatcher);
76 |
--------------------------------------------------------------------------------
/level-21_redux-store/stores/TodoStore.js:
--------------------------------------------------------------------------------
1 | const { ReduceStore } = FluxUtils;
2 |
3 | const {
4 | ActionTypes,
5 | AppDispatcher
6 | } = window.App;
7 |
8 | const _createTodo = (todos, title) => {
9 | return [
10 | ...todos,
11 | {
12 | id: todos[todos.length - 1].id + 1,
13 | title,
14 | completed: false
15 | }
16 | ];
17 | };
18 |
19 | const _updateTodo = (todos, id, title) => {
20 | const idx = todos.findIndex((todo) => todo.id === id);
21 | if (idx === -1) return todos;
22 |
23 | const newTodos = [ ...todos ];
24 | newTodos[idx] = {
25 | ...todos[idx],
26 | title
27 | };
28 | return newTodos;
29 | };
30 |
31 | const _toggleTodo = (todos, id, completed) => {
32 | const idx = todos.findIndex((todo) => todo.id === id);
33 | if (idx === -1) return todos;
34 |
35 | const newTodos = [ ...todos ];
36 | newTodos[idx] = {
37 | ...todos[idx],
38 | completed
39 | };
40 | return newTodos;
41 | };
42 |
43 | const _deleteTodo = (todos, id) => {
44 | const idx = todos.findIndex((todo) => todo.id === id);
45 | if (idx === -1) return todos;
46 |
47 | const newTodos = [ ...todos ];
48 | newTodos.splice(idx, 1);
49 | return newTodos;
50 | };
51 |
52 | class TodoStore extends ReduceStore {
53 | getInitialState() {
54 | return [];
55 | }
56 |
57 | reduce(state, action) {
58 | switch (action.type) {
59 | case ActionTypes.LOAD_TODOS_SUCCESS:
60 | return action.todos;
61 | case ActionTypes.CREATE_TODO:
62 | return _createTodo(state, action.title);
63 | case ActionTypes.UPDATE_TODO:
64 | return _updateTodo(state, action.id, action.title);
65 | case ActionTypes.TOGGLE_TODO:
66 | return _toggleTodo(state, action.id, action.completed);
67 | case ActionTypes.DELETE_TODO:
68 | return _deleteTodo(state, action.id);
69 | default:
70 | return state;
71 | }
72 | }
73 | }
74 |
75 | window.App.TodoStore = new TodoStore(AppDispatcher);
76 |
--------------------------------------------------------------------------------
/level-22_redux-actions/stores/TodoStore.js:
--------------------------------------------------------------------------------
1 | const { ReduceStore } = FluxUtils;
2 |
3 | const {
4 | ActionTypes,
5 | AppDispatcher
6 | } = window.App;
7 |
8 | const _createTodo = (todos, title) => {
9 | return [
10 | ...todos,
11 | {
12 | id: todos[todos.length - 1].id + 1,
13 | title,
14 | completed: false
15 | }
16 | ];
17 | };
18 |
19 | const _updateTodo = (todos, id, title) => {
20 | const idx = todos.findIndex((todo) => todo.id === id);
21 | if (idx === -1) return todos;
22 |
23 | const newTodos = [ ...todos ];
24 | newTodos[idx] = {
25 | ...todos[idx],
26 | title
27 | };
28 | return newTodos;
29 | };
30 |
31 | const _toggleTodo = (todos, id, completed) => {
32 | const idx = todos.findIndex((todo) => todo.id === id);
33 | if (idx === -1) return todos;
34 |
35 | const newTodos = [ ...todos ];
36 | newTodos[idx] = {
37 | ...todos[idx],
38 | completed
39 | };
40 | return newTodos;
41 | };
42 |
43 | const _deleteTodo = (todos, id) => {
44 | const idx = todos.findIndex((todo) => todo.id === id);
45 | if (idx === -1) return todos;
46 |
47 | const newTodos = [ ...todos ];
48 | newTodos.splice(idx, 1);
49 | return newTodos;
50 | };
51 |
52 | class TodoStore extends ReduceStore {
53 | getInitialState() {
54 | return [];
55 | }
56 |
57 | reduce(state, action) {
58 | switch (action.type) {
59 | case ActionTypes.LOAD_TODOS_SUCCESS:
60 | return action.todos;
61 | case ActionTypes.CREATE_TODO:
62 | return _createTodo(state, action.title);
63 | case ActionTypes.UPDATE_TODO:
64 | return _updateTodo(state, action.id, action.title);
65 | case ActionTypes.TOGGLE_TODO:
66 | return _toggleTodo(state, action.id, action.completed);
67 | case ActionTypes.DELETE_TODO:
68 | return _deleteTodo(state, action.id);
69 | default:
70 | return state;
71 | }
72 | }
73 | }
74 |
75 | window.App.TodoStore = new TodoStore(AppDispatcher);
76 |
--------------------------------------------------------------------------------
/level-23_redux-middlewares/stores/TodoStore.js:
--------------------------------------------------------------------------------
1 | const { ReduceStore } = FluxUtils;
2 |
3 | const {
4 | ActionTypes,
5 | AppDispatcher
6 | } = window.App;
7 |
8 | const _createTodo = (todos, title) => {
9 | return [
10 | ...todos,
11 | {
12 | id: todos[todos.length - 1].id + 1,
13 | title,
14 | completed: false
15 | }
16 | ];
17 | };
18 |
19 | const _updateTodo = (todos, id, title) => {
20 | const idx = todos.findIndex((todo) => todo.id === id);
21 | if (idx === -1) return todos;
22 |
23 | const newTodos = [ ...todos ];
24 | newTodos[idx] = {
25 | ...todos[idx],
26 | title
27 | };
28 | return newTodos;
29 | };
30 |
31 | const _toggleTodo = (todos, id, completed) => {
32 | const idx = todos.findIndex((todo) => todo.id === id);
33 | if (idx === -1) return todos;
34 |
35 | const newTodos = [ ...todos ];
36 | newTodos[idx] = {
37 | ...todos[idx],
38 | completed
39 | };
40 | return newTodos;
41 | };
42 |
43 | const _deleteTodo = (todos, id) => {
44 | const idx = todos.findIndex((todo) => todo.id === id);
45 | if (idx === -1) return todos;
46 |
47 | const newTodos = [ ...todos ];
48 | newTodos.splice(idx, 1);
49 | return newTodos;
50 | };
51 |
52 | class TodoStore extends ReduceStore {
53 | getInitialState() {
54 | return [];
55 | }
56 |
57 | reduce(state, action) {
58 | switch (action.type) {
59 | case ActionTypes.LOAD_TODOS_SUCCESS:
60 | return action.todos;
61 | case ActionTypes.CREATE_TODO:
62 | return _createTodo(state, action.title);
63 | case ActionTypes.UPDATE_TODO:
64 | return _updateTodo(state, action.id, action.title);
65 | case ActionTypes.TOGGLE_TODO:
66 | return _toggleTodo(state, action.id, action.completed);
67 | case ActionTypes.DELETE_TODO:
68 | return _deleteTodo(state, action.id);
69 | default:
70 | return state;
71 | }
72 | }
73 | }
74 |
75 | window.App.TodoStore = new TodoStore(AppDispatcher);
76 |
--------------------------------------------------------------------------------
/level-21_redux-store/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/level-22_redux-actions/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/level-23_redux-middlewares/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/level-15_flux-stores/stores/TodoStore.js:
--------------------------------------------------------------------------------
1 | const {
2 | ActionTypes,
3 | AppDispatcher
4 | } = window.App;
5 |
6 | const CHANGE_EVENT = 'CHANGE';
7 |
8 | const _emitter = new EventEmitter();
9 |
10 | let _todos = [];
11 |
12 | const _createTodo = (todos, title) => {
13 | todos.push({
14 | id: todos[todos.length - 1].id + 1,
15 | title,
16 | completed: false
17 | });
18 | return todos;
19 | };
20 |
21 | const _updateTodo = (todos, id, title) => {
22 | const target = todos.find((todo) => todo.id === id);
23 | if (target) target.title = title;
24 | return todos;
25 | };
26 |
27 | const _toggleTodo = (todos, id, completed) => {
28 | const target = todos.find((todo) => todo.id === id);
29 | if (target) target.completed = completed;
30 | return todos;
31 | };
32 |
33 | const _deleteTodo = (todos, id) => {
34 | const idx = todos.findIndex((todo) => todo.id === id);
35 | if (idx !== -1) todos.splice(idx, 1);
36 | return todos;
37 | };
38 |
39 | window.App.TodoStore = {
40 | getAll() {
41 | return _todos;
42 | },
43 | addChangeListener(callback) {
44 | _emitter.on(CHANGE_EVENT, callback);
45 | return () => _emitter.removeListener(CHANGE_EVENT, callback);
46 | },
47 | dispatchToken: AppDispatcher.register((action) => {
48 | switch (action.type) {
49 | case ActionTypes.LOAD_TODOS_SUCCESS:
50 | _todos = action.todos;
51 | _emitter.emit(CHANGE_EVENT);
52 | break;
53 | case ActionTypes.CREATE_TODO:
54 | _todos = _createTodo(_todos, action.title);
55 | _emitter.emit(CHANGE_EVENT);
56 | break;
57 | case ActionTypes.UPDATE_TODO:
58 | _todos = _updateTodo(_todos, action.id, action.title);
59 | _emitter.emit(CHANGE_EVENT);
60 | break;
61 | case ActionTypes.TOGGLE_TODO:
62 | _todos = _toggleTodo(_todos, action.id, action.completed);
63 | _emitter.emit(CHANGE_EVENT);
64 | break;
65 | case ActionTypes.DELETE_TODO:
66 | _todos = _deleteTodo(_todos, action.id);
67 | _emitter.emit(CHANGE_EVENT);
68 | break;
69 | }
70 | })
71 | };
72 |
--------------------------------------------------------------------------------
/level-16_flux-controller-view/stores/TodoStore.js:
--------------------------------------------------------------------------------
1 | const {
2 | ActionTypes,
3 | AppDispatcher
4 | } = window.App;
5 |
6 | const CHANGE_EVENT = 'CHANGE';
7 |
8 | const _emitter = new EventEmitter();
9 |
10 | let _todos = [];
11 |
12 | const _createTodo = (todos, title) => {
13 | todos.push({
14 | id: todos[todos.length - 1].id + 1,
15 | title,
16 | completed: false
17 | });
18 | return todos;
19 | };
20 |
21 | const _updateTodo = (todos, id, title) => {
22 | const target = todos.find((todo) => todo.id === id);
23 | if (target) target.title = title;
24 | return todos;
25 | };
26 |
27 | const _toggleTodo = (todos, id, completed) => {
28 | const target = todos.find((todo) => todo.id === id);
29 | if (target) target.completed = completed;
30 | return todos;
31 | };
32 |
33 | const _deleteTodo = (todos, id) => {
34 | const idx = todos.findIndex((todo) => todo.id === id);
35 | if (idx !== -1) todos.splice(idx, 1);
36 | return todos;
37 | };
38 |
39 | window.App.TodoStore = {
40 | getAll() {
41 | return _todos;
42 | },
43 | addChangeListener(callback) {
44 | _emitter.on(CHANGE_EVENT, callback);
45 | return () => _emitter.removeListener(CHANGE_EVENT, callback);
46 | },
47 | dispatchToken: AppDispatcher.register((action) => {
48 | switch (action.type) {
49 | case ActionTypes.LOAD_TODOS_SUCCESS:
50 | _todos = action.todos;
51 | _emitter.emit(CHANGE_EVENT);
52 | break;
53 | case ActionTypes.CREATE_TODO:
54 | _todos = _createTodo(_todos, action.title);
55 | _emitter.emit(CHANGE_EVENT);
56 | break;
57 | case ActionTypes.UPDATE_TODO:
58 | _todos = _updateTodo(_todos, action.id, action.title);
59 | _emitter.emit(CHANGE_EVENT);
60 | break;
61 | case ActionTypes.TOGGLE_TODO:
62 | _todos = _toggleTodo(_todos, action.id, action.completed);
63 | _emitter.emit(CHANGE_EVENT);
64 | break;
65 | case ActionTypes.DELETE_TODO:
66 | _todos = _deleteTodo(_todos, action.id);
67 | _emitter.emit(CHANGE_EVENT);
68 | break;
69 | }
70 | })
71 | };
72 |
--------------------------------------------------------------------------------
/level-17_container-pattern/stores/TodoStore.js:
--------------------------------------------------------------------------------
1 | const {
2 | ActionTypes,
3 | AppDispatcher
4 | } = window.App;
5 |
6 | const CHANGE_EVENT = 'CHANGE';
7 |
8 | const _emitter = new EventEmitter();
9 |
10 | let _todos = [];
11 |
12 | const _createTodo = (todos, title) => {
13 | todos.push({
14 | id: todos[todos.length - 1].id + 1,
15 | title,
16 | completed: false
17 | });
18 | return todos;
19 | };
20 |
21 | const _updateTodo = (todos, id, title) => {
22 | const target = todos.find((todo) => todo.id === id);
23 | if (target) target.title = title;
24 | return todos;
25 | };
26 |
27 | const _toggleTodo = (todos, id, completed) => {
28 | const target = todos.find((todo) => todo.id === id);
29 | if (target) target.completed = completed;
30 | return todos;
31 | };
32 |
33 | const _deleteTodo = (todos, id) => {
34 | const idx = todos.findIndex((todo) => todo.id === id);
35 | if (idx !== -1) todos.splice(idx, 1);
36 | return todos;
37 | };
38 |
39 | window.App.TodoStore = {
40 | getAll() {
41 | return _todos;
42 | },
43 | addChangeListener(callback) {
44 | _emitter.on(CHANGE_EVENT, callback);
45 | return () => _emitter.removeListener(CHANGE_EVENT, callback);
46 | },
47 | dispatchToken: AppDispatcher.register((action) => {
48 | switch (action.type) {
49 | case ActionTypes.LOAD_TODOS_SUCCESS:
50 | _todos = action.todos;
51 | _emitter.emit(CHANGE_EVENT);
52 | break;
53 | case ActionTypes.CREATE_TODO:
54 | _todos = _createTodo(_todos, action.title);
55 | _emitter.emit(CHANGE_EVENT);
56 | break;
57 | case ActionTypes.UPDATE_TODO:
58 | _todos = _updateTodo(_todos, action.id, action.title);
59 | _emitter.emit(CHANGE_EVENT);
60 | break;
61 | case ActionTypes.TOGGLE_TODO:
62 | _todos = _toggleTodo(_todos, action.id, action.completed);
63 | _emitter.emit(CHANGE_EVENT);
64 | break;
65 | case ActionTypes.DELETE_TODO:
66 | _todos = _deleteTodo(_todos, action.id);
67 | _emitter.emit(CHANGE_EVENT);
68 | break;
69 | }
70 | })
71 | };
72 |
--------------------------------------------------------------------------------
/level-20_redux-reducers/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | TodoApp
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
38 |
39 |
--------------------------------------------------------------------------------
/level-11_component-lifecycle/TodoApp.js:
--------------------------------------------------------------------------------
1 | const {
2 | InputField,
3 | TodoHeader,
4 | TodoList
5 | } = window.App;
6 |
7 | const _createTodo = (todos, title) => {
8 | todos.push({
9 | id: todos[todos.length - 1].id + 1,
10 | title,
11 | completed: false
12 | });
13 | return todos;
14 | };
15 |
16 | const _updateTodo = (todos, id, title) => {
17 | const target = todos.find((todo) => todo.id === id);
18 | if (target) target.title = title;
19 | return todos;
20 | };
21 |
22 | const _toggleTodo = (todos, id, completed) => {
23 | const target = todos.find((todo) => todo.id === id);
24 | if (target) target.completed = completed;
25 | return todos;
26 | };
27 |
28 | const _deleteTodo = (todos, id) => {
29 | const idx = todos.findIndex((todo) => todo.id === id);
30 | if (idx !== -1) todos.splice(idx, 1);
31 | return todos;
32 | };
33 |
34 | class TodoApp extends React.Component {
35 | constructor(props, context) {
36 | super(props, context);
37 | this.state = {
38 | todos: []
39 | };
40 | }
41 |
42 | componentDidMount() {
43 | fetch('./todos.json')
44 | .then((response) => response.json())
45 | .then((todos) => this.setState({ todos }));
46 | }
47 |
48 | updateTodosBy(updateFn) {
49 | return (...args) => {
50 | this.setState({
51 | todos: updateFn(this.state.todos, ...args)
52 | });
53 | };
54 | }
55 |
56 | render() {
57 | const { todos } = this.state;
58 | return (
59 |
60 | !todo.completed).length}
64 | />
65 |
69 |
75 |
76 | );
77 | }
78 | }
79 |
80 | window.App.TodoApp = TodoApp;
81 |
--------------------------------------------------------------------------------
/level-14_flux-actions/components/TodoApp.js:
--------------------------------------------------------------------------------
1 | const {
2 | InputField,
3 | TodoHeader,
4 | TodoList
5 | } = window.App;
6 |
7 | const _createTodo = (todos, title) => {
8 | todos.push({
9 | id: todos[todos.length - 1].id + 1,
10 | title,
11 | completed: false
12 | });
13 | return todos;
14 | };
15 |
16 | const _updateTodo = (todos, id, title) => {
17 | const target = todos.find((todo) => todo.id === id);
18 | if (target) target.title = title;
19 | return todos;
20 | };
21 |
22 | const _toggleTodo = (todos, id, completed) => {
23 | const target = todos.find((todo) => todo.id === id);
24 | if (target) target.completed = completed;
25 | return todos;
26 | };
27 |
28 | const _deleteTodo = (todos, id) => {
29 | const idx = todos.findIndex((todo) => todo.id === id);
30 | if (idx !== -1) todos.splice(idx, 1);
31 | return todos;
32 | };
33 |
34 | class TodoApp extends React.Component {
35 | constructor(props, context) {
36 | super(props, context);
37 | this.state = {
38 | todos: []
39 | };
40 | }
41 |
42 | componentDidMount() {
43 | fetch('./todos.json')
44 | .then((response) => response.json())
45 | .then((todos) => this.setState({ todos }));
46 | }
47 |
48 | updateTodosBy(updateFn) {
49 | return (...args) => {
50 | this.setState({
51 | todos: updateFn(this.state.todos, ...args)
52 | });
53 | };
54 | }
55 |
56 | render() {
57 | const { todos } = this.state;
58 | return (
59 |
60 | !todo.completed).length}
64 | />
65 |
69 |
75 |
76 | );
77 | }
78 | }
79 |
80 | window.App.TodoApp = TodoApp;
81 |
--------------------------------------------------------------------------------
/level-15_flux-stores/components/TodoApp.js:
--------------------------------------------------------------------------------
1 | const {
2 | InputField,
3 | TodoHeader,
4 | TodoList
5 | } = window.App;
6 |
7 | const _createTodo = (todos, title) => {
8 | todos.push({
9 | id: todos[todos.length - 1].id + 1,
10 | title,
11 | completed: false
12 | });
13 | return todos;
14 | };
15 |
16 | const _updateTodo = (todos, id, title) => {
17 | const target = todos.find((todo) => todo.id === id);
18 | if (target) target.title = title;
19 | return todos;
20 | };
21 |
22 | const _toggleTodo = (todos, id, completed) => {
23 | const target = todos.find((todo) => todo.id === id);
24 | if (target) target.completed = completed;
25 | return todos;
26 | };
27 |
28 | const _deleteTodo = (todos, id) => {
29 | const idx = todos.findIndex((todo) => todo.id === id);
30 | if (idx !== -1) todos.splice(idx, 1);
31 | return todos;
32 | };
33 |
34 | class TodoApp extends React.Component {
35 | constructor(props, context) {
36 | super(props, context);
37 | this.state = {
38 | todos: []
39 | };
40 | }
41 |
42 | componentDidMount() {
43 | fetch('./todos.json')
44 | .then((response) => response.json())
45 | .then((todos) => this.setState({ todos }));
46 | }
47 |
48 | updateTodosBy(updateFn) {
49 | return (...args) => {
50 | this.setState({
51 | todos: updateFn(this.state.todos, ...args)
52 | });
53 | };
54 | }
55 |
56 | render() {
57 | const { todos } = this.state;
58 | return (
59 |
60 | !todo.completed).length}
64 | />
65 |
69 |
75 |
76 | );
77 | }
78 | }
79 |
80 | window.App.TodoApp = TodoApp;
81 |
--------------------------------------------------------------------------------
/level-13_flux-dispatcher/components/TodoApp.js:
--------------------------------------------------------------------------------
1 | const {
2 | InputField,
3 | TodoHeader,
4 | TodoList
5 | } = window.App;
6 |
7 | const _createTodo = (todos, title) => {
8 | todos.push({
9 | id: todos[todos.length - 1].id + 1,
10 | title,
11 | completed: false
12 | });
13 | return todos;
14 | };
15 |
16 | const _updateTodo = (todos, id, title) => {
17 | const target = todos.find((todo) => todo.id === id);
18 | if (target) target.title = title;
19 | return todos;
20 | };
21 |
22 | const _toggleTodo = (todos, id, completed) => {
23 | const target = todos.find((todo) => todo.id === id);
24 | if (target) target.completed = completed;
25 | return todos;
26 | };
27 |
28 | const _deleteTodo = (todos, id) => {
29 | const idx = todos.findIndex((todo) => todo.id === id);
30 | if (idx !== -1) todos.splice(idx, 1);
31 | return todos;
32 | };
33 |
34 | class TodoApp extends React.Component {
35 | constructor(props, context) {
36 | super(props, context);
37 | this.state = {
38 | todos: []
39 | };
40 | }
41 |
42 | componentDidMount() {
43 | fetch('./todos.json')
44 | .then((response) => response.json())
45 | .then((todos) => this.setState({ todos }));
46 | }
47 |
48 | updateTodosBy(updateFn) {
49 | return (...args) => {
50 | this.setState({
51 | todos: updateFn(this.state.todos, ...args)
52 | });
53 | };
54 | }
55 |
56 | render() {
57 | const { todos } = this.state;
58 | return (
59 |
60 | !todo.completed).length}
64 | />
65 |
69 |
75 |
76 | );
77 | }
78 | }
79 |
80 | window.App.TodoApp = TodoApp;
81 |
--------------------------------------------------------------------------------