【React自制全家桶】九、Redux入手
阿新 • • 發佈:2019-01-09
一、React專案中為什麼要用Redux
上圖:
左圖當使用純React開發稍微大點的專案,因為React資料是瀑布式的,只能通過父子元件傳遞資料,所以實現關係不大的兩React的元件之間的資料傳遞就會讓你非常難受,操作起來非常複雜。如右圖,由此應運而生了Redux資料流框架,專門解決資料流問題,
Redux的基本原理就是React的所有元件涉及到的資料全部都儲存在共用的Store中,這樣所有元件都可以通過Store來改動資料和獲取資料,非常方便。
二、Redux安裝
在已經安裝yarn的前提下
yarn add redux
三、Redux工作流講解
先上圖:
為了方便理解,我將一個redux的操作流程視為從圖書館取書的流程:
1、React Components(借書人)
2、說借一本《React全棧》書(Action Creations就是代表需要進行的操作)
3、Store(圖書館管理員)聽到了這句話
4、從reducers(記書本)中查詢書在哪放著
5、Store(圖書館管理員)查到(即reducers返回newState)
6、Store(圖書館管理員)將書交給React Components(借書人)(即:將改動後的state發給React Components(借書人))
四、Redux使用入門
1、使用谷歌瀏覽器下載瀏覽器rudux外掛(建議開啟科學上網後下載安裝)
2、在src目錄下建立store資料夾
3、在store資料夾下建立index.js檔案作為redux的入口檔案
import {createStore} from 'redux'; // 引入reducer import reducer from './reducer'; const store = createStore( reducer, // 顯示redux除錯工具 window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() ); export default store;
4、在store資料夾下建立reducer.js檔案作為資料
import {CHANGE_INPUT_VALUE , ADD_TODO_ITEM , DELETE_TODO_ITEM} from './actionTypes';
const defaultState = {
inputValue : "",
list: []
};
//reducers可以接收state但是絕不能修改state
//reducers不能有非同步操作也不能有與時間相關的操作,也不能對接收的引數進行修改
//返回純函式,純函式指的是:給定固定輸入,則輸出固定,而且不會有任何副作用
export default (state = defaultState,action) => {
// state是上一個store中儲存的資料,action是使用者發過來的操作請求
// 1、JSON.parse()用於從一個字串中解析出json物件
// 2、JSON.stringify()用於從一個物件解析出字串
if (action.type === CHANGE_INPUT_VALUE) {
// state深拷貝
const newState = JSON.parse(JSON.stringify(state));
newState.inputValue = action.value;
return newState;
}
if (action.type === ADD_TODO_ITEM) {
// state深拷貝
const newState = JSON.parse(JSON.stringify(state));
newState.list.push(newState.inputValue);
newState.inputValue = '';
return newState;
}
if (action.type === DELETE_TODO_ITEM) {
// state深拷貝
const newState = JSON.parse(JSON.stringify(state));
newState.list.splice(action.index, 1);
return newState;
}
return state;
}
5、核心元件中匯入store
import React,{Component} from 'react';
import 'antd/dist/antd.css';
import {Input, Button, List} from 'antd';
import store from './store/index';
import {getInputChangeAction, getAddItemAction, getDeleteItemAction} from './store/actionCreators'
// import {CHANGE_INPUT_VALUE , ADD_TODO_ITEM , DELETE_TODO_ITEM} from './store/actionTypes';
class TodoList extends Component{
constructor(props){
super(props);
this.state = store.getState();
this.handleInputChange = this.handleInputChange.bind(this);
this.handleStoreChange = this.handleStoreChange.bind(this);
this.handleBtnClick = this.handleBtnClick.bind(this);
// 監聽store中資料的變化,改變則通過handleStoreChange()函式重新渲染
store.subscribe(this.handleStoreChange)
}
render(){
return(
<div style={{margin:'30px'}}>
<div>
<Input
type="text"
value={this.state.inputValue}
placeholder='todo info'
style={{width:'300px',marginRight:'10px'}}
onChange={this.handleInputChange}
/>
<Button type="primary" onClick={this.handleBtnClick}>提交</Button>
</div>
<div>
<List
style={{width:'300px',marginTop:'10px'}}
bordered
// 資料來源
dataSource={this.state.list}
renderItem={(item,index) => (<List.Item onClick={this.handleItemDelete.bind(this,index)}>{item}</List.Item>)}
/>
</div>
</div>
)
}
// 輸入框改變
handleInputChange(e){
const action = getInputChangeAction(e.target.value);
// 將action派發給store
store.dispatch(action);
}
// 資料重新獲取並渲染
handleStoreChange(){
// store.getState()從store中重新取資料
// this.setState()更新資料
this.setState(store.getState())
}
// 點選後增添資料
handleBtnClick(){
// 建立action物件
const action = getAddItemAction();
// 將action發給store
store.dispatch(action);
}
handleItemDelete(index){
const action = getDeleteItemAction(index);
store.dispatch(action);
}
}
export default TodoList;
6、升級——store中建立actionCreators
import {ADD_TODO_ITEM, CHANGE_INPUT_VALUE, DELETE_TODO_ITEM} from "./actionTypes";
export const getInputChangeAction = (value) => ({
type : CHANGE_INPUT_VALUE,
value
});
export const getAddItemAction = () => ({
type : ADD_TODO_ITEM
});
export const getDeleteItemAction = (index) => ({
type : DELETE_TODO_ITEM,
index
});
7、升級——store中建立actionTypes
export const CHANGE_INPUT_VALUE = 'change_input_value';
export const ADD_TODO_ITEM = 'add_todo_item';
export const DELETE_TODO_ITEM = 'delete_todo_item';