クラスコンポーネントで作成していたReactのプロジェクトを、React Hooksを使用した関数コンポーネントにリファクタリングする際に、React ReduxとReact RouterもHooksに対応させました。
その時に対応の仕方を簡単にですがまとめました。
React Reduxはv7.1
から、React Routerはv5.1
からHooksに対応しています。
環境
- React : 16.12.0
- React Redux : 7.2.0
- React Router : 5.1.2
Redux Hooks
React ReduxがHooksに対応したことにより、各コンポーネントにおいて簡単にdispatch
とstate
を利用できるようになりました。今回は以下の3点について説明します。
- ActionをStoreにDispatchする
mapDispatchToProps
→useDispatch()
- StoreからStateを取得する
mapStateToProps
→useSelector()
- ReactコンポーネントとReduxのStoreを紐付ける
connect
→いらない
mapDispatchToProps
はuseDispatch()
に、mapStateToProps
はuseSelector()
を使うことで、Hooksに対応できます。これで各コンポーネントで簡単にdispatchとstateを利用することができます。またconnect()
を使用する必要がなくなります。
mapDispatchToProps
まずはmapDispatchToProps
でActionをStoreにDispatchしていたところを、useDispatch()
で置き換えます。
基本的な使い方は以下のようになります。Actionに登録されている関数をgateData
としておきます。
- mapDispatchToprops
class App extends Component{
componentDidMount(){
this.props.gateData();
}
render(){
// JSX
}
}
const mapDispatchToProps = (dispatch) => {
return{
gateData: () => dispatch(gateData())
}
}
- useDispatch()
const App = () => {
// mapDispatchToPropsの部分
const dispatch = useDispatch();
useEffect(()=>{
// this.props.gateData()の部分
dispatch(gateData());
})
return(
// JSX
)
}
完全に関数コンポーネント内にReduxの実装をまとめることができます。さらに直感的にReduxの機能を使用することができます。
mapStateToProps
次はmapStateToProps
でStoreからStateを取得していたところを、useSelector()
を使用して、Hooksに対応します。
基本的な使い方は以下のようになります。Reducerに登録されているstateをdata
としておきます。
- mapStateToProps
class App extends Component{
render(){
const { data } = this.props;
return(
// JSX
)
}
}
const mapStateToProps = (state) => {
return{
data: state.data
}
}
- useSelector
const App = () => {
// mapStateToProps & this.propsの部分
const data = useSelector(state => state.data);
return(
// JSX
)
}
一目でわかりますが、useSelectorを使用するだけでStoreからStateを取得することができます。
独自Hooksと組み合わせる
Hooks APIとRedux Hooksを組み合わせて、Actionを実行してReducerからStateを取得する独自Hooksを作成します。
例として以下のようなクラスコンポーネントがあったとします。
class App extends Component{
componentDidMount(){
this.props.getData();
}
render(){
const { data } = this.props;
return(
// JSX
)
}
}
const mapDispatchToProps = (dispatch) => {
return{
getData: () => dispatch(getData())
}
}
const mapStateToProps = (state) => {
return{
data: state.data
}
}
export default connect(mapStateToProps,mapDispatchToProps)(App);
上記のクラスコンポーネントを独自Hooksを利用して、関数コンポーネントで実装します。
const useGetData = () => {
const dispatch = useDispatch();
const data = useSelector(state => state.data);
useEffect(() => {
dispatch(getData());
})
return data;
}
const App = () => {
const data = useGetData();
return(
// JSX
)
}
export default App;
Hooks APIとRedux Hooksを利用してActionを実行してStateを返す独自HooksのuseGetData
と、データを表示するコンポーネントApp
に分離することができます。Reduxを利用してデータを取得するロジック部分をコンポーネントから切り離すことができるので、ロジック部分は他のコンポーネントでも使いまわせます。
コンポーネントごとに、connect
やmapDispatchToProps
、mapStateToProps
を記述する煩わしさからも解放されます。コードもすっきりしますね。
以上がRedux Hooksの使い方でした。
React Router Hooks
React RouterがHooksに対応したことにより、これまでコンポーネントのprops
から取得していたhistory
やparams
を関数を用いて、どこからでも取得することができます。
つまりコンポーネント以外の関数からも使用できるようになります。
以下の3つを説明します。
- useHistory() : ページ遷移で使用する
history
を取得する - useLocation() : 現在のURLやpathやクエリパラメータを取得する
- useParams() : URLからパスパラメータを取得する
useHistory
useHistory()
を使用することで、ページ遷移で使用するhistory
を取得することができます。
Hooks未対応の場合、コンポーネントのpropsからhistoryを取得します。そのため、クラス内でしか取得できませんでした。
useHistroy()
を使うことで、コンポーネント以外でもhistoryを取得することができます。
const App = () => {
const history = useHistory();
const handleOnclick = (e) => {
e.preventDefault();
// 画面遷移
history.push('/');
// props.history.push('/');
}
return(
<Link onClick={handleOnclick}>
Link
</Link>
)
}
historyを取得できれば、history.push('/')
やhistory.goBack()
を実行することができます。
useLocation
useLocation()
を使用することで、現在のページのURLやクエリパラメータを取得することができます。
Hooks未対応の場合、コンポーネントのpropsからlocationを取得し、pathからURLをsearchからクエリパラメータを取得ます。そのため、クラス内でしか取得できませんでした。
useLocation()
を使うことで、コンポーネント以外でもlocationを取得することができます。
?id=1&name=sample
というクエリパラメータを設定したとします。
const App = () =>{
// const location = props.location;
const location = useLocation();
// URLを取得
const path = location.path;
// クエリパスを取得
const query = location.serach;
return(
<>
<p>{path}</p>
<p>{query}</p>
</>
)
}
useParams
React Routerでは、<Route exact paht='/url/:id' component={}></Route>
のように、pathに:~
とすることで、URLからパスパラメータを取得することができます。
クラスコンポーネントでは、コンポーネントのpropsから取得します。
useParams()
を使うことで、コンポーネント以外でもparamsを取得することができます。
const App = () => {
// URLパラメータを取得
const {id} = useParams();
// const id = this.props.match.params.id;
return(
<>
{id}
</>
)
}
React RouterのRouteの:~
の~の変数を合わせることで、パスパラメータを取得することができます。
簡単ですが、React Routerで使用した、useHistory
, useLocation
, useParams
の使い方でした。総じてReact RoterがHooksに対応したことで、関数のどこからでも使用することができるようになったことがメリットかなと思います。
まとめ
簡単にですがReduxとReact RouterのHooks対応をまとめました。
ReduxとReact Router共に、それぞれの機能を関数を呼び出すことで利用できるので、独自Hooksとの親和性が高いのかなと感じました。
全体的にも実装の仕方がシンプルで使いやすく見やすくなってます。
まだまだReact初心者ですが少しづつモダンな実装ができるように勉強していきます。