React (5) Redux 進階
阿新 • • 發佈:2019-01-07
1. UI 元件
- render()函式放在另外一個單獨的UI.js 檔案和動作行為分開
- UI 上面所用的資料和函式, 通過父元件傳遞過來,用 this.props 來接收
- UI 元件下面:
import 'antd/dist/antd.css'; import { Input, Button, List } from 'antd'; import React, {Component} from 'react'; class TodoListUI extends Component { render(){ return ( <div style={{ margin: '10px' }}> <div> <Input value={this.props.inputValue} style={{ width: '300px', marginRight: '10px' }} onChange={this.props.handleInputChange} /> <Button type="primary" onClick={this.props.btnClick} >提交</Button> </div> <List bordered dataSource={this.props.list} renderItem={(item, index) => (<List.Item onClick={(index)=>{this.props.handledelete(index)}}>{item}</List.Item>)} style={{marginTop:'10px',width:'300px'}} /> </div>) } } export default TodoListUI
父元件傳遞資料
render() { return ( <TodoListUI inputValue={this.state.inputValue} handleInputChange={this.handleInputChange} btnClick={this.btnClick} list={this.state.list} handledelete={this.handledelete} /> ) }
2. 無狀態元件
- 當一個普通的元件只有render()函式的時候, 沒有業務邏輯的時候它就是一個無狀態元件, 類似沒有業務邏輯的UI元件
- 無狀態元件效能比較高, 因為它就是一個js, 一個普通元件既包含一個類還有render(), 效能差別就在這
- 下面是一個無狀態元件
import 'antd/dist/antd.css'; import { Input, Button, List } from 'antd'; import React from 'react'; const TodoListUI = (props) => { return ( <div style={{ margin: '10px' }}> <div> <Input value={props.inputValue} style={{ width: '300px', marginRight: '10px' }} onChange={props.handleInputChange} /> <Button type="primary" onClick={props.btnClick} >提交</Button> </div> <List bordered dataSource={props.list} renderItem={(item, index) => (<List.Item onClick={(index) => { props.handledelete(index) }}>{item}</List.Item>)} style={{ marginTop: '10px', width: '300px' }} /> </div> ) } export default TodoListUI
3. Redux 傳送非同步請求 (使用redux-thunk)
- 使用 axios
- 使用 redux-thunk 將非同步函式寫在redux/動作檔案裡面, 不用redux-thunk的話可以將ajax 回撥放在元件的生命週期函式裡面
但是,隨著元件的內容增多, 元件可能相對的比較複雜, 所以建議還是使用中介軟體
生命週期函式直接提交函式
componentDidMount(){
const action = getTodoList();
store.dispatch(action); // 當呼叫這個函式時, 非同步函式會自動執行
}
redux/action檔案函式
export const getTodoList = () => {
return (dispatch) => {
axios.get('/list.json').then((res)=>{
const data = res.data;
const action = initListAction(data);
dispatch(action);
})
}
};
store檔案
import {
createStore,
applyMiddleware,
compose
} from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducer';
const composeEnhancers =
typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// Specify extension’s options like name, actionsBlacklist, actionsCreators, serialize...
}) : compose;
const enhancer = composeEnhancers(
applyMiddleware(thunk),
// other store enhancers if any
);
const store = createStore(reducer,enhancer);
export default store;
ps : 如果不適用 redux-thunk, 直接在生命週期函式中使用(但是不建議)
componentDidMount(){
axios.get('/list.json').then((res)=>{
const data = res.data;
const action = initListAction(data);
dispatch(action);
})
}
4. Redux 中介軟體
一般情況 : 在action通過 dispatch這個方法, 把物件傳送給store
使用中介軟體: 中介軟體簡單來說就是, 對store的dispatch方法進行升級, 允許傳過來一個函式,而不是隻能是物件. 在傳過來的時候, 對傳過來的action進行判斷, 如果是函式,則先執行函式,再進行下一步的操作, 就好像這樣,傳遞過來的函式立即執行, 這個函式預設引數是dispatch , 然後通過 dispatch 提交下一個action動作
使用的是redux-thunk中介軟體
export const getTodoList = () => {
return (dispatch) => {
axios.get('/list.json').then((res)=>{
const data = res.data;
const action = initListAction(data);
dispatch(action);
})
}
};
ps : 只有redux才有中介軟體, 而不是react
5. Redux-saga 的基本使用
1. store/ index 下配置 (github上面有詳細的配置)
import {
createStore,
applyMiddleware,
compose
} from 'redux';
import createSagaMiddleware from 'redux-saga'
import reducer from './reducer';
import mySaga from './sagas'
const sagaMiddleware = createSagaMiddleware();
const composeEnhancers =
typeof window === 'object' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;
const enhancer = composeEnhancers(
applyMiddleware(sagaMiddleware)
);
const store = createStore(reducer,enhancer);
sagaMiddleware.run(mySaga);
export default store;
2.actionCreators.js 建立行為型別
export const getInitList = () => ({
type: GET_INIT_LIST,
});
3. 執行步驟, 元件周期函式派發action
componentDidMount(){
const action = getInitList();
store.dispatch(action)
}
4.sagas.js 接收action判斷行為型別 (使用方法詳見github)
saga就是元件派發action後, 攔截下來, 優先處理,該型別的函式,
但是與thunk不同的是, saga內建了一些方法可以處理非同步函式
import {takeEvery, put} from 'redux-saga/effects';
import {GET_INIT_LIST} from './actionTypes';
import {initListAction} from './actionCreators';
import axios from 'axios';
function* getInitList() {
try{ // 成功的時候
const res = yield axios.get('/list.json');
const action = initListAction(res.data);
yield put(action)
}catch(e){
console.log('list.json 網路請求失敗')
}
}
// generator 函式
function* mySaga() {
yield takeEvery(GET_INIT_LIST, getInitList); // 判斷action型別,處理
}
export default mySaga;
5.saga 內建 put 方法繼續提交action給reducer執行後面的步驟, 後面的就是redux常規的步驟
6. React-Redux
1. 檔案格式
2. index.js 入口檔案
import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList';
import {Provider} from 'react-redux';
import store from './store';
// 這個標籤裡面的所有元件都可以使用store裡面的資料
// Provider 這元件可以把store提供給它含的所有元件
const App=(
<Provider store={store}>
<TodoList/>
</Provider>
)
// JSX 語法
ReactDOM.render(App, document.getElementById('root'));
3. 元件
import React from 'react';
// import store from './store';
import { connect } from 'react-redux';
const TodoList =(props) => { // 無狀態元件, 效能更佳
const {inputValue,list,changeInputValue,putChange,delClick} = props
return (
<div>
<div>
<input value={inputValue} onChange={changeInputValue} />
<button onClick={putChange}>提交</button>
</div>
<ul>
{list.map((item, index) => {
return <li key={index} onClick={delClick.bind(this, index)}>{item}</li>
})}
</ul>
</div>
)
}
// mapStateToProps 箭頭函式裡面的state是和store裡面的state對應的
const mapStateToProps = (state) => {
return {
inputValue: state.inputValue,
list:state.list
}
}
// mapDispatchToProps 箭頭函式裡面的state是和store裡面的state對應的
// store.dispatch, props
const mapDispatchToProps = (dispatch) => {
return {
changeInputValue(e) {
const action = {
type: 'change_input_value',
value: e.target.value
}
dispatch(action);
},
putChange() {
const action = {
type: 'put_change'
}
dispatch(action)
},
delClick(index){
const action = {
type: 'del_click',
index: index
}
dispatch(action)
}
}
}
// connect(null, null) (TodoList)
// 這個語法的意思是, 和store連結
// 第一個引數是state對映
// 第二個引數是action對映
export default connect(mapStateToProps, mapDispatchToProps)(TodoList);
4. store/index.js
import {createStore} from 'redux';
import reducer from './reducer';
const store = createStore(reducer);
export default store;
5. store/reducer.js
const defaultState ={
inputValue : 'hello',
list : []
}
export default (state=defaultState,action) =>{
if(action.type === 'change_input_value'){
const newState = JSON.parse(JSON.stringify(state)); // 深拷貝
newState.inputValue = action.value;
return newState;
}
if(action.type==='put_change'){
const newState = JSON.parse(JSON.stringify(state));
newState.list.push(newState.inputValue);
newState.inputValue = '';
return newState;
}
if(action.type ==='del_click'){
const newState = JSON.parse(JSON.stringify(state));
newState.list.splice(action.index,1);
return newState;
}
return state;
}