React16.8開始內置了10個Hook,核心是2個:react
useStateredux
有狀態組件寫法:數組
class Example extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } render() { return ( <div> <p>You clicked {this.state.count} times</p> <button onClick={() => this.setState({ count: this.state.count + 1 })}> Click me </button> </div> ); } }
無狀態組件寫法:緩存
const Example = props => { const { count, onClick } = props; return ( <div> <p>You clicked {count} times</p> <button onClick={onClick}> Click me </button> </div> ) }
hooks是有狀態的函數:ide
import { useState } from 'react'; const Example = () => { const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ) }
注意,useState生成的setter在更新state的時候不會合並:函數
const [data, setData] = useState({ count: 0, name: 'abc' }); // name沒有被使用,應該分開聲明 useEffect(() => { // data: { count: 0, name: 'abc' } -> { count: 0 } setData({ count: 1 }) })
在咱們的純函數組件裏,每一個useState都會生產一對state和stateSetter,咱們無需考慮更多的狀態樹的設計和組件的劃分設計,邏輯代碼能夠直接從根組件寫起。this
咱們應用的發展途徑大體分爲如下3個階段,基於hook開發過程將更有彈性:spa
不一樣於真正的redux,在實際應用中,hook帶來了一種更加靈活和純粹的模式。如今咱們能夠用10行代碼實現一個全局的redux,也能夠用2行代碼隨時隨地實現一個局部的redux:設計
10行代碼實現一個全局的redux:code
import React from 'react'; const store = React.createContext(null); export const initialState = { name: 'aa' }; export function reducer(state, action) { switch (action.type) { case 'changeName': return { ...state, name: action.payload }; default: throw new Error('Unexpected action'); } } export default store;
Provider根組件掛上:
import React, { useReducer } from 'react'; import store, { reducer, initialState } from './store'; function App() { const [state, dispatch] = useReducer(reducer, initialState); return ( <store.Provider value={{ state, dispatch }}> <div /> </store> ) }
子組件調用:
import React, { useContext } from 'react'; import store from './store'; function Child() { const { state, dispatch } = useContext(store); }
隨時隨地的一個局部redux:
import React, { useReducer } from 'react'; const initialState = { name: 'aa' }; function reducer(state, action) { switch (action.type) { case 'changeName': return { ...state, name: action.payload }; default: throw new Error('Unexpected action'); } } function Component() { const [state, dispatch] = useReducer(reducer, initialState); ... }
當咱們想在兩個函數之間共享邏輯時,咱們會把它提取到第三個函數中。而組件和 hook 都是函數,因此也一樣適用這種方式。不一樣的是,hook 是有狀態的函數,它能實現以往純函數所不能作到的更高級別的複用——狀態邏輯的複用。
component寫法:
class App extends React.Component { constructor(props) { super(props); this.state = { count: 0, name: undefined }; } componentDidMount() { service.getInitialCount().then(data => { this.setState({ count: data }); }); service.getInitialName().then(data => { this.setState({ name: data }); }); } componentWillUnmount() { service.finishCounting().then(() => { alert('計數完成'); }); } addCount = () => { this.setState({ count: this.state.count + 1 }); }; handleNameChange = name => { this.setState({ name }); }; render() { const { count, name } = this.state; return ( <div> <div> <p>You clicked {count} times</p> <button onClick={this.addCount}>Click me</button> </div> <Input value={name} onChange={this.handleNameChange} /> </div> ); } }
hook寫法:
function useCount(initialValue) { const [count, setCount] = useState(initialValue); useEffect(() => { service.getInitialCount().then(data => { setCount(data); }); return () => { service.finishCounting().then(() => { alert('計數完成'); }); }; }, []); function addCount() { setCount(c => c + 1); } return { count, addCount }; } function useName(initialValue) { const [name, setName] = useState(initialValue); useEffect(() => { service.getInitialName().then(data => { setName(data); }); }, []); function handleNameChange(value) { setName(value); } return { name, handleNameChange }; } const App = () => { const { count, addCount } = useCount(0); const { name, setName } = useName(); return ( <div> <p>You clicked {count} times</p> <button onClick={addCount}>Click me</button> <Input value={name} onChange={setName} /> </div> ); };
如上,使用component的寫法裏,count和name,還有與之相關的一票兒邏輯,散落在組件的生命週期和方法裏。雖然咱們能夠將組件的state和變動action抽成公共的,但涉及到反作用的action,到最終仍是繞不開組件的生命週期。但一個組件的生命週期只有一套,不可避免的會出現一些徹底不相干的邏輯寫在一塊兒。如此一來,便沒法實現徹底的狀態邏輯複用。
咱們再看看使用hook的寫法,咱們將count相關的邏輯和name相關的邏輯經過自定義hook,封裝在獨立且封閉的邏輯單元裏。以往class組件的生命週期在這裏不復存在。生命週期是和UI強耦合的一個概念,雖然易於理解,但它自然距離數據很遙遠。而hook以一種相似rxjs模式的數據流訂閱實現了組件的反作用封裝,這樣的好處就是咱們只須要關心數據。因此hook所帶來的,毫不僅僅只是簡化了state的定義與包裝。
自定義hook實現了狀態邏輯與UI分離,經過合理抽象自定義hook,可以實現很是高級別的業務邏輯抽象複用
let hooks, i; function useState() { i++; if (hooks[i]) { // 再次渲染時 return hooks[i]; } // 第一次渲染 hooks.push(...); } // 準備渲染 i = -1; hooks = fiber.hooks || []; // 調用組件 Component(); // 緩存 Hooks 的狀態 fiber.hooks = hooks;
本文由博客一文多發平臺 OpenWrite 發佈!