這是一篇比較全面講解 React 的文章,裏面不少基礎知識但願你本身一邊查閱資料一邊學習。全文從業務開發中最經常使用見 loading 效果不一樣是實現講起,說下如今前端開發在業務上應該有的思考。html
最簡單的實現,咱們在 Loading 組件內部聲明一個狀態,經過代碼邏輯判斷 loading 效果的展現。前端
export default class extends Component { ... render() { return this.state.loading ? <div className="loader" /> : <div>finish</div>; } }
完整演示react
隨着業務的發展,這個 Loading 組件用到的地方會很是多,上面這個代碼耦合了不少邏輯,爲了讓這個組件可以很好的複用,那咱們抽離出組件的業務邏輯,將內部狀態進行提高,那這個組件就是一個能被複用的 UI 組件。git
export default function(props) { return props.loading ? <div className="loader" /> : <div>finish</div>; }
完整演示github
注:上面兩段代碼你可能會想,爲何 Func
和 Class
都能實現一個組件,他們有什麼差異嗎?
其實你在開發時不容易感受到差異,但 React 自己是進行了不少差異處理,若是是 Class 類,React 會用 new
關鍵字實例化,而後調用該實例的 render
方法,若是是 Func 函數,React 會直接調用它。編程
若是你是一個 jQuery 轉型 React 的開發,會很天然的想到,我找到 Loading 組件的節點,控制他的顯示與隱藏,固然這也是能夠的,React 提供 Refs 方便你訪問 DOM 節點 或 React 元素。redux
export default class extends Component { componentDidMount() { fetch().then(() => { this.el.changeLoading(false); }); } render() { return ( <Loading ref={el => { this.el = el; }} /> ); } }
完整演示前端框架
當你的應用作到必定的複雜度,不一樣的頁面都會有 loading 效果,你確定不但願每一個頁面都重複的書寫同樣的邏輯,這樣會致使你的代碼重複且混亂。框架
React 中有兩個比較常見的解決方案 HOC
和 Render Props
,其實這兩個這兩個概念都是不依賴 React 的。dom
讓咱們暫時忘掉 React,下面我對 HOC
和 Render Props
寫兩個例子,你會發現組件複用是如此簡單。
HOC 其實就是一種裝飾器模式,它接受一個組件做爲參數,而後返回相同的組件,這樣就能夠額外增長一些功能。
const func = () => { console.log("func"); }; const wrap = func => { console.log("wrap"); return func; }; // wrap 邏輯已被複用 wrap(func)();
Render Props 就是咱們給一個函數傳遞一個回調函數作爲參數,該回調函數就能利用外面函數的執行結果作爲參數,執行任何操做。
const func = param => { console.log("func"); }; const wrap = (param, func) => { console.log("wrap"); func(param); }; // wrap 邏輯已被複用 wrap("", func);
相同點:
重用組件邏輯
;回調地獄
。不一樣點:
總的來講,在須要複用組件邏輯的時候,我我的更傾向於 Render Props 的方式。
當你的應用愈來愈大,組件之間交互愈來愈複雜,那整個頁面的數據邏輯將變得難以管理,這時候爲了方便管理應用的狀態,你能夠選擇一些狀態管理工具,例如 Redux、Flux、dva 等。
我不太想談這些數據流框架,由於他們的概念 action
、store
、dispatch
太過於生澀難懂。
現代前端框架 React 和 Vue 其實都是一個套路,經過數據渲染試圖,而後視圖上操做反過來更新數據,從新渲染視圖,刷新頁面。
數據叫作 store
,動做叫作 ation
,觸發行爲叫 dispatch
,而後數據到視圖的渲染由 React/Vue 處理的。
(圖片來自 這裏)
// reducers.js const initialState = { loading: false }; export default function reducer(state = initialState, action) { switch (action.type) { case "CHANGE_LOADING": return { loading: action.payload }; default: return state; } }
當你代碼中有大量的異步操做時,例如 fetch 請求,你確定會想到事件監聽
、回調函數
、發佈/訂閱
。
很好,上一個例子其實就是事件監聽
的處理方式,而後回調函數
的主流的解決方案是 redux-thunk,而發佈/訂閱
的主流解決方案是 saga。
import { takeLatest, put } from "redux-saga/effects"; import fetch from "./fetch"; function* fetchInfo(action) { yield put({ type: "CHANGE_LOADING", payload: true }); yield fetch(); yield put({ type: "CHANGE_LOADING", payload: false }); } export default function* fetchSaga() { yield takeLatest("FETCH_REQUEST", fetchInfo); }
當你耐心看到這裏,我知道你對 React 確定有必定的經驗,如今還能夠作不少,例如把 loading 狀態提高到 Store 的頂部,那整個站點就只有一個 loading 了,而後你還能夠將 fetch 再封裝一個 HOC 修改 loading 狀態,這就是一個相對完美的 loading,其實 React 業務開發均可以用這個套路。
上面 redux 的例子是否是過於複雜
對於簡單的業務,雖然有不少頁面,嵌套層次也很複雜,你固然能夠不用狀態管理工具,你能夠試着使用 Context,它能夠方便你傳遞數據,它其實就是 Render Props 的一種實現。
export default React.createContext({ loading: false, changeLoading: () => {} });
寫到這,靜一下,是否是哪裏作錯了什麼?
個人業務只是想寫個簡單的 loading 效果,卻瞭解了一堆組件生命週期的概念。
Hooks 恰好幫你解決了這樣的問題,Hooks 能容許你經過執行單個函數調用來使用函數中的 React 功能,讓你把面向生命週期編程
變成面向業務邏輯編程
。
import React, { useState, useEffect } from "react"; import Loading from "./Loading/index"; import fetch from "./fetch"; function App() { const [loading, setLoading] = useState(true); useEffect(() => { fetch().then(() => { setLoading(false); }); }, []); return <Loading loading={loading} />; } export default App;
上面對每一個點都作了具體實現,但他們都不是隔離的,你能夠根據你的認知和業務特色總結抽象一套本身的方法論;
多瞭解
、多抽象
、多思考
,練就十八般武藝,遇到問題的時候才能遊刃有餘;
React 如今宣傳的東西愈來愈多,你最好先深刻了解他們,而後用批判的眼光,保持理智,防止本身天天用很新的特性重構你本身的代碼。
When do I know I’m ready for Redux?
文章可隨意轉載,但請保留此 原文連接。很是歡迎有激情的你加入 ES2049 Studio,簡歷請發送至 caijun.hcj(at)alibaba-inc.com 。