業務當中,經常存在組件之間數據共用的問題,由於組件層級關係可能很複雜,經過props進行傳遞將會極其的繁瑣。react
爲了解決這個問題,提供了一個context
方便實現數據共享,使用方式也極其的簡便。react
const {Provider, Consumer} = React.createContext('nihao');
const demo = value=>{
return <div>{value}</div>
}
// 方式1
<Provider value="hello world">
// ... 若干層級或者交叉,這裏展現 hello world,記住這裏按照上一章說的props render的寫法
<Consumer>
{demo}
</Consumer>
</Provider>
// 方式2
// ...父層級,最頂層沒有使用Provider會使用默認值展現 'nihao'
<Consumer>
{demo}
</Consumer>
// 爲了使得數據能得到相應而且使得視圖刷新我們能夠
const {Provider, Consumer} = React.createContext();
class Pvd extends React.Component {
constructor(props) {
super(props);
this.state = {
hello: 'hello',
};
}
this.setHello = hello => this.setState({ hello });
render(){
return <Provider value={{hello: this.state.hello, setHello: this.setHello}}>
// ... 若干層級或者交叉,這裏展現 hello world,記住這裏按照上一章說的props render的寫法
{this.props.children}
</Provider>
}
}
const App = (props)=>{
const ConsumerU = props.context.Consumer || Consumer;
return <Pvd>
<ConsumerU>
{
props=>{
return <div onClick={()=>props.setHello(Math.random())}>{props.data}</div>
}
}
</ConsumerU>
</Pvd>
}
複製代碼
實際spa
項目中,公用數據模型每每比較複雜,使用上面那種方式很容易邏輯混亂,爲了使其可以便於管理,經常會引入redux
。經過閱讀redux源碼發現,他是典型的以訂閱發佈模式設計思路,用純函數處理數據流。主要有如下一些概念:git
使用思路:github
做爲redux
處理異步數據流的中間件之一,saga 利用 generator 能夠屢次返回以及內部掛起機制的特性(具體就不在這展開了),更好的處理異步數據流,下面用一個實例來展現他們的用法。ps:爲何不用thunk,由於簡單的使人髮指,逼格徹底不夠由於thunk的設計思想是dispatch非標準格式的 action,並且實際數據交互仍是放在業務邏輯(這個是重點)。typescript
import {combineReducers, createStore, applyMiddleware, Dispatch, Middleware, AnyAction, MiddlewareAPI} from 'redux';
import createSagaMiddleware from 'redux-saga';
import root from './saga';
// 兩個數據模型
import user from './model/User';
// 頁面控制模型
import pageCommonConfig from './model/PageCommonConfig';
// log中間件
const logMiddleWare:Middleware = (store: MiddlewareAPI)=>(next: Dispatch<AnyAction>)=>(action:any)=>{
console.log('log:', action);
return next(action);
}
// 調用 redux-saga 包中的 createSagaMiddleware ,建立一個sageMiddleware
const sageMiddleware = createSagaMiddleware();
export default createStore(
combineReducers({ user , pageCommonConfig}),
applyMiddleware(
logMiddleWare,
sageMiddleware
)
);
sageMiddleware.run(root);
// root 定義
import { all, fork, CallEffectFn } from 'redux-saga/effects';
import * as User from './User';
// import * as Goods from './Goods';
var forkArray:any[] = [];
// fork 是非阻塞試監聽的數組,由於咱們須要同時監聽多個action
const makeItertorArrayFrok = (arr: CallEffectFn<any>[])=>arr.map(item=>fork(item));
const geratorForkArray = (...arg: {}[])=>{
arg.forEach(item=>{
forkArray = forkArray.concat(makeItertorArrayFrok(Object.values(item)));
})
}
// 把全部要監聽的 generator 對象都包裝到一個數組
geratorForkArray(User);
export default function* root(){
try {
// 經過 all 啓動對全部 generator 的監聽。
yield all(forkArray);
} catch(e){
console.log(e);
}
}
// User Address 的 CRUD
import { setUserCredit, setUserAddress, setAddressCodeTable, addOrEditUserAddress } from '@store/model/User/actions';
import { getUserCredit, getUserAddress , deleteUserAddress, modiUserAddress, getAddressTableCode} from '@api/user';
import { modiAddressItem as modiAddressI } from './actions';
import { take, call, put, takeEvery, select} from "redux-saga/effects";
// 刪除警告
function showDeleteAlert(title= '警告', tips= '你肯定要刪除當前地址嗎?'){
return new Promise((resolve,reject)=>{
const alertInstance = alert(title, tips, [
{ text: '取消', onPress: () => {reject();alertInstance.close()}, style: 'default' },
{ text: '肯定', onPress: () => {resolve();console.log('肯定')} },
]);
})
};
// take 用於監聽 action
// call 用於調用函數,該函數應該是個 generator 函數或者返回一個Promise。
export function* queryAddress(){
try{
// take 只監聽 type 爲 queryAddress 的 action 一次。
// 由於查找地址只用調用一次,進行基礎的數據構建便可。
yield take('queryAddress');
// 當有這個 action 被派發時往下走去請求數據
const res = yield call(getUserAddress);
// 拿到結果往 reducer 層拋
yield put(setUserAddress(res));
} catch (e) {
}
}
export function* deleteAddressItem(){
// takeEvery 監聽屢次,接收一個 generator 函數做爲參數
yield takeEvery('deleteAddressItem',function*(action: AnyAction){
try{
// 刪除警告,等待響應
yield call(showDeleteAlert)
// select 用於查詢 state 裏的值,接收一個方法。而後把值拷貝出來,由於是引用類型,不要改變原有結構。
const address: Address[] = [...yield select((state:MallStoreState)=>state.user.address)];
// 找
let result = address.findIndex(val=>val.addressId === action.id);
// 找到
if(result !== -1){
// 默認地址不讓刪
if(address[result].isDefaultAddress == "Y")return Toast.fail('默認地址不能刪除', 1); // 服務器刪除
yield call(deleteUserAddress, address[result].addressId, address[result].channel);
}
// 本地刪除
if(address){
address.splice(result,1);
yield put(setUserAddress(address));
Toast.success('刪除成功', 1);
}
} catch (e) {
}
});
}
// 修改默認地址
export function* modiAddressItemDefault(){
// 不解釋
yield takeEvery('modiAddressItemDefault',function*(action: AnyAction){
try{
// 不解釋+1
const address: Address = yield select((state:MallStoreState)=>(state.user.address as Address[]).find(val=>val.addressId === action.id));
if(address.isDefaultAddress === "N") {
// 它還能夠拋給本身就是往下拋不拋到reducer層
yield put(modiAddressI(Object.assign({}, address, {isDefaultAddress : 'Y'})));
}
} catch (e) {
console.log(e);
}
})
}
// 不解釋+2
export function* modiAddressItem(){
yield takeEvery('modiAddressItem',function*(action: AnyAction){
try{
const addressId = yield call(modiUserAddress, action.address);
yield put(addOrEditUserAddress(Object.assign(action.address, {addressId: +addressId})));
} catch (e) {
}
});
}
複製代碼
react
組件公共數據管理講完了,redux
講完了。要將 redux
和 react
串聯,咱們通常會用到 react-redux
。根據咱們常過的 `react-redux 方法和組件,咱們嘗試着理解一下他們的構造思路。redux
react
組件,組件參數接收一個 redux
的 store
。context.Provider
中的 ,接收的確定是 store.getState
的值。state
的調整能做用於組件,組件內部確定會有一系列的針對 store
的 subscribe
。subscribe
。mapStateToProps
方法,接收一個 state
做爲參數,返回一個 state
的一個映射集合mapDispatchToProps
方法,接收一個 dispath
做爲參數,返回一個dispatch
相應action
的集合Component
的函數,將上面兩個方法生產的結果做爲props
傳遞給Componet
。最終返回一個高階組件,可能比較抽象,這裏大概寫一下。// 爲了讓 mapStateToProps mapDispatchToProps 能拿到 state 和 dispatch咱們須要藉助一個Consumer
const connect = (mapStateToProps, mapDispatchToProps)=>{
return WarpComponent=>{
// 內部閉包
return props=>{
const Consumer = props.context.Consumer;
return <Consumer>
{
store=>{
return <WarpComponent
...props
...mapDispatchToProps(store.dispatch)
...mapStateToProps(store.state)/>
}
}
}
</Consumer>
}
}
}
複製代碼
實際源碼思路和這個也是差很少的,只不過多了不少數據的normalize處理。這裏就不展開詳述了。api
that's all tk.數組