Hook
是React 16.8
的新特性,它可讓在不編寫class
類組件的狀況下使用state
以及其餘的React
特性;而Context
是React16.3
版本里面引入新的Context API
,在以往React
版本中存在一個Context API
,那是一個幕後試驗性功能,官方提議避免使用,Redux
的原理就是創建在舊的Context API
。如今新的Context ApI
提供了一個無需爲每層組件手動添加 props,就能在組件樹間進行數據傳遞的方法,爲數據通信另闢蹊徑。javascript
爲解決多層嵌套不一樣層級組件之間props
數據傳遞,這種數據傳遞及其繁雜,並且後期不易進行維護,爲避免driling
式數據通信,能夠採用redux
進行數據通信。在新版本React 16.8.6
中Context
爲咱們帶來新的通信方式。css
Context API
組成部分React.createContext
函數:建立context
上下文,參數是一個默認值(須要傳遞state
數據),state
能夠是Object、Array
或者基本類型數據。html
Provider
:由React.createContext
建立返回對象的屬性。在Redux vs. The React Context API中比喻成構建組件樹中的電子總線
比較形象。java
Consumer
:由React.createContext
建立返回對象的屬性。比喻接入電子總線
獲取數據。react
Context
vs redux
Context
的context.Provider/Context.Consumer
和redux
的provider/connect
很是類似。Context
採用的是生產者消費者的模式,咱們能夠利用高階函數(Hoc
)模擬實現一個redux
。git
redux
是經過dispatch
一個action
去修改store
數據;在React 16.8.6
版本的React hooks
提供的useredcuers
和useContext
爲咱們更方便經過Context+hooks
的形式去打造一個屬於本身redux
github
Context
簡單例子
Context
設計目的是爲了共享那些對於一個組件樹而言是「全局」的數據,例如當前認證的用戶、主題或首選語言。redux
Class.contextType
掛載在class
上的 contextType
屬性會被重賦值爲一個由 React.createContext()
建立的 Context
對象。這能讓你使用 this.context
來消費最近 Context
上的那個值。你能夠在任何生命週期中訪問到它,包括render
函數中。api
Context.Consumer
讓你在函數式組件中完成訂閱 context
。這須要函數做爲子元素(function as a child)這種作法。這個函數接收當前的 context
值,返回一個 React
節點。傳遞給函數的value
值等同於往上組件樹離這個 context
最近的Provider
提供的 value
值。若是沒有對應的 Provider
,value
參數等同於傳遞給createContext()
的 defaultValue
。數組
// Context 可讓咱們無須明確地傳遍每個組件,就能將值深刻傳遞進組件樹。
// 爲當前的 theme 建立一個 context(「light」爲默認值)。
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// 使用一個 Provider 來將當前的 theme 傳遞給如下的組件樹。
// 不管多深,任何組件都能讀取這個值。
// 在這個例子中,咱們將 「dark」 做爲當前的值傳遞下去。
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// 中間的組件不再必指明往下傳遞 theme 了。
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// 指定 contextType 讀取當前的 theme context。
// React 會往上找到最近的 theme Provider,而後使用它的值。
// 在這個例子中,當前的 theme 值爲 「dark」。
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
// 也能夠按照這種形式獲取
function ThemedButton(){
return (
<ThemeContext.Counsumer>
{theme =>(
<Button theme={theme} />
)
}
</ThemeContext.Counsumer>
);
}
複製代碼
context
的詳細用法能夠參考 Context文檔
React Hooks
是React 16.8.6
版本爲函數式組件添加了在各生命週期中獲取state
和props
的通道。可以讓您在不編寫類的狀況下使用 state(狀態) 和其餘 React 功能。再也不須要寫class
組件,你的全部組件都將是Function
。若是想了解更多關於React hooks
信息能夠參考Hooks API 參考。
useState
:獲取組件state
狀態數據,第一個參數是保存的數據,第二參數是操做數據的方法,相似於setState
。可用ES6
的數組解構賦值來進行獲取。useEffect
: 網絡請求、訂閱某個模塊、DOM
操做都是反作用,useEffect
是專門用來處理反作用的。在class
類組件中,componentDidMount
和componentDidUpdate
生命週期函數是用來處理反作用的。useContext
:useContext
能夠很方便去訂閱context
的改變,並在合適的時候重渲染組件。例如上面的函數式組件中,經過Consumer
的形式獲取Context
的數據,有了useContext
能夠改寫成下面:function ThemedButton(){
const value = useContext(ThemeContxet);
return (
<Button theme={value} /> ); } 複製代碼
若是習慣了redux
經過reducer
改變state
或者props
的形式,應該比較很好上手useReducers
,useReducers
和useContext
是這篇文章比較重點的API
。
useReducers
:useReducers
能夠傳入三個參數,第一個是自定義reducer
,第二參數是初始化默認值,第三個參數是一個函數,接受第二個參數進行計算獲取默認值(可選)。const [state,dispatch] = useReducer(reducer,initialValue)
複製代碼
下面是useReducers
官方示例:
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'reset':
return initialState;
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
// A reducer must always return a valid state.
// Alternatively you can throw an error if an invalid action is dispatched.
return state;
}
}
function Counter({initialCount}) {
const [state, dispatch] = useReducer(reducer, {count: initialCount});
return (
<> Count: {state.count} <button onClick={() => dispatch({type: 'reset'})}> Reset </button> <button onClick={() => dispatch({type: 'increment'})}>+</button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> </> ); } 複製代碼
redux
具備Provider
組件,經過store
進行傳值。在這裏,咱們使用Context
模擬實現Provider
和store
傳值,完整代碼能夠參考 simple-redux。
Provider
組件代碼中的storeContext
是React.createContext()
函數建立的Context對象。this.props.store
是模擬經過store
傳值操做。
import React,{Component} from 'react';
import {storeContext} from './store';
export default class Provider extends Component{
render(){
return (
<storeContext.Provider value={this.props.store}> {this.props.children} </storeContext.Provider> ) } } 複製代碼
store
文件,包括reducer
、Context
的建立,initialState
與reducers
的定義。
import React from 'react';
export const storeContext = React.createContext();
export const initialState = {
user:'kiwis',
age:23
}
export const reducer = (state, action)=>{
switch (action.type) {
case 'CHANGENAME':
return {user:'harhao',age:24}
default:
return initialState;
}
}
複製代碼
在根組件App.js
中,使用React hooks
d的useReducer
鉤子函數,返回更改state
的dispatch
函數。而後把store
數據和dispatch
傳遞進封裝的Provider
組件中。
import React,{useReducer} from 'react';
import Provider from './views/Provider';
import Child from './views/child';
import {initialState as store,reducer} from './views/store';
import './App.css';
function App() {
const [state,dispatch] = useReducer(reducer,store);
return (
<div className="App"> <Provider store={{state,dispatch}}> <Child/> </Provider> </div>
);
}
export default App;
複製代碼
在useConext
外包裹一層函數,更好模擬connect
語法。這裏採用自定義React hooks
的方法。定義一個自定hook
函數useConnect
,以下所示:
import {useConext} from 'react';
export default function useConnect(props){
return useContext(props);
}
複製代碼
在App.js
的子組件Child
中,在redux
中經過connect
高階函數來傳遞數據。這裏可使用自定義React hooks
函數useConnect
獲取state
和dispatch
。
import React,{useContext} from 'react';
import useConnect from './connect';
import {storeContext} from './store';
import DeepChild from './deepChild';
function Child() {
const {state,dispatch}= useConnect(storeContext);
return (
<div className="child"> <p>姓名:{state.user}</p> <p>年齡:{state.age}</p> <button onClick={()=>dispatch({type:'CHANGENAME'})}>changeName</button> <p>deep child:</p> <DeepChild/> </div>
);
}
export default Child;
複製代碼
在Child
子組件中,引入DeepChild
組件。經過useContext
獲取頂層最近的state
數據。
import React,{useContext} from 'react';
import {storeContext} from './store';
import useConnect from './connect';
export default function DeepChild(){
const {state} = useConnect(storeContext);
return (
<div> {state.user} </div>
)
}
複製代碼
child
子組件和DeepChild
孫組件經過useConnect
獲取頂層數據,最終運行效果以下所示:
若是喜歡能夠給個贊~或星~喲
git地址:github.com/Harhao/simp…
參考文章
React中文文檔
[譯]2019 React Redux 徹底指南
[譯] Redux vs. React 的 Context API
React Hooks 解析(上):基礎
React Hooks 解析(下):進階