React状態管理ツール-Redux学習ノート
React状態管理ツール-Redux
前端高給訓練キャンプ
1.Reduxコア
1.1 Redux紹介
JavaScript状態容器は、予測可能な状態管理を提供します.
1.2 Reduxコア概念とフロー
Store:格納状態のコンテナ、JavaScriptオブジェクトView:ビュー、HTMLページアクション:オブジェクト、状態に対してどのような操作が行われているかを記述し、Reducers:関数、操作状態を説明し、新しい状態に戻ります.
1.3 Redux使用:カウンタケース
2.1 ReactでReduxを使わない時に発生した問題
Reactではコンポーネント通信のデータストリームは一方向であり、トップのコンポーネントはProps属性によって下部のコンポーネントにデータを転送することができ、下部のコンポーネントは上部のコンポーネントにデータを転送することができない.下のコンポーネントでデータを修正するには、上のコンポーネントから下のコンポーネントに修正データを転送する方法が必要です.プロジェクトがますます大きくなると、コンポーネント間のデータ転送が難しくなります.
2.2 ReactプロジェクトにReduxを加えるメリット
Reduxを使用してデータを管理し、Storeはコンポーネントと独立しているため、データ管理がコンポーネントと独立し、コンポーネントとコンポーネントの間でデータを転送するのが困難な問題を解決した.
2.3ダウンロードReduxコンポーネントは、dispach方法によってアクションをトリガする . Storeはアクションを受け取り、アクションをReducer に配信する. Reducerは、アクションタイプに従って状態を変更し、変更された状態をStore に戻す.コンポーネントはStoreの状態を購読しています.Storeの状態更新はコンポーネント に同期されます.
2.5 Redux使用手順
2.5.1 storeを作成するリレーパラメータ は、パラメータを受け取り、 を伝える.
src/index.js
src/store/reducers/root.reducer.js
3.1中間部品とは何ですか?
中間価格は私達がreduxアプリケーションを拡張し、強化することができます.
3.2 Redux中間部品の開発
中間部品のテンプレートを開発する
中間部品は開発が完了した後、登録されてこそ、Reduxのワークフローで有効になります.
src/store/index.js
3.4 Redux中間部品開発例thunk(非同期中間部品)
現在のこの中間関数は、どのような非同期操作を実行したいのかに関心がなく、ただ非同期操作を実行しているかに関心があります.もしあなたが実行しているのが非同期操作であれば、actionをトリガする時に、関数を伝えてください.もし実行しているのが同期操作なら、actionオブジェクトを伝達します.非同期の操作コードは、あなたが送ってきた関数の中に書いてください.この中間部品の関数は、あなたが送ってきた関数を呼び出した時に、dispach方法を過去のsrc/store/middleware/thunk.jsに伝えます.
src/store/actions/modal.actions.js
4.Redux常用ミドルウェア
4.1 redux-thunk
4.1.1 redux-thunkダウンロード
4.2.1 redux-saga解決の問題
4.2.2ダウンロードredux-saga
src/store/index.js
src/store/index.js
src/store/sagas/counter.saga.js
src/store/index.js
src/store/saga/root.saga.js
src/store/saga/modal.saga.js
src/store/index.js
4.3.1 redux-actions解決の問題
reduxプロセスの中で大量のサンプルコードは読み书きに苦しみます.redux-actionを使ってアクションとReducerの処理を简略化できます.
4.3.2 redux-actionのダウンロード
src/store/actions/counter.actions.js
前端高給訓練キャンプ
1.Reduxコア
1.1 Redux紹介
JavaScript状態容器は、予測可能な状態管理を提供します.
1.2 Reduxコア概念とフロー
Store:格納状態のコンテナ、JavaScriptオブジェクトView:ビュー、HTMLページアクション:オブジェクト、状態に対してどのような操作が行われているかを記述し、Reducers:関数、操作状態を説明し、新しい状態に戻ります.
1.3 Redux使用:カウンタケース
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Reduxtitle>
head>
<body>
<button id="minus">-button>
<span id="count">0span>
<button id="plus">+button>
<script src="./redux.min.js">script>
<script>
// 3.
const initialState = {
count: 0
}
// 2. reducer
function reducer(state = initialState, action) {
switch (action.type) {
case 'increment':
return {
count: state.count + 1 };
case 'decrement':
return {
count: state.count - 1 };
default:
return state;
}
}
// 1. store
const store = Redux.createStore(reducer);
// 4. action
const increment = {
type: 'increment' }
const decrement = {
type: 'decrement' }
// 5.
document.getElementById('minus')
.addEventListener('click', function () {
// 6. dispatch action
store.dispatch(decrement)
})
document.getElementById('plus')
.addEventListener('click', function () {
// 6. dispatch action
store.dispatch(increment)
})
// store {dispatch: ƒ, subscribe: ƒ, getState: ƒ, replaceReducer: ƒ, Symbol(observable): ƒ}
console.log(store)
// store state
console.log(store.getState());
//
store.subscribe(() => {
console.log(store.getState())
document.getElementById('count').innerHTML = store.getState().count
});
script>
body>
html>
1.4 ReduxコアAPI// store
const store = Redux.createStore(reducer);
// 2. reducer
function reducer(state = initialState, action) {
switch (action.type) {
case 'increment':
return {
count: state.count + 1 };
case 'decrement':
return {
count: state.count - 1 };
default:
return state;
}
}
// store state
store.getState()
//
store.subscribe(() => {
console.log(store.getState())
});
// dispatch action
store.dispatch(increment)
2.React+Redux2.1 ReactでReduxを使わない時に発生した問題
Reactではコンポーネント通信のデータストリームは一方向であり、トップのコンポーネントはProps属性によって下部のコンポーネントにデータを転送することができ、下部のコンポーネントは上部のコンポーネントにデータを転送することができない.下のコンポーネントでデータを修正するには、上のコンポーネントから下のコンポーネントに修正データを転送する方法が必要です.プロジェクトがますます大きくなると、コンポーネント間のデータ転送が難しくなります.
2.2 ReactプロジェクトにReduxを加えるメリット
Reduxを使用してデータを管理し、Storeはコンポーネントと独立しているため、データ管理がコンポーネントと独立し、コンポーネントとコンポーネントの間でデータを転送するのが困難な問題を解決した.
2.3ダウンロードRedux
npm install redux react-redux
2.4 Reduxワークフロー2.5 Redux使用手順
2.5.1 storeを作成する
// src/store/index.js
import {
createStore } from 'redux'
import reducer from './reducers/counter.reducer'
export const store = createStore(reducer)
ルートコンポーネントにstore
を使用する.import React from 'react';
import ReactDOM from 'react-dom';
import Counter from './components/Counter'
import {
Provider } from 'react-redux'
import {
store} from './store'
/**
* react-redux
* Provider
* connect
*/
ReactDOM.render(
// provider , store
<Provider store={
store}>
<Counter />
</Provider>,
document.getElementById('root')
);
2.5.2 reducerを作成する// src/store/reducers/counter.reducer.js
import {
DECREMENT, INCREMENT } from "../count/counter.const";
const initialState = {
count: 0
}
export default function reducer (state = initialState, action) {
switch (action.type) {
case INCREMENT:
return {
count: state.count + 1 };
case DECREMENT:
return {
count: state.count - 1 };
default:
return state;
}
}
// src/store/count/counter.const.js
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
2.5.3コンポーネントの中でconnectを使ってstoreの中のstateとdispatchを受け入れます.connect
方法は、2つのパラメータを受け取り、高次のコンポーネントを返す.connect
方法の最初のパラメータはmapStateToProps
方法であり、storeのstateをコンポーネントのprops
に伝達し、mapStateToProps
方法のパラメータはstate
であり、戻り値はオブジェクトであり、コンポーネントに伝達される.const mapStateToProps = (state) => ({
count: state.count,
a: 'a', // ,
})
connect
方法の2番目のパラメータはmapDispatchToProps
方法であり、store
のdispatch
をコンポーネントのprops
に伝達し、mapDispatchToProps
方法のパラメータはdispatch
であり、戻り値はオブジェクトであり、オブジェクト内の方法はdispatch
を使用してもよく、このオブジェクト内の方法はコンポーネントに伝達される.const mapDispatchToProps = (dispatch) => ({
increment () {
dispatch({
type: 'increment'})
},
decrement () {
dispatch({
type: 'decrement' })
}
})
また、redux
のbindActionCreators
を介して、action
関数を作成しても良いです.import {
bindActionCreators} from 'redux'
// bindActionCreators
const mapDispatchToProps = dispatch => (
//
...bindActionCreators({
increment () {
return {
type: 'increment'}
},
decrement () {
return {
type: 'decrement'}
}
}, dispatch)
)
または作成:const mapDispatchToProps = dispatch => bindActionCreators({
increment () {
return {
type: 'increment'}
},
decrement () {
return {
type: 'decrement'}
}
}, dispatch)
bindActionCreators
の最初のパラメータをオフにしてもよい.import * as counterActions from '../store/actions/counter.actions'
const mapDispatchToProps = dispatch => bindActionCreators(conterActions, dispatch)
// src/store/actions/counter.actions.js
import {
DECREMENT, INCREMENT } from "../count/counter.const"
export const increment = () => ({
type: INCREMENT})
export const decrement = () => ({
type: DECREMENT})
connect
方法は、mapStateToProps
およびmapDispatchToProps
を受け取り、高次のコンポーネントに戻り、Counter
のコンポーネントに導かれる.export default connect(mapStateToProps, mapDispatchToProps)(Counter)
最終コンポーネントコードは以下の通りです.// src/components/Counter.js
import React from 'react'
import {
connect} from 'react-redux'
import {
bindActionCreators} from 'redux'
import * as counterActions from '../store/actions/counter.actions'
function Counter ({
count, increment, decrement}) {
return (
<div>
<button onClick={
decrement}>-</button>
<span>{
count}</span>
<button onClick={
increment}>+</button>
</div>
)
}
// 1. connect store, store ,
// 2. connect store , props
// 3. connect dispatch
const mapStateToProps = (state) => ({
count: state.count,
a: 'a', // ,
})
const mapDispatchToProps = dispatch => bindActionCreators(counterActions, dispatch)
export default connect(mapStateToProps, mapDispatchToProps)(Counter)
2.5.4 action伝達パラメータ<button onClick={
() => increment(5)}> + 5</button>
reducer
export const increment = payload => ({
type: INCREMENT, payload})
export const decrement = payload => ({
type: DECREMENT, payload})
reducer
は、受信したデータに基づいて処理する.export default function reducer (state = initialState, action) {
switch (action.type) {
case INCREMENT:
return {
count: state.count + action.payload };
case DECREMENT:
return {
count: state.count - action.payload };
default:
return state;
}
}
2.6 reduxポップアップブロックの判例を実現するstore
中の状態が多いほど、reducer
中のswitch
分岐は多くなり、維持に不利で、reducer
を分割する必要がある.src/index.js
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {
Provider } from 'react-redux'
import {
store} from './store'
ReactDOM.render(
// provider , store
<Provider store={
store}>
<App />
</Provider>,
document.getElementById('root')
);
src/store/index.js// src/store/index.js
import {
createStore } from 'redux'
import reducer from './reducers/counter.reducer'
export const store = createStore(reducer)
src/store/reducers/counter.reducer.js// src/store/reducers/counter.reducer.js
import {
DECREMENT, INCREMENT } from "../const/counter.const";
import {
HIDEMODAL, SHOWMODAL } from "../const/modal.const";
const initialState = {
count: 0,
show: false
}
export default function reducer (state = initialState, action) {
switch (action.type) {
case INCREMENT:
return {
...state, count: state.count + action.payload };
case DECREMENT:
return {
...state, count: state.count - action.payload };
case SHOWMODAL:
return {
...state, show: true };
case HIDEMODAL:
return {
...state, show: false };
default:
return state;
}
}
src/App.js// src/App.js
import Modal from './components/Modal'
import Counter from './components/Counter'
function App() {
return (
<div className="App">
<Counter />
<Modal />
</div>
);
}
export default App;
src/components/Modal.js// src/components/Modal.js
import React from 'react'
import {
connect } from 'react-redux'
import {
bindActionCreators } from 'redux'
import * as modalActions from '../store/actions/modal.actions'
function Modal ({
showStatus, show, hide }) {
const styles = {
display: showStatus ? 'block': 'none',
width: 200,
height: 200,
position: 'absolute',
top: 0,
right: 0,
bottom: 0,
left: 0,
margin: 'auto',
backgroundColor: 'skyblue'
}
return (
<div>
<button onClick={
show}> </button>
<button onClick={
hide}> </button>
<div style={
styles}></div>
</div>
)
}
const mapStateToProps = state => ({
showStatus: state.show
})
const mapDispatchToProps = dispatch => bindActionCreators(modalActions, dispatch)
export default connect(mapStateToProps, mapDispatchToProps)(Modal)
src/store/actions/modal.actions.js// src/store/actions/modal.action.js
import {
HIDEMODAL, SHOWMODAL } from "../const/modal.const"
export const show = () => ({
type: SHOWMODAL })
export const hide = () => ({
type: HIDEMODAL })
src/store/const/modal.co nst.js// src/store/const/modal.const.js
export const SHOWMODAL = 'showModal'
export const HIDEMODAL = 'hideModal'
2.7分割reducer
reducer
によって提供されるツールcombineReducers
を使用して、それぞれの小さなreducer
をマージする.src/store/reducers/root.reducer.js
// src/store/reducers/root.reducer.js
import {
combineReducers} from 'redux'
import CounterReducer from './counter.reducer'
import ModalReducer from './modal.reducer'
// { counter: { count: 0 }, modal: { show: false } }
export default combineReducers({
counter: CounterReducer,
modal: ModalReducer
})
src/store/reducers/counter.reducer.js// src/store/reducers/counter.reducer.js
import {
DECREMENT, INCREMENT } from "../const/counter.const";
const initialState = {
count: 0,
}
export default function counterReducer (state = initialState, action) {
switch (action.type) {
case INCREMENT:
return {
...state, count: state.count + action.payload };
case DECREMENT:
return {
...state, count: state.count - action.payload };
default:
return state;
}
}
src/store/reducers/modal.reducer.js// src/store/reducers/modal.reducer.js
import {
HIDEMODAL, SHOWMODAL } from "../const/modal.const";
const initialState = {
show: false
}
export default function modalReducer (state = initialState, action) {
switch (action.type) {
case SHOWMODAL:
return {
...state, show: true };
case HIDEMODAL:
return {
...state, show: false };
default:
return state;
}
}
store
を作成するときに入ってきたreducer
は、私たちが先ほど定義したroot.reducer.js
から来ています.import {
createStore } from 'redux'
import RootReducer from './reducers/root.reducer'
export const store = createStore(RootReducer)
各コンポーネントのmapStateToProps
においても、対応する変更が発生する(state.counter
およびstate.modal
).const mapStateToProps = (state) => ({
count: state.counter.count,
})
const mapStateToProps = state => ({
showStatus: state.modal.show
})
3.Redux中間部品3.1中間部品とは何ですか?
中間価格は私達がreduxアプリケーションを拡張し、強化することができます.
3.2 Redux中間部品の開発
中間部品のテンプレートを開発する
export default store => next => action => {
}
3.3中間部品を登録する中間部品は開発が完了した後、登録されてこそ、Reduxのワークフローで有効になります.
src/store/index.js
// src/store/index.js
import {
createStore, applyMiddleware } from 'redux'
import logger from './middlewares/logger'
createStore(reducer, applyMiddleware(
logger
))
src/store/middleware/logger.jsconst logger = store => next => action => {
console.log(store)
console.log(action)
next(action) // next(action)
}
export default logger
複数の中間部品を登録する場合、中間部品の実行順序は登録順序です.createStore(reducer, applyMiddleware(
logger,
test
))
実行順序は、logger
ミドルウェアを先に実行し、test
ミドルウェアを実行することである.中間部品の終わりにnext(action)
を起動しないと、フロー全体はここでカードになります.これ以上実行しません.3.4 Redux中間部品開発例thunk(非同期中間部品)
現在のこの中間関数は、どのような非同期操作を実行したいのかに関心がなく、ただ非同期操作を実行しているかに関心があります.もしあなたが実行しているのが非同期操作であれば、actionをトリガする時に、関数を伝えてください.もし実行しているのが同期操作なら、actionオブジェクトを伝達します.非同期の操作コードは、あなたが送ってきた関数の中に書いてください.この中間部品の関数は、あなたが送ってきた関数を呼び出した時に、dispach方法を過去のsrc/store/middleware/thunk.jsに伝えます.
// src/store/middleware/thunk.js
import {
DECREMENT, INCREMENT } from "../const/counter.const";
const thunk = ({
dispatch}) => next => action => {
if (typeof action === 'function') {
return action(dispatch) // action dispatch
}
next(action)
}
export default thunk
非同期関数actionをactionファイルに定義します.src/store/actions/modal.actions.js
// src/store/actions/modal.actions.js
import {
HIDEMODAL, SHOWMODAL } from "../const/modal.const"
export const show = () => ({
type: SHOWMODAL })
export const hide = () => ({
type: HIDEMODAL })
export const show_async = () => dispatch => {
setTimeout(() => {
dispatch(show())
}, 2000);
}
元々はshow
を使っていたところをshow_async
に切り替え、非同期の機能を実現しました.4.Redux常用ミドルウェア
4.1 redux-thunk
4.1.1 redux-thunkダウンロード
npm install redux-thunk
4.1.2 redux-thunkの導入import thunk from 'redux-thunk';
4.1.3 redux-thunkを登録するimport {
applyMiddleware } from 'redux'
createStore(rootReducer, applyMiddleware(thunk));
4.1.4 redux-thunk中間部品を使用するconst loadPosts = () => async dispatch => {
const posts = await axios.get('/api/posts').then(response => response.data);
dispatch({
type: LOADPOSTSSUCCE, payload: posts});
}
4.2 redux-saga4.2.1 redux-saga解決の問題
redux-saga
は、非同期動作をAction Creator
ファイルから引き出して、別個のファイルに置くことができる.4.2.2ダウンロードredux-saga
npm install redux-saga
4.2.3レdux-sagaミドルウェアを作成するsrc/store/index.js
// src/store/index.js
import createSagaMiddleware from 'redux-saga';
const sagaMiddleware = createSagaMiddleware();
4.2.4サイガミドレワに登録するsrc/store/index.js
// src/store/index.js
createStore(reducer, applyMiddleware(sagaMiddleware))
4.2.5 sagaを使ってaction非同期を受け入れて操作を実行するsrc/store/sagas/counter.saga.js
// src/store/sagas/counter.saga.js
import {
takeEvery, put, delay } from 'redux-saga/effects'
import {
increment } from '../actions/counter.actions'
import {
INCREMENT_ASYNC } from '../const/counter.const'
// takeEvery action
// put action
function * increment_async_fn (action) {
yield delay(2000) // 2
yield put(increment(action.payload))
}
export default function * counterSaga () {
// action
yield takeEvery(INCREMENT_ASYNC, increment_async_fn) // action
}
src/store/actions/counter.actions.js// src/store/actions/counter.actions.js
// saga
export const increment_async = (payload) => ({
type: INCREMENT_ASYNC, payload });
src/store/const/counter.co nst.js// src/store/const/counter.const.js
export const INCREMENT_ASYNC = 'increment_async'
src/components/Counter.js <button onClick={
() => increment_async(20)}>+</button>
4.2.6サガを起動するsrc/store/index.js
// src/store/index.js
import counterSaga from './sagas/counter.saga'
sagaMiddleware.run(counterSaga);
4.2.7合併sagasrc/store/saga/root.saga.js
// src/store/saga/root.saga.js
import {
all } from 'redux-saga/effects'
import counterSaga from './counter.saga'
import modalSaga from './modal.saga'
export default function * rootSaga () {
yield all([
counterSaga(),
modalSaga()
])
}
modal.saga.jsは変わりません.modal.saga.jsは以下の通りです.src/store/saga/modal.saga.js
// src/store/saga/modal.saga.js
import {
takeEvery, put, delay } from 'redux-saga/effects'
import {
show } from '../actions/modal.actions'
import {
SHOWMODAL_ASYNC } from '../const/modal.const'
// takeEvery action
// put action
function * showModal_async_fn () {
yield delay(2000)
yield put(show())
}
export default function * modalSaga () {
// action
yield takeEvery(SHOWMODAL_ASYNC, showModal_async_fn)
}
store入口ファイルのsagaミドルウェア起動root.sagasrc/store/index.js
// src/store/index.js
import rootSaga from './sagas/root.saga'
sagaMiddleware.run(rootSaga)
4.3 redux-actions4.3.1 redux-actions解決の問題
reduxプロセスの中で大量のサンプルコードは読み书きに苦しみます.redux-actionを使ってアクションとReducerの処理を简略化できます.
4.3.2 redux-actionのダウンロード
npm install redux-actions
4.3.3アクションを作成するimport {
createAction } from 'redux-actions'
const increment_action = createAction('increment');
const decrement_action = createAction('decrement');
4.3.4 Reducerを作成するsrc/store/actions/counter.actions.js
src/store/actions/counter.actions.js
// redux-actions
import {
createAction } from 'redux-actions'
export const increment = createAction('increment')
export const decrement = createAction('decrement')
src/store/reducers/counter.reducer.js// src/store/reducers/counter.reducer.js
import {
handleActions as createReducer } from 'redux-actions'
import {
increment, decrement } from '../actions/counter.actions'
const initialState = {
count: 0,
}
const handleIncrement = (state, action) => ({
count: state.count + action.payload
})
const handleDecrement = (state, action) => ({
count: state.count - action.payload
})
export default createReducer({
[increment]: handleIncrement,
[decrement]: handleDecrement,
}, initialState)
コンポーネント使用:src/components/Counter.js// src/components/Counter.js
function Counter ({
count, increment, decrement}) {
return (
<div>
<button onClick={
() => decrement(1)}>-</button>
<span>{
count}</span>
<button onClick={
() => increment(1)}>+</button>
</div>
)
}
redux-actionsはredux-sagaにも結合して使用できます.