Redux基本語法

麵條請不要欺負漢堡發表於2018-05-30

一.Redux

1.什麼是Redux

Redux是專注於狀態管理,單一狀態 ,單向資料流處理.

在Redux中,所有的資料(比如state)被儲存在一個被稱為store的容器中 → 在一個應用程式中只能有一個。store本質上是一個狀態樹,儲存了所有物件的狀態。任何UI元件都可以直接從store訪問特定物件的狀態。要通過本地或遠端元件更改狀態,需要分發一個action。分發在這裡意味著將可執行資訊傳送到store。當一個store接收到一個action,它將把這個action代理給相關的reducer。reducer是一個純函式,它可以檢視之前的狀態,執行一個action並且返回一個新的狀態。簡單的理解:old state  ———》action(行為狀態:type:{接受型別})  ——》reducer 函式(計算state,)——》得到 new  state

import { createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'; 

//3.新建store  compose結合redux devTool的外掛
const store = createStore(chat,applyMiddleware(thunk))

//1.定義常量  action
const MSG_LIST = 'MSG_LIST' //聊天資訊列表
const MSG_RECV = 'MSG_RECV' //接受資訊
const MSG_READ = 'MSG_READ' //未讀資訊

/*2.reducer
函式有2個引數,一個state 一個action
*/
//state的值
const initState = {
	chatmsg:[],//每條聊天的資訊
	users:{},//使用者資訊
	unread:0//未讀資訊列表
}

export function chat(state=initState, action){
	switch(action.type){
		case MSG_LIST:
			return {...state,users:action.payload.users,chatmsg:action.payload.msgs,unread:action.payload.msgs.filter(v=>!v.read&&v.to==action.payload.userid).length}
		case MSG_RECV:
			return {...state,chatmsg:[...state.chatmsg,action.payload],unread:state.unread+n}
		case MSG_READ:
			return {...state,chatmsg:[...state.chatmsg,action.payload]}
		default:
		 	return state
	}
}

//簡單的話  就只要1-3的步驟,不過一般都是還含有action函式   

//1.2  action函式
function msgList(msgs,users,userid){
	return {type:MSG_LIST,payload:{msgs,users,userid}}
}
//1.1  接受請求的後端的函式
export function getMsgList(){
	return (dispatch)=>{
		axios.get('/user/getmsglist')
			.then(res=>{
				if (res.status===200&&res.data.code===0) {
					dispatch(msgList(res.data.msgs,res.data.users))
				}
			})		
	}
}

2.三個基本原則

A.單一資料來源
       整個應用的 state 被儲存在一棵 object tree 中,並且這個 object tree 只存在於唯一一個 store 中。
B.State 是隻讀的
唯一改變 state 的方法就是觸發 action,action 是一個用於描述已發生事件的普通物件。
C.使用純函式來執行修改

為了描述 action 如何改變 state tree ,你需要編寫 reducers。

二、基本概念

1.Store

 

Store 就是儲存資料的地方,你可以把它看成一個容器。整個應用只能有一個 Store。

 

Redux 提供createStore這個函式,用來生成新的Store。

import { createStore } from 'redux';
const store = createStore(fn);

createStore函式接受另一個函式作為引數,返回新生成的 Store 物件

注意:

createStore方法還可以接受第二個引數,表示 State 的最初狀態。這通常是伺服器給出的。

let store = createStore(todoApp, window.STATE_FROM_SERVER)

上面程式碼中,window.STATE_FROM_SERVER就是整個應用的狀態初始值。注意,如果提供了這個引數,它會覆蓋 Reducer 函式的預設初始值。

2. State

 

state是Store物件包含所有資料。如果想得到某個時點的資料,就要對 Store 生成快照。這種時點的資料集合,就叫做 State。
當前時刻的 State,可以通過store.getState()拿到。

 

import { createStore } from 'redux';
const store = createStore(fn);
const state = store.getState();

Redux 規定, 一個 State 對應一個 View。只要 State 相同,View 就相同。你知道 State,就知道 View 是什麼樣,反之亦然。

3.Action

Action是把資料從應用傳到store的有效載荷。它是store資料的唯一來源。一般來說,通過store.dispatch()將action傳到store。

State 的變化,會導致 View 的變化。但是,使用者接觸不到 State,只能接觸到 View。所以,State 的變化必須是 View 導致的。Action 就是 View 發出的通知,表示 State 應該要發生變化了。

Action 是一個物件。其中的type屬性是必須的,表示 Action 的名稱。

const ADD_TODO = 'ADD_TODO'
//Action 的名稱是ADD_TODO,它攜帶的資訊是字串ADD_TODO

const action = {
	type: 'ADD_TODOS',
	payload: 'Learn Redux'
};
//Action 的名稱是ADD_TODOS,它攜帶的資訊是字串Learn Redux。

 

可以這樣理解,Action 描述當前發生的事情。改變 State 的唯一辦法,就是使用 Action。它會運送資料到 Store。其他屬性可以自由設定,社群有一個規範可以參考。

 

4.Action Creator

View 要傳送多少種訊息,就會有多少種 Action。如果都手寫,會很麻煩。可以定義一個函式來生成 Action,這個函式就叫 Action Creator。簡單來說Action 建立函式 就是生成 action 的方法。“action” 和 “action 建立函式” 這兩個概念很容易混在一起,使用時最好注意區分。

在 Redux 中的 action 建立函式只是簡單的返回一個 action:

function addTodo(text) {
  return {
    type: ADD_TODO,
    text
  }
}
const action = addTodo('Learn Redux');
上面程式碼中,addTodo函式就是一個 Action Creator。

5.Reducer

 

Store 收到 Action 以後,必須給出一個新的 State,這樣 View 才會發生變化。這種 State 的計算過程就叫做 Reducer。

 

Reducer 是一個函式,它接受 Action 和當前 State 作為引數,返回一個新的 State。也可以這樣說Reducers 指定了應用狀態的變化如何響應 actions 併傳送到 store 的,記住 actions 只是描述了有事情發生了這一事實,並沒有描述應用如何更新 state。

//通過reducer函式來計算:根據老的state 和action ,生成新的state
const defaultState = 0;
const reducer = (state = defaultState, action) => {
  switch (action.type) {
    case 'ADD':
      return state + action.payload;
    default: 
      return state;
  }
};

const actions = [
	{ type: 'ADD', payload: 0 },
	{ type: 'ADD', payload: 1 },
	{ type: 'ADD', payload: 2 }
];
//1.新的store
const store=createStore(reducer);
//action 建立函式
function addTodo(payload) {
	return {
		type: 'ADD',
		payload
	}
}

//派發事件 傳遞action
store.dispatch(addTodo(2))
console.log(store.getState())//輸出state的值是2

通過上面的程式碼,就可以瞭解到reducer函式就是計算state的過程。

注意:reducer有特別的一點就是純函式。也就是說,只要是同樣的輸入,必定得到同樣的輸出。

純函式是函數語言程式設計的概念,必須遵守以下一些約束。

不得改寫引數
不能呼叫系統 I/O 的API
不能呼叫Date.now()或者Math.random()等不純的方法,因為每次會得到不一樣的結果

由於 Reducer 是純函式,就可以保證同樣的State,必定得到同樣的 View。但也正因為這一點,Reducer 函式裡面不能改變 State,必須返回一個全新的物件.

請參考:Reducer 最佳實踐,Redux 開發最重要的部分

Redux Reducer 的拆分

三.API

(一).Store API

1.store.getState()

store.getState()就是獲取當前state的值

//派發事件 傳遞action
store.dispatch(addTodo(2))
console.log(store.getState())//輸出state的值是2

2.store.dispatch()

store.dispatch()是 View 發出 Action 的唯一方法。store.dispatch接受一個 Action 物件作為引數,將它傳送出去

function counter(state=0,action){
	switch(action.type){
		case '加機關槍':
			return state+1
		case '減機關槍':
			return state-1
		default:
			return 10
	}
}

//1.新的store
const store=createStore(counter);
//action 建立函式
function addTodo(text) {
  return {
    type: '加機關槍',
    text
  }
}

//派發事件 傳遞action
store.dispatch(addTodo('666'))
console.log(store.getState())//輸出state的值是11

3.store.subscribe()

Store 允許使用store.subscribe方法設定監聽函式,一旦 State 發生變化,就自動執行這個函式。

function counter(state=0,action){
	switch(action.type){
		case '加機關槍':
			return state+1
		case '減機關槍':
			return state-1
		default:
			return 10
	}
}
//1.新的store
const store=createStore(counter)

//通過store 獲取狀態
const init =store.getState();
console.log(init)
function Listener(){
	const current = store.getState();
	console.log(`現在有機槍${current}把`)
}
//隨時監聽state的變化
store.subscribe(Listener);


//派發事件 傳遞action
store.dispatch({type:'加機關槍'})

store.dispatch({type:'加機關槍'})

顯然,只要把 View 的更新函式(對於 React 專案,就是元件的render方法或setState方法)放入listen,就會實現 View 的自動渲染。

store.subscribe方法返回一個函式,呼叫這個函式就可以解除監聽。

let unsubscribe = store.subscribe(() =>
  console.log(store.getState())
);

unsubscribe();

4.replaceReducer(nextReducer)

替換 store 當前用來計算 state 的 reducer。這是一個高階 API。只有在你需要實現程式碼分隔,而且需要立即載入一些 reducer 的時候才可能會用到它。在實現 Redux 熱載入機制的時候也可能會用到。 

四.Redux如何和React一起使用

例子:對機關槍的管理,這裡上交(減少)和申請(增加機關槍)管理

Redux如何和React一起使用,具體操作:把store.dispatch方法傳遞給元件,內部可以呼叫修改狀態,subscribe訂閱render函式,每次修改都重新渲染,redux相關內容,一道單獨的檔案index.redux.js單端管理

1.首先建立action  對機關槍的操作  統一建立在index.redux.js

//6.通過傳送過來的action 找到對應的type  就知道是 reducer 的counter函式時加1還是減1
//就得到新的store

//action
const ADD_GUN='加機關槍';
const REMOVE_GUN='減機關槍';

//reduer
export function counter(state=0,action){
	switch(action.type){
		case '加機關槍':
			return state+1
		case '減機關槍':
			return state-1
		default:
			return 10
	}
}

//action creator
export  function addGUN(){
	return {type:ADD_GUN}
}
export  function removeGUN(){
	return {type:REMOVE_GUN}
}

2.要引入模板 建立store   index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { createStore } from 'redux'; 
import {counter,addGUN,removeGUN} from './index.redux';

//1.新建store
const store = createStore(counter);


function render() {
	//reactDOM 渲染頁面
   //2.這個store 以在元件屬性props傳給Component 元件
	ReactDOM.render(<App store={store} addGUN={addGUN} removeGUN={removeGUN} />, document.getElementById('root'));
	registerServiceWorker();
}
render();
//3.以subscribe訂閱render的函式 這樣我們可以知道state的變化
store.subscribe(render);

Store 的監聽函式設定為render,每次 State 的變化都會導致網頁重新渲染。

3.元件頁面的操作 App.js

import React from 'react';

class App extends React.Component {
	render() {
		//4.通過元件的屬性props 獲取store以及資料操作addGUN
		//5.在通過dispatch(addGUN()) 傳送action
		const store=this.props.store;
		const num =store.getState();
		const addGUN=this.props.addGUN;
		const removeGUN=this.props.removeGUN;
		return (
			<div>
				<h1>現在有機槍{num}把</h1>
				<button onClick={()=>store.dispatch(addGUN())}>申請武器</button>
				<button onClick={()=>store.dispatch(removeGUN())}>上交武器</button>
			</div>
		);
	}
}

export default App;

參考:Redux 入門教程(一):基本用法

Redux.自述

通過上面的學習得到自己的理解:

建立action(多個型別)——》建立reducer(計算每個型別下的state的值)——》通過這個reducer函式  得到新的值state( const store=createStore(reducer) const state = store.getState();)——》把這個值以props屬性傳給元件——》元件接受新的值——》同時釋出(dispatch)該元件的acton型別——》通過元件分派的action得到是reducer那種型別下的計算方法——》得到值

如果有理解錯誤,請大家指出來。