[モダン反応]6枚道徳的


1.reduceで使用するキーワード


アクション


状態に変化が必要な場合、動作が発生します.次の形式で構成されたオブジェクトとして表示されます.
{
  type: "TOGGLE_VALUE"
}
アクションオブジェクトにはtypeフィールドが必要で、その他の値は開発者が勝手に入力できます.
{
  type: "ADD_TODO",
  data: {
    id: 0,
    text: "리덕스 배우기"
  }
}
{
  type: "CHANGE_INPUT",
  text: "안녕하세요"
}

アクション作成


アクション生成関数は、アクションを作成する関数です.単純にパラメータを取得し,動作対象とする.
export function addTodo(data) {
  return {
    type: "ADD_TODO",
    data
  };
}

// 화살표 함수로도 만들 수 있습니다.
export const changeInput = text => ({ 
  type: "CHANGE_INPUT",
  text
});
動作生成関数を作成して使用するのは、後で構成部品で動作を容易にするためです.通常、関数の前にexportキーを付けて、他のファイルからロードして使用します.
ガイドを使用する場合は、アクション生成関数を使用する必要はありません.また、アクションが発生するたびに直接アクションオブジェクトを作成することもできます.

Reducer


reducerは状態変化を引き起こす関数である.李ヨセフは2つのパラメータを受け入れた.
function reducer(state, action) {
  // 상태 업데이트 로직
  return alteredState;
}
Reduserは、現在の状態と受信した動作によって新しい状態を作成し、返します.このリーダはuserreducerを使用する場合に作成されるリーダと同じ形式を有する.
// 예시

function counter(state, action) {
  switch (action.type) {
    case 'INCREASE':
      return state + 1;
    case 'DECREASE':
      return state - 1;
    default:
      return state;
  }
}
useReducerでは、default:の部分で処理されてエラーが発生するのが一般的であり、throw new Error('Unhandled Action')のように、リドスのreducerでは、既存のstateをそのまま返すように書くべきである.(エラー処理)
reduceを使用すると、複数のreducerを作成し、rootreducerにマージできます.ルトリー・ドゥーサーアンの李・ドゥーサーはスティーブ・ドゥーサーと呼ばれています.

ストア


reduceでは、各アプリケーションにショップが作成されます.ショップには、現在のアプリケーションのステータスとリダイレクト、追加の組み込み関数があります.

派遣する


dispatchはショップの内蔵関数の一つです.dispatchはトリガ動作であることが理解できる.dispatch関数は動作をパラメータとして渡します.dispatch(action)このように呼び出されると、ショップはリカバリプログラム関数を実行し、アクションを処理する論理がある場合、ショップはアクションに基づいて新しいステータスを作成します.

購読(購読)


サブスクリプションもショップの内蔵関数の一つです.subscribe関数は関数形式の値をパラメータとして受け入れる.特定の関数をsubscribe関数に渡すと、動作が割り当てられるたびに渡された関数が呼び出されます.
反応器に反応器を使用する場合、通常、この関数を直接使用することは少ない.代わりに、react-dredoxというライブラリが提供するconnect関数またはuseSelector Hookを使用して、リカバリポイントのステータスを購読します.

2.李徳思の3つのルール


1.アプリケーションにショップがあります。


1つのアプリケーションでは、1つの店しか使用できません.更新が頻繁すぎる場合や、アプリケーションの一部が完全に分離されている場合など、複数のショップを作成することができます.しかしそれでは開発ツールは利用できません.

2.ステータスは読み取り専用です。


リアクターでは、stateを更新する必要がある場合はsetStateを使用します.配列を更新する必要がある場合は、配列自体に直接プッシュしません.concatなどの関数を使用して、既存の配列を変更するのではなく、新しい配列を作成し、代わりに更新します.非常に深い構造を持つオブジェクトを更新する場合は、既存のオブジェクトに触れずにObject.assignを使用するか、拡張演算子(...)を使用して更新します.
道徳的にもそうです.既存のステータスに影響を及ぼさずに新しいステータスを作成および更新する場合は、後で開発者ツールでロールバックするか、ロールバックすることができます.
Ridexで不変性を維持する必要があるのは、内部データの変化を検出するためにshallow equality検査が行われたからである.このように、オブジェクトの変化が検出されると、オブジェクトを深く内部比較する必要がなく、表面を舐めることで比較することで、良好な性能を維持することができる.

3.変化を引き起こす関数、reducerは純粋な関数でなければなりません。

  • reducer関数は、以前の状態と動作オブジェクトをパラメータとして受け入れます.
  • までの状態は絶対に触れず、変化を引き起こす新しい状態オブジェクトを作成して返します.
  • 同じパラメータを呼び出すreducer関数は、常に同じ結果値を返さなければならない.
  • と同じ入力の場合は、常に同じ出力が必要です.
  • の新しいDate()、ランダムな数値生成、ネットワーク要求などの->非純粋なタスク->reducer関数の外で処理する必要があります.
  • Ridexミドルウェア
  • を使用

    3.Ridexの使用準備


    復帰ポイント内の状態は、動作の派遣に伴って更新されます.

    exercise.js

    import { createStore } from 'redux';
    
    // createStore는 스토어를 만들어주는 함수입니다.
    // 리액트 프로젝트에서는 단 하나의 스토어를 만듭니다.
    
    /* 리덕스에서 관리 할 상태 정의 */
    const initialState = {
      counter: 0,
      text: '',
      list: []
    };
    
    /* 액션 타입 정의 */
    // 액션 타입은 주로 대문자로 작성합니다.
    const INCREASE = 'INCREASE';
    const DECREASE = 'DECREASE';
    const CHANGE_TEXT = 'CHANGE_TEXT';
    const ADD_TO_LIST = 'ADD_TO_LIST';
    
    /* 액션 생성함수 정의 */
    // 액션 생성함수는 주로 camelCase 로 작성합니다.
    function increase() {
      return {
        type: INCREASE // 액션 객체에는 type 값이 필수입니다.
      };
    }
    
    // 화살표 함수로 작성하는 것이 더욱 코드가 간단하기에,
    // 이렇게 쓰는 것을 추천합니다.
    const decrease = () => ({
      type: DECREASE
    });
    
    const changeText = text => ({
      type: CHANGE_TEXT,
      text // 액션안에는 type 외에 추가적인 필드를 마음대로 넣을 수 있습니다.
    });
    
    const addToList = item => ({
      type: ADD_TO_LIST,
      item
    });
    
    /* 리듀서 만들기 */
    // 위 액션 생성함수들을 통해 만들어진 객체들을 참조하여
    // 새로운 상태를 만드는 함수를 만들어봅시다.
    // 주의: 리듀서에서는 불변성을 꼭 지켜줘야 합니다!
    
    function reducer(state = initialState, action) {
      // state 의 초깃값을 initialState 로 지정했습니다.
      switch (action.type) {
        case INCREASE:
          return {
            ...state,
            counter: state.counter + 1
          };
        case DECREASE:
          return {
            ...state,
            counter: state.counter - 1
          };
        case CHANGE_TEXT:
          return {
            ...state,
            text: action.text
          };
        case ADD_TO_LIST:
          return {
            ...state,
            list: state.list.concat(action.item)
          };
        default:
          return state;
      }
    }
    
    /* 스토어 만들기 */
    const store = createStore(reducer);
    
    console.log(store.getState()); // 현재 store 안에 들어있는 상태를 조회합니다.
    
    // 스토어안에 들어있는 상태가 바뀔 때 마다 호출되는 listener 함수
    const listener = () => {
      const state = store.getState();
      console.log(state);
    };
    
    const unsubscribe = store.subscribe(listener);
    // 구독을 해제하고 싶을 때는 unsubscribe() 를 호출하면 됩니다.
    
    // 액션들을 디스패치 해봅시다.
    store.dispatch(increase());
    store.dispatch(decrease());
    store.dispatch(changeText('안녕하세요'));
    store.dispatch(addToList({ id: 1, text: '와우' }));

    index.js

    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    import App from './App';
    import * as serviceWorker from './serviceWorker';
    import './exercise'
    
    ReactDOM.render(<App />, document.getElementById('root'));
    
    // If you want your app to work offline and load faster, you can change
    // unregister() to register() below. Note this comes with some pitfalls.
    // Learn more about service workers: https://bit.ly/CRA-PWA
    serviceWorker.unregister();

    4.冗長モジュールの作成


    reduceモジュールは、以下のすべての項目を含むJavaScriptファイルを表します.
  • 動作タイプ
  • 動作作成関数
  • 李杜瑟
  • アクション映画と再放送を分ける必要はありません.Reduserと動作関連コードを1つのファイルにまとめて作成し、Ducksパターンと呼ぶ.

    カウンタモジュールの作成


    modules/counter.js

    /* 액션 타입 만들기 */
    // Ducks 패턴을 따를땐 액션의 이름에 접두사를 넣어주세요.
    // 이렇게 하면 다른 모듈과 액션 이름이 중복되는 것을 방지 할 수 있습니다.
    const SET_DIFF = 'counter/SET_DIFF';
    const INCREASE = 'counter/INCREASE';
    const DECREASE = 'counter/DECREASE';
    
    /* 액션 생성함수 만들기 */
    // 액션 생성함수를 만들고 export 키워드를 사용해서 내보내주세요.
    export const setDiff = diff => ({ type: SET_DIFF, diff });
    export const increase = () => ({ type: INCREASE });
    export const decrease = () => ({ type: DECREASE });
    
    /* 초기 상태 선언 */
    const initialState = {
      number: 0,
      diff: 1
    };
    
    /* 리듀서 선언 */
    // 리듀서는 export default 로 내보내주세요.
    export default function counter(state = initialState, action) {
      switch (action.type) {
        case SET_DIFF:
          return {
            ...state,
            diff: action.diff
          };
        case INCREASE:
          return {
            ...state,
            number: state.number + state.diff
          };
        case DECREASE:
          return {
            ...state,
            number: state.number - state.diff
          };
        default:
          return state;
      }
    }

    todosモジュールの作成


    modules/todos.js

    /* 액션 타입 선언 */
    const ADD_TODO = 'todos/ADD_TODO';
    const TOGGLE_TODO = 'todos/TOGGLE_TODO';
    
    /* 액션 생성함수 선언 */
    let nextId = 1; // todo 데이터에서 사용 할 고유 id
    export const addTodo = text => ({
      type: ADD_TODO,
      todo: {
        id: nextId++, // 새 항목을 추가하고 nextId 값에 1을 더해줍니다.
        text
      }
    });
    export const toggleTodo = id => ({
      type: TOGGLE_TODO,
      id
    });
    
    /* 초기 상태 선언 */
    // 리듀서의 초기 상태는 꼭 객체타입일 필요 없습니다.
    // 배열이여도 되고, 원시 타입 (숫자, 문자열, 불리언 이여도 상관 없습니다.
    const initialState = [
      /* 우리는 다음과 같이 구성된 객체를 이 배열 안에 넣을 것입니다.
      {
        id: 1,
        text: '예시',
        done: false
      } 
      */
    ];
    
    export default function todos(state = initialState, action) {
      switch (action.type) {
        case ADD_TODO:
          return state.concat(action.todo);
        case TOGGLE_TODO:
          return state.map(
            todo =>
              todo.id === action.id // id 가 일치하면
                ? { ...todo, done: !todo.done } // done 값을 반전시키고
                : todo // 아니라면 그대로 둠
          );
        default:
          return state;
      }
    }

    スーパーユーザーの作成


    1つのプロジェクトに複数のディーラーがある場合は、1つのディーラーに統合して使用します.マージされたreducerをrootreducerと呼びます.統合リスナーの操作は、リスナーに組み込まれたコンビネーションリスナーという関数を使用します.

    modules/index.js

    import { combineReducers } from 'redux';
    import counter from './counter';
    import todos from './todos';
    
    const rootReducer = combineReducers({
      counter,
      todos
    });
    
    export default rootReducer;
    ReduserをRoot Reduserに統合したら、ショップを作りましょう.

    index.js


    counter、todos subriduserの状態をマージ!
    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    import App from './App';
    import * as serviceWorker from './serviceWorker';
    import { createStore } from 'redux';
    import rootReducer from './modules';
    
    const store = createStore(rootReducer); // 스토어를 만듭니다.
    
    ReactDOM.render(<App />, document.getElementById('root'));
    
    serviceWorker.unregister();

    応答項目への減益の適用


    index.jsでは、Appコンポーネントを囲むためにProviderというコンポーネントがロードされます.そしてstoreをProviderのアイテムに入れます.これで、どの構成部品がレンダリングされていても、リカバリストアにアクセスできます.
    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    import App from './App';
    import * as serviceWorker from './serviceWorker';
    import { createStore } from 'redux';
    import { Provider } from 'react-redux';
    import rootReducer from './modules';
    
    const store = createStore(rootReducer); // 스토어를 만듭니다.
    
    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById('root')
    );
    
    serviceWorker.unregister();

    5.実施カウンタ


    プレゼンテーション構成部品の作成


    Presentation素子とは,レプリケーションストアに直接アクセスせずに,propsとして必要な値や関数のみを取得するために使用される素子である.プレゼンテーションコンポーネントでは、宣言UIに重点を置き、必要な値または関数をpropsとして取得して使用します.

    Counter.js

    import React from 'react';
    
    function Counter({ number, diff, onIncrease, onDecrease, onSetDiff }) {
      const onChange = e => {
        // e.target.value 의 타입은 문자열이기 때문에 숫자로 변환해주어야 합니다.
        onSetDiff(parseInt(e.target.value, 10));
      };
      return (
        <div>
          <h1>{number}</h1>
          <div>
            <input type="number" value={diff} min="1" onChange={onChange} />
            <button onClick={onIncrease}>+</button>
            <button onClick={onDecrease}>-</button>
          </div>
        </div>
      );
    }
    
    export default Counter;

    app.js

    import CounterContainer from './containers/CounterContainer';
    
    function App() {
      return (
        <div>
          <CounterContainer />
        </div>
      );
    }
    
    export default App;

    コンテナ構成部品の作成


    コンテナ構成部品とは、リカバリポイントのステータスを照会したり、動作を派遣したりできる構成部品です.また、HTMLラベルではなく、他のプレゼンテーションコンポーネントを読み込んで使用します.

    CounterContainer.js

    import React from 'react';
    import { useSelector, useDispatch } from 'react-redux';
    import Counter from '../components/Counter';
    import { increase, decrease, setDiff } from '../modules/counter';
    
    function CounterContainer() {
      // useSelector는 리덕스 스토어의 상태를 조회하는 Hook입니다.
      // state의 값은 store.getState() 함수를 호출했을 때 나타나는 결과물과 동일합니다.
      const { number, diff } = useSelector(state => ({
        number: state.counter.number,
        diff: state.counter.diff
      }));
    
      // useDispatch 는 리덕스 스토어의 dispatch 를 함수에서 사용 할 수 있게 해주는 Hook 입니다.
      const dispatch = useDispatch();
      // 각 액션들을 디스패치하는 함수들을 만드세요
      const onIncrease = () => dispatch(increase());
      const onDecrease = () => dispatch(decrease());
      const onSetDiff = diff => dispatch(setDiff(diff));
    
      return (
        <Counter
          // 상태와
          number={number}
          diff={diff}
          // 액션을 디스패치 하는 함수들을 props로 넣어줍니다.
          onIncrease={onIncrease}
          onDecrease={onDecrease}
          onSetDiff={onSetDiff}
        />
      );
    }
    
    export default CounterContainer;
    import React from 'react';
    import CounterContainer from './containers/CounterContainer';
    
    function App() {
      return (
        <div>
          <CounterContainer />
        </div>
      );
    }
    
    export default App;
    Presentationエレメントとコンテナエレメントを区切り文字の使用に設定するのが慣例ですが、必ずしもそうする必要はありません.既定では、プレゼンテーション/コンテナ構成部品は、ディレクトリ内で個別に分割されることなく別々に作成されます.

    6.Ridex開発者ツールの適用


    Reduce Developerツールを使用すると、開発者ツールで現在のショップのステータスを表示したり、これまでにdispatchされたアクションと、これらのアクションのステータスがアクションによってどのように変化するかを表示できます.また、直接dispatch動作も可能です.

    index.変更

    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    import App from './App';
    import * as serviceWorker from './serviceWorker';
    import { createStore } from 'redux';
    import { Provider } from 'react-redux';
    import rootReducer from './modules';
    import { composeWithDevTools } from 'redux-devtools-extension'; // 리덕스 개발자 도구
    
    const store = createStore(rootReducer, composeWithDevTools()); // 스토어를 만듭니다.
    // composeWithDevTools 를 사용하여 리덕스 개발자 도구 활성화
    
    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
      document.getElementById('root')
    );
    
    
    serviceWorker.unregister();

    7.待機事項リストの実施


    プレゼンテーション構成部品の実装


    components/Todos.js

    import React, { useState } from 'react';
    
    // 컴포넌트 최적화를 위하여 React.memo를 사용합니다
    const TodoItem = React.memo(function TodoItem({ todo, onToggle }) {
      return (
        <li
          style={{ textDecoration: todo.done ? 'line-through' : 'none' }}
          onClick={() => onToggle(todo.id)}
        >
          {todo.text}
        </li>
      );
    });
    
    // 컴포넌트 최적화를 위하여 React.memo를 사용합니다
    const TodoList = React.memo(function TodoList({ todos, onToggle }) {
      return (
        <ul>
          {todos.map(todo => (
            <TodoItem key={todo.id} todo={todo} onToggle={onToggle} />
          ))}
        </ul>
      );
    });
    
    function Todos({ todos, onCreate, onToggle }) {
      // 리덕스를 사용한다고 해서 모든 상태를 리덕스에서 관리해야하는 것은 아닙니다.
      const [text, setText] = useState('');
      const onChange = e => setText(e.target.value);
      const onSubmit = e => {
        e.preventDefault(); // Submit 이벤트 발생했을 때 새로고침 방지
        onCreate(text);
        setText(''); // 인풋 초기화
      };
    
      return (
        <div>
          <form onSubmit={onSubmit}>
            <input
              value={text}
              placeholder="할 일을 입력하세요.."
              onChange={onChange}
            />
            <button type="submit">등록</button>
          </form>
          <TodoList todos={todos} onToggle={onToggle} />
        </div>
      );
    }
    
    export default Todos;

    コンテナ構成部品の作成

    import React, { useCallback } from 'react';
    import { useSelector, useDispatch } from 'react-redux';
    import Todos from '../components/Todos';
    import { addTodo, toggleTodo } from '../modules/todos';
    
    function TodosContainer() {
      // useSelector 에서 꼭 객체를 반환 할 필요는 없습니다.
      // 한 종류의 값만 조회하고 싶으면 그냥 원하는 값만 바로 반환하면 됩니다.
      const todos = useSelector(state => state.todos);
      const dispatch = useDispatch();
    
      const onCreate = text => dispatch(addTodo(text));
      const onToggle = useCallback(id => dispatch(toggleTodo(id)), [dispatch]); // 최적화를 위해 useCallback 사용
    
      return <Todos todos={todos} onCreate={onCreate} onToggle={onToggle} />;
    }
    
    export default TodosContainer;

    App.js

    import React from 'react';
    import CounterContainer from './containers/CounterContainer';
    import TodosContainer from './containers/TodosContainer';
    
    function App() {
      return (
        <div>
          <CounterContainer />
          <hr />
          <TodosContainer />
        </div>
      );
    }
    
    export default App;

    8.ユーザー・セレクタの最適化


    反応(コンテナ)構成部品の反応状態を確認し、最適化のために使用するときに考慮すべき事項を理解します.(最適化は、コンテナ構成部品が必要な場合にのみ再レンダリングされます)

    1.ユーザー・セレクタを複数回使用します。


    値が変更された場合にのみ、構成部品が再ロードされます.
    const number = useSelector(state => state.counter.number);
    const diff = useSelector(state => state.counter.diff);

    2.react-reduxの浅いEqual関数をユーザーセレクタの2番目のパラメータに渡します。


    userSelectorの2番目のパラメータはequalityFnです.
    equalityFn?: (left: any, right: any) => boolean
    前の値を次の値と比較し、trueの場合は再ロードしません.falseの場合は再ロードします.
    import React from 'react';
    import { useSelector, useDispatch, shallowEqual } from 'react-redux';
    import Counter from '../components/Counter';
    import { increase, decrease, setDiff } from '../modules/counter';
    
    function CounterContainer() {
      // useSelector는 리덕스 스토어의 상태를 조회하는 Hook입니다.
      // state의 값은 store.getState() 함수를 호출했을 때 나타나는 결과물과 동일합니다.
      const { number, diff } = useSelector(
        state => ({
          number: state.counter.number,
          diff: state.counter.diff
        }),
        shallowEqual // <-
      );
    
      (...)
    浅いEqualはreact-reducxに組み込まれた関数で、オブジェクトの最も外側の値を比較します.
    // 가장 겉에 있는 값: object.a, object.b, object.c
    
    const object = {
      a: {
        x: 3,
        y: 2,
        z: 1
      },
      b: 1,
      c: [{ id: 1 }]
    }