mapStateToProps()を使いこなして効率的なReact-Redux開発
React-ReduxとmapStateToProps()の理解
React は、ユーザーインターフェース構築のためのJavaScriptライブラリです。コンポーネントと呼ばれる独立した部分で構成され、動的なUIを構築できます。
Redux は、アプリケーションの状態管理のためのライブラリです。状態を単一のストアに保存し、コンポーネント間で共有できるようにします。
React-Redux
React-Redux は、ReactとReduxを組み合わせるためのライブラリです。ReactコンポーネントをReduxストアに接続し、状態の読み書きを可能にします。
mapStateToProps()
mapStateToProps() は、React-ReduxでコンポーネントとReduxストアを接続するために使用する関数です。この関数は、コンポーネントが使用する必要がある状態をストアから取得し、コンポーネントのプロパティに変換します。
const mapStateToProps = (state) => {
return {
count: state.counter.count,
};
};
const MyComponent = (props) => {
return (
<div>
<h1>カウント: {props.count}</h1>
</div>
);
};
export default connect(mapStateToProps)(MyComponent);
上記の例では、mapStateToProps()
は state
オブジェクトから count
プロパティを取得し、MyComponent
コンポーネントのプロパティとして渡しています。
mapStateToProps()
は以下の引数を受け取ります。
state
: Reduxストアの状態オブジェクトownProps
: コンポーネント自身のプロパティ
mapStateToProps()
は純粋な関数である必要があります。つまり、引数と同じ入力に対して常に同じ出力を返す必要があります。mapStateToProps()
はパフォーマンスに影響を与える可能性があります。そのため、必要な状態のみを取得するようにする必要があります。
├── App.js
└── Counter.js
App.js
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import Counter from './Counter';
const store = createStore((state = { count: 0 }, action) => {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
case 'DECREMENT':
return { ...state, count: state.count - 1 };
default:
return state;
}
});
const App = () => {
return (
<Provider store={store}>
<Counter />
</Provider>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
Counter.js
import React from 'react';
import { connect } from 'react-redux';
const mapStateToProps = (state) => {
return {
count: state.count,
};
};
const mapDispatchToProps = (dispatch) => {
return {
increment: () => dispatch({ type: 'INCREMENT' }),
decrement: () => dispatch({ type: 'DECREMENT' }),
};
};
const Counter = (props) => {
return (
<div>
<h1>カウント: {props.count}</h1>
<button onClick={props.increment}>+</button>
<button onClick={props.decrement}>-</button>
</div>
);
};
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
解説
App.js
は、Reduxストアを作成し、Provider
コンポーネントでラップすることで、Counter
コンポーネントにストアを提供しています。Counter.js
は、mapStateToProps()
を使用して、count
プロパティをReduxストアから取得しています。mapDispatchToProps
を使用して、increment()
とdecrement()
アクションをReduxストアに dispatch する関数を取得しています。
mapStateToProps() の代替方法
useSelector()
useSelector()
フックは、React Redux v7.0.0 で導入された新しいフックです。mapStateToProps()
と同様の機能を提供しますが、より簡潔で使いやすいコードを書くことができます。
const MyComponent = () => {
const count = useSelector((state) => state.counter.count);
return (
<div>
<h1>カウント: {count}</h1>
</div>
);
};
上記の例では、useSelector()
フックを使用して、count
プロパティをReduxストアから取得しています。
connectAdvanced()
connectAdvanced()
は、connect()
関数の内部実装です。connect()
よりも低レベルな API を提供し、より細かい制御が可能です。
const MyComponent = connectAdvanced(
(state) => ({
count: state.counter.count,
}),
{
increment: () => dispatch({ type: 'INCREMENT' }),
decrement: () => dispatch({ type: 'DECREMENT' }),
}
)(MyComponent);
上記の例では、connectAdvanced()
を使用して、MyComponent
コンポーネントをReduxストアに接続しています。
自作の HOC
独自の Higher-Order Component (HOC) を作成することもできます。HOC は、コンポーネントを別のコンポーネントでラップし、追加の機能を与える関数です。
const withCounter = (Component) => {
return (props) => {
const count = useSelector((state) => state.counter.count);
return (
<Component
{...props}
count={count}
/>
);
};
};
const MyComponent = withCounter((props) => {
return (
<div>
<h1>カウント: {props.count}</h1>
</div>
);
});
上記の例では、withCounter()
HOC を作成して、MyComponent
コンポーネントに count
プロパティを提供しています。
recompose
や react-redux-firebase
などのライブラリは、mapStateToProps()
の代替方法を提供しています。
どの方法を選択すべきか
どの方法を選択すべきかは、プロジェクトの要件と開発者の好みによって異なります。
mapStateToProps()
は、最も一般的な方法であり、多くの場合で十分です。useSelector()
は、より簡潔で使いやすいコードを書くことができます。connectAdvanced()
は、より細かい制御が必要な場合に使用します。- 自作の HOC は、特殊な要件がある場合に使用します。
javascript reactjs redux