次の上に最小のtodoアプリを作成することによってReduxアーキテクチャを学んでください.js


この記事では、私はちょうど2つの機能(ToDoを追加し、ToDoを削除)を持っているので、シンプルなToDoアプリを作成することによって反応Reduxアーキテクチャを説明します.
これは、ここでの例題のステップガイドです.

saltyshiomix / nextjs-redux-todo-app
次の最小のtodoアプリ.ReduxアーキテクチャにおけるJS

機能
  • 最小であるが井戸構造
  • CSSでない
  • これらのスタックを学ぶことができます.
  • next
  • react

  • redux and react-redux
  • redux-thunk
  • reselect
  • recompose
  • 用途
    # installation
    $ git clone https://github.com/saltyshiomix/nextjs-todo-app.git
    $ cd nextjs-todo-app
    $ yarn (or `npm install`)
    
    # development mode
    $ yarn dev (or `npm run dev`)
    
    # production mode
    $ yarn build (or `npm run build`)
    $ yarn start (or `npm start`)

    The Point of View

    Folder Structures

    NEXT.js Structures

    .
    ├── components
    │   ├── page.tsx
    │   └── todo.tsx
    ├── next-env.d.ts
    ├── pages
    │   ├── _app.tsx
    │   └── index.tsx
    └── tsconfig.json
    

    redux構造
    .
    ├── actions
    │   └── index.ts
    ├── components
    │   ├── page.tsx
    │   └── todo.tsx
    ├── constants
    │   └── actionTypes.ts
    ├── containers
    │   └── page.tsx
    ├── reducers
    │   ├── index.ts
    │   └── todo.ts
    ├── selectors
    │   └── index.ts
    └── store.ts
    

    全体構造
    .
    ├── actions
    │   └── index.ts
    ├── components
    │   ├── page.tsx
    │   └── todo.tsx
    ├── constants
    │   └── actionTypes.ts
    ├── containers
    │   └── page.tsx
    ├── next-env.d.ts
    ├── package.json
    ├── pages
    │   ├── _app.tsx
    │   └── index.tsx
    ├── reducers
    │   ├── index.ts
    │   └── todo.ts
    ├── selectors
    │   └── index.ts
    ├── store.ts
    └── tsconfig.json
    

    ステップ1 :こんにちは
    $ mkdir test-app
    $ cd test-app
    
    その後、人口package.json and pages/index.tsx :
    パッケージ.JSON
    {
      "name": "test-app",
      "scripts": {
        "dev": "next"
      }
    }
    
    ページ/インデックス.TSX
    export default () => <p>Hello World</p>;
    
    次に、以下のコマンドを実行します.
    # install dependencies
    $ npm install --save next react react-dom
    $ npm install --save-dev typescript @types/node @types/react @types/react-dom
    
    # run as development mode
    $ npm run dev
    
    それだ!
    移動するhttp://localhost:3000 そして、あなたはHello World !

    ステップ2:ビルドredux todoアプリ(突然、私は参照してください).
    私はReduxアーキテクチャを説明しない!lol
    ちょっと感じて、国家と視野の分離.

    機能の定義
    アクションタイプのIDを定義しますconstants/actionTypes.ts :
    export const TODO_ONCHANGE = 'TODO_ONCHANGE';
    export const TODO_ADD = 'TODO_ADD';
    export const TODO_DELETE = 'TODO_DELETE';
    
    そしてactions/index.ts , 短縮子にコールバックを定義します.
    (単に引数を定義し、データを返します.
    import {
      TODO_ONCHANGE,
      TODO_ADD,
      TODO_DELETE,
    } from '../constants/actionTypes';
    
    export const onChangeTodo = (item) => ({ type: TODO_ONCHANGE, item });
    
    export const addTodo = (item) => ({ type: TODO_ADD, item });
    
    export const deleteTodo = (item) => ({ type: TODO_DELETE, item });
    

    国家管理(減速者)
    reducers/todo.ts , 初期状態とその処理方法を定義します.
    import {
      TODO_ONCHANGE,
      TODO_ADD,
      TODO_DELETE,
    } from '../constants/actionTypes';
    
    export const initialState = {
      // this is a TODO item which has one "value" property
      item: {
        value: '',
      },
      // this is a list of the TODO items
      data: [],
    };
    
    export default (state = initialState, action) => {
      // receive the type and item, which is defined in the `actions/index.ts`
      const {
        type,
        item,
      } = action;
    
      switch (type) {
        case TODO_ONCHANGE: {
          // BE CAREFUL!!!
          // DON'T USE THE REFERENCE LIKE THIS:
          //
          //     state.item = item;
          //     return state; // this `state` is "previous" state!
          //
          // Please create a new instance because that is a "next" state
          //
          return Object.assign({}, state, {
            item,
          });
        }
    
        case TODO_ADD: {
          // if the `item.value` is empty, return the "previous" state (skip)
          if (item.value === '') {
            return state;
          }
    
          return Object.assign({}, state, {
            // clear the `item.value`
            item: {
              value: '',
            },
            // create a new array instance and push the item
            data: [
              ...(state.data),
              item,
            ],
          });
        }
    
        case TODO_DELETE: {
          // don't use `state.data` directly
          const { data, ...restState } = state;
    
          // `[...data]` means a new instance of the `data` array
          // and filter them and remove the target TODO item
          const updated = [...data].filter(_item => _item.value !== item.value);
    
          return Object.assign({}, restState, {
            data: updated,
          });
        }
    
        // do nothing
        default: {
          return state;
        }
      }
    };
    
    次に定義するreducers/index.ts すべての縮小子を組み合わせたものです.
    (現在は1つの減速機のみ)
    import { combineReducers } from 'redux';
    import todo, { initialState as todoState } from './todo';
    
    export const initialState = {
      todo: todoState,
    };
    
    export default combineReducers({
      todo,
    });
    

    店を作る
    私たちは店からどんな州にでもアクセスできるように1つの店を定義します.
    そして、次のページで次のページを開きます.jspages/_app.tsx 最良の選択肢の一つです.
    ストア.TS
    import thunkMiddleware from 'redux-thunk';
    import {
      createStore,
      applyMiddleware,
      compose,
      Store as ReduxStore,
    } from 'redux';
    import { createLogger } from 'redux-logger';
    import reducers, { initialState } from './reducers';
    
    const dev: boolean = process.env.NODE_ENV !== 'production';
    
    export type Store = ReduxStore<typeof initialState>;
    
    export default (state = initialState): Store => {
      const middlewares = dev ? [thunkMiddleware, createLogger()] : [];
      return createStore(reducers, state, compose(applyMiddleware(...middlewares)));
    };
    
    ページ/アプリ.TSX
    import { NextPageContext } from 'next';
    import App from 'next/app';
    import withRedux from 'next-redux-wrapper';
    import { Provider } from 'react-redux';
    import store, { Store } from '../store';
    
    interface AppContext extends NextPageContext {
      store: Store;
    }
    
    class MyApp extends App<AppContext> {
      render() {
        const { store, Component, ...props } = this.props;
        return (
          <Provider store={store}>
            <Component {...props} />
          </Provider>
        );
      }
    }
    
    export default withRedux(store)(MyApp);
    

    ページを作成する
    まず、深いネスト状態を避けるためにセレクタを定義します.
    import { createSelector } from 'reselect';
    
    export const selectState = () => state => state.todo;
    
    export const selectTodoItem = () =>
      createSelector(
        selectState(),
        todo => todo.item,
      );
    
    export const selectTodoData = () =>
      createSelector(
        selectState(),
        todo => todo.data,
      );
    
    第二に、そのセレクタを使用して、アクションでコンテナに渡します.
    コンテナ/ページ.TS
    import { connect } from 'react-redux';
    import { createSelector } from 'reselect';
    import {
      compose,
      pure,
    } from 'recompose';
    import {
      onChangeTodo,
      addTodo,
      deleteTodo,
    } from '../actions';
    import {
      selectTodoItem,
      selectTodoData,
    } from '../selectors';
    import Page from '../components/page';
    
    export default compose(
      connect(
        createSelector(
          selectTodoItem(),
          selectTodoData(),
          (item, data) => ({ item, data }),
        ),
        {
          onChangeTodo,
          addTodo,
          deleteTodo,
        },
      ),
      pure,
    )(Page);
    
    ページコンポーネントを実装する
    コンポーネント/ページ.TSX
    import React from 'react';
    import { compose } from 'recompose';
    import Todo from './todo';
    
    const Page = (props) => {
      // defined in the `containers/page.ts`, so the `props` is like this:
      //
      // const {
      //   item,
      //   data,
      //   onChangeTodo,
      //   addTodo,
      //   deleteTodo,
      // } = props;
      //
      return <Todo {...props} />;
    };
    
    export default compose()(Page);
    
    コンポーネント/todoを実装します.TSX :
    import React from 'react';
    import { compose } from 'recompose';
    
    const Todo= (props) => {
      const {
        item,
        data,
        onChangeTodo,
        addTodo,
        deleteTodo,
      } = props;
    
      return (
        <React.Fragment>
          <h1>TODO</h1>
          <form onSubmit={(e) => {
            e.preventDefault();
            addTodo({
              value: item.value,
            });
          }}>
            <input
              type="text"
              value={item.value}
              onChange={e => onChangeTodo({
                value: e.target.value,
              })}
            />
            <br />
            <input
              type="submit"
              value="SUBMIT"
              style={{
                display: 'none',
              }}
            />
          </form>
          <hr />
          {data.map((item, index) => (
            <p key={index}>
              {item.value}
              {' '}
              <button onClick={() => deleteTodo(item)}>
                DELETE
              </button>
            </p>
          ))}
        </React.Fragment>
      );
    };
    
    export default compose()(Todo);
    

    書き換えpages/index.tsx最後に更新pages/index.tsx このように:
    import {
      NextPageContext,
      NextComponentType,
    } from 'next';
    import { compose } from 'recompose';
    import { connect } from 'react-redux';
    import Page from '../containers/page';
    import { addTodo } from '../actions';
    import { Store } from '../store';
    
    interface IndexPageContext extends NextPageContext {
      store: Store;
    }
    
    const IndexPage: NextComponentType<IndexPageContext> = compose()(Page);
    
    IndexPage.getInitialProps = ({ store, req }) => {
      const isServer: boolean = !!req;
    
      // we can add any custom data here
      const { todo } = store.getState();
      store.dispatch(addTodo(Object.assign(todo.item, {
        value: 'Hello World!',
      })));
    
      return {
        isServer,
      };
    }
    
    export default connect()(IndexPage);
    

    TODO_ONCHANGE :

    TODO_ADD :

    TODO_DELETE :


    結論

    Practice makes perfect.


    あなたの読書をありがとう!