Xi-Yuer

Xi-Yuer

github

リアクト

React:UI に特化した JavaScript フロントエンドライブラリ#

Redux#

redux は、アプリ全体のコンポーネントの共有状態とデータを集中管理するための状態管理ライブラリです。

store#

import { createStore } from 'redux'

const init = {
    value:'defaultValue'
}

const store = createStore((state=init,action) => {
    const { type, data } = action
    switch(type){
        case 'add':
            return {...state,num:data}
        default:
            return state
    }
})
export default store

アクションをディスパッチする#

import store from './store'

const actionCreator = (data) => {
	return {
		type:'add',
		data
	}
}

btnClick = () => {
    const action = actionCreator(233)
	store.dispatch(action)
}

store の値を取得する#

import store from './store'

class Home extends React.component{
    componentDidMount(() => {
        this.unSubscribe = store.subscribe(() => {
            // storeの状態変化を監視してビューを更新
            this.setState({})
        })
    })
    componentWillUnmount(() => {
        // 監視を解除
        this.unSubscribe()
    })
    render(){
        return (
        	<>
            <h2> { store.getState().num }</h2>
            </>
        )
    }
}

react-redux#

react-redux は、コンポーネント内でアクションをより簡単にディスパッチし、store 内のデータを取得できるようにし、データの更新が自動的にコンポーネントを再レンダリングします。

  • Provider は、最外層のルートコンポーネントを包み、その子コンポーネントが状態を共有できるようにするコンポーネントです。
import React from 'react';
import ReactDOM from 'react-dom';

import { Provider } from 'react-redux';
import store from './store';

import App from './App';
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>
);
  • 関数型コンポーネントでアクションを送信し、store 内の値を取得する
import React, { memo } from 'react';
import { useDispatch, useSelector } from 'react-redux';

export default memo(function Text() {
  const dispatch = useDispatch();
  const num = useSelector(state => state.num); // 値を取得

  const btnClick = () => {
    // アクションを送信
    dispatch({
      type: 'add',
      num: 10,
    });
  };
  return (
    <div>
      <h2></h2>
      <button onClick={e => btnClick()}>アクションを送信</button>
    </div>
  );
});
  • クラスコンポーネントで store を取得し、アクションを発生させる ==> connect

    connect は、コンポーネントと store を関連付けてデータを共有できるようにします。

import React from 'react'
import { connect } from 'react-redux'

class Home extends React.component {
    const btnClick = () => {
        this.props.sendAction()
    }
    <>
       <button onClick={this.btnClick}>ディスパッチ</button>
    </>
}
export connect(
    // パラメータ1:reduxのstate; パラメータ2:コンポーネント自身のprops
    // 戻り値:オブジェクト、これをコンポーネントのpropsに渡します。
    (state,ownProps)=>{ // 受け取る

    },
    // パラメータ1:dispatch関数、アクションをディスパッチするためのもの;パラメータ2:自身のprops
    // 戻り値:オブジェクト、これをHomeコンポーネントのpropsに渡します。
    (dispatch,ownProps)=>{ // 送信
        return {
            sendAction:() => {
                // dispatchを利用してアクションを送信
                dispatch({
                    type:'add',
                    num:1
                })
            }
        }
    }
	)(Home)

コードを改善する#

  • store
import { createStore } from 'redux';

const init = {
  value: 'デフォルト値',
  num: 0,
};

function reducer(state = init, action) {
  const { type, data } = action;
  switch (type) {
    case 'add':
      return { ...state, num: data };
    case 'sub':
      return { ...state, num: data };
    default:
      return state;
  }
}

export default createStore(reducer);
  • ルートコンポーネント
import React, { PureComponent } from 'react';
import { Provider } from 'react-redux';

import About from './pages/About';
import Home from './pages/Home';
import store from './store';

export default class App extends PureComponent {
  render() {
    return (
      <Provider store={store}>
        <Home ownProps={'ヒヒヒ'} />
        <hr />
        <About />
      </Provider>
    );
  }
}
  • クラスコンポーネント
import React, { PureComponent } from 'react';
import { connect } from 'react-redux';

class Home extends PureComponent {
  btnClick = () => {
    this.props.addAction();
  };
  render() {
    return (
      <div>
        <h2>Home:クラスコンポーネント</h2>
        <button onClick={this.btnClick}>+</button>
        <h2>現在のカウント:{this.props.num}</h2>
      </div>
    );
  }
}
const mapStateToProps = (state, ownProps) => {
  return state;
};
const mapDispatchToProps = (dispatch, ownProps) => {
  let num = 0;
  return {
    addAction: () => {
      dispatch({
        type: 'add',
        data: ++num,
      });
    },
  };
};
export default connect(mapStateToProps, mapDispatchToProps)(Home);
  • 関数型コンポーネント
import React, { memo } from 'react';
import { useDispatch, useSelector } from 'react-redux';

export default memo(function About() {
  const dispatch = useDispatch();
  const num = useSelector(state => state.num);

  const btnClick = () => {
    let temNum = num;
    dispatch({
      type: 'sub',
      data: --temNum,
    });
  };
  return (
    <div>
      <h2>About:関数型コンポーネント</h2>
      <button onClick={btnClick}>-</button>
      <h2>現在のカウント{num}</h2>
    </div>
  );
});

combineReducers(reducers)#

  • reducers/todos.js
export default function todos(state = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      return state.concat([action.text]);
    default:
      return state;
  }
}
  • reducers/counter.js
export default function counter(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      return state;
  }
}
  • reducers/index.js
import { combineReducers } from 'redux';
import todos from './todos';
import counter from './counter';

export default combineReducers({
  todos,
  counter,
});
  • App.js
import { createStore } from 'redux';
import reducer from './reducers/index';

let store = createStore(reducer);
console.log(store.getState());
// {
//   counter: 0,
//   todos: []
// }

store.dispatch({
  type: 'ADD_TODO',
  text: 'Reduxを使用する',
});
console.log(store.getState());
// {
//   counter: 0,
//   todos: [ 'Reduxを使用する' ]
// }

Mobx#

store#

import { action, computed, runInAction, makeAutoObservable, flow } from 'mobx';

export class CounterStore {
  // データを定義
  count = 0;
  list = [1, 2, 3, 4, 5];
  asyncData = 0;
  flowData = 'default';
  constructor() {
    // データをリアクティブにする
    makeAutoObservable(this, {
      // マークすることも、しないこともできます
      incrementCount: action,
      decrementCount: action,
      changeFilterList: action,
      asyncAdd: action,
      asyncFlow: action.bound,
      filterList: computed,
    });
  }
  // データを変更するためのアクション関数を定義
  incrementCount = () => {
    this.count++;
  };
  decrementCount = () => {
    this.count--;
  };
  changeFilterList = () => {
    this.list.push(...[5, 4, 3, 2, 1]);
  };

  // 非同期アクション(runInActionでラップ)
  asyncAdd = async () => {
    await Promise.resolve();
    runInAction(() => {
      this.incrementCount();
      this.asyncData++;
    });
  };
  // 非同期アクション(flowを使用)
  asyncFlow = flow(function* () {
    yield console.log(this); // thisをバインドする必要があります
    const data = yield Promise.resolve('Flow');
    this.flowData = data;
  });

  // computedを定義
  get filterList() {
    return this.list.map(item => item * 2);
  }
}

ルートストア#

import { CounterStore } from './counter';

class Store {
  constructor() {
    this.counter = new CounterStore();
  }
}
const store = new Store();
export { store };

store の共有#

コンテキストを使用

import React, { createContext } from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { store } from './store';
export const Context = createContext();

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Context.Provider value={store}>
    <App />
  </Context.Provider>
);

コンポーネント内での使用#

import React, { memo, useContext } from 'react';

import { observer } from 'mobx-react-lite';
import { Context } from '../..';

const Counter = () => {
  const {
    counter: {
      count,
      changeFilterList,
      asyncData,
      flowData,
      decrementCount,
      filterList,
      incrementCount,
      asyncAdd,
      asyncFlow,
    },
  } = useContext(Context);
  return (
    <div>
      <button onClick={incrementCount}>+</button>
      <span>{count}</span>
      <button onClick={decrementCount}>-</button>
      {filterList.map((i, n) => {
        return <li key={n}>{i}</li>;
      })}
      <button onClick={changeFilterList}>配列を追加</button>
      <hr />
      <span>{asyncData}</span>
      <button onClick={asyncAdd}>非同期runInAction</button>
      <hr />
      <span>{flowData}</span>
      <button onClick={asyncFlow}>非同期flow</button>
    </div>
  );
};

export default memo(observer(Counter));
読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。