Reactを学ぶ14

「React Hooks 入門 - Hooksと Redux を組み合わせて最新のフロントエンド状態管理手法を習得しよう!」を参考に。
作業時間:8時間 経過時間:58時間
useState
const [count,setCount] = useState(0)
...
// const increment = () => setCount(count + 1) 現在のstateに対して
const increment = () => setCount(previousCount => previousCount + 1) //前回のstateに対して
return (
<button onClick={increment}>+1</button>
)
propsでuseStateに参照させることもできる。 onClickで関数を呼べば良いのに気づかなかった。 呼び出した関数先でstateの更新処理を行える。
const App = props => {
// const initialStates = {
// name: '',
// price: 1000
// }
// const [name,setName] = useState(initialStates.name)
// const [price,setPrice] = useState(initialStates.price)
const [name,setName] = useState(props.name)
const [price,setPrice] = useState(props.price)
...
const reset = () => {
setPrice(props.price)
setName(props.name)
}
...
return(
<button onClick={reset}>Reset</button>
)
}
App.defaultProps = {
name: '',
price: 1000
}
useEffect
const App = props => {
...
useEffect(()=>{
console.log('This is like componentDidMount or componentDidupdate')
})
useEffect(()=>{
console.log('This is like componentDidMount')
},[])
useEffect(()=>{
console.log('This callback is for name only.')
},[name])
...
return (
...)
}
状態遷移
状態遷移表というのが存在する 例えば以下のように、状態が遷移する要素と、遷移させるトリガーを対にした関係図になる。
| | 追加 | 削除 | 更新 | | ---------- | ---------- | ---------- | ---------- | | リスト | リストの最後に追加 | リストの最後を削除 | リストの最後を更新 |
Reactでは、このカラムがAction,追加・削除・更新などの種別のフィールドがタイプ、内容のフィールドがReducerとなる。
状態遷移図というのもある。
useReducer
// src/components/App.js
import Event from '../Event'
// useRdecuerの第一引数には、reducer,第二引数にはstateの初期状態、第三引数には初期処理
const [state,dispatch] = useReducer(reducer,[])
const App = () => {
...
return (
...
<tbody>
{state.map((event,index) => (<Event key={index} event={event} dispatch={dispatch} />))}
</tbody>
...
)
}
// // src/Event/index.js
const Event = ({event,dispatch}) => {
...
}
子コンポーネントでどうやってstateやdispatchにアクセスするか、忘れていた。propsだ。 dispatchのイメージが固まってきた。 handlerでオブジェクト(action)がdispatchに渡される。 dispatchに渡ると、reducerにオブジェクトが渡される。タイプが識別され、応じて処理される。 どこで、dispatchとreducerが紐付けられているか、ぼんやりしていたけど、 純粋に useReducerの引数にreducerを渡して、そこからstateとdispatchを定義しているから、紐付いているのか、hooksの場合は。
componentsの分割
分割した子コンポーネントの中で、state,dispatchを定義すると、親と別々のオブジェクトが生成されてしまう。なので、同期がとれない。参照するようにしないといけない。propsを使用して解決する。
参照できない例(別々のメモリが確保される?)
// src/components/App.js
const App = () => {
const [state,dispatch] = useReducer(reducer,[])
return (
<div className="container-fluid">
<EventFrom />
<Events />
</div>
);
}
// src/components/Events.js
const Events = () => {
const [state,dispatch] = useReducer(reducer,[])
return (
...
)
}
参照できる例
// src/components/App.js
const App = () => {
const [state,dispatch] = useReducer(reducer,[])
return (
<div className="container-fluid">
<EventFrom state={state} dispatch={dispatch} />
<Events state={state} dispatch={dispatch} />
</div>
);
}
// src/components/Events.js
const Events = ({state,dispatch}) => {
return (
...
)
}
同期をしたいデータを参照させるのは、hooksであろうとなかろうと同じ考え方、propsで紐付ける。 props:インターフェース。
Action
actionのタイプは定数で定義するのが慣習らしい。
Prop Drilling 問題
親から子にデータを参照させていくには、propsで受け渡していく必要がある。 しかし、階層が深くなったり、広くなったりすると、propsを受け渡すためだけに記述をしないといけないケースが増える。それを解決する手段がRedux。 コンポーネントに対して、stateとdispatchをreact-reduxのconnectでデコーレーションさせる。 そうすると、子自身からアクセスできるようになる。
context
createContext API
// src/contexts/AppContext.js
import {createContext} from 'react'
const AppContext = createContext()
export default AppContext
// src/components/App.js
import AppContext from '../contexts/AppContext'
const App = () => {
return (
<AppContext.Provider value={'hello world'}>
...
</AppContext.Provider>
);
}
// src/components/Events.js
import AppContext from '../contexts/AppContext'
const Events = ({state,dispatch}) => {
return (
<AppContext.Consumer>
{value=>{
return <div>{value}</div>
}}
</AppContext.Consumer>
...
)
}
useContext
// src/contexts/AppContext.js
import {createContext} from 'react'
const AppContext = createContext()
export default AppContext
// src/components/App.js
import AppContext from '../contexts/AppContext'
const App = () => {
return (
<AppContext.Provider value={'hello world'}>
...
</AppContext.Provider>
);
}
// src/components/Events.js
import React, {useContext} from 'react
...
import AppContext from '../contexts/AppContext'
const Events = ({state,dispatch}) => {
const value = useContext(AppContext)
return (
<div>{value}</div>
...
)
}
引数にcontextを渡せば、変数を受け取れる。 依存関係を減らせるのが良い。そのコンポーネントだけに集中できる。
Redux
複数の状態管理を行うには、Reactとは別のライブラリが必要となる。Redux。 Reduxでreducerを複数管理することによって、状態管理をそれぞれ保持できる。 combineReducers()を使用すると、stateの返り値がオブジェクトとして返される。
// src/reducers/index.js
import {combineReducers} from 'redux'
import events from './events'
export default combineReducers({events})
// state.eventsでアクセスできる
// src/components/App.js
// オブジェクトで初期化する
const initialState = {
events: [],
}
const [state,dispatch] = useReducer(reducer,initialState)
LocalStorage
localstorageと併用してデータの永続化を行える。 stringfyメソッドで文字列化し、pargeで戻す。
const APP_KEY = 'appWithRedux'
const App = () => {
const appState = localStorage.getItem(APP_KEY)
const initialState = appState ? JSON.parse(appState) : {
events: [],
operationLogs: []
}
const [state,dispatch] = useReducer(reducer,initialState)
useEffect(()=>{
localStorage.setItem(APP_KEY,JSON.stringify(state))
},[state])
...
}
そのほか
Git
git push origin HEAD
が分からないので調べた。
リモートリポジトリに対して、ローカルリポジトリ名と同じブランチ名にプッシュできる。
知らなかった。。とても楽。
git push -u origin HEAD
のオプションuは追跡監視するとのことだがまだピンと来ていない。。