針對 react hooks 的新版本解決方案react
若想要無縫使用原來的 redux,和其配套的中間件 promise,thunk,saga 等等的話
能夠使用 redux-react-hook
git
github 連接 redux-react-hookgithub
一個簡單的使用例子:redux
import {useDispatch, useMappedState} from 'redux-react-hook'; export function DeleteButton({index}) { // 相似於之前 react-redux 中的 connect 函數 const mapState = useCallback( state => ({ canDelete: state.todos[index].canDelete, name: state.todos[index].name, }), [index], ); // 獲取 redux 的數據 const {canDelete, name} = useMappedState(mapState); // 獲取 dispatch const dispatch = useDispatch(); // button click handle const deleteTodo = useCallback( () => dispatch({ type: 'delete todo', index, }), [index], ); return ( <button disabled={!canDelete} onClick={deleteTodo}> Delete {name} </button> ); }
使用方法和之前一致promise
在 index 或 app 中提供全局的 redux 與 dispatchapp
function isPromise(obj) { return ( !!obj && (typeof obj === "object" || typeof obj === "function") && typeof obj.then === "function" ); } function wrapperDispatch(dispatch) { // 功能和 redux-promise 相同 return function (action) { isPromise(action.payload) ? action.payload.then(v => { dispatch({type: action.type, payload: v}) }).catch((error) => { dispatch(Object.assign({}, action, { payload: error, error: true })); return Promise.reject(error); }) : dispatch(action); }; } function Wrap(props) { // 確保在 dispatch 後不會刷新APP組件 const [state, dispatch] = useReducer(reducers, ReducersValue); console.log('render wrap') return (<MainContext.Provider value={{state: state, dispatch: wrapperDispatch(dispatch)}}>{props.children}</MainContext.Provider>) } function App() { console.log('render App') return <Wrap> <Router> <Switch> <Route path="/login" component={Login} exact/> <Route path="/" component={MainIndex}/> </Switch> </Router> </Wrap> }
具體使用:ide
function useDispatch() { // 獲取 dispatch const store = useContext(MainContext); return store.dispatch; } function useStoreState(mapState) { //存儲 state 且判斷是否須要 render const {state:store} = useContext(MainContext); const mapStateFn = () => mapState(store); const [mappedState, setMappedState] = useState(() => mapStateFn()); const lastRenderedMappedState = useRef(); // Set the last mapped state after rendering. useEffect(() => { lastRenderedMappedState.current = mappedState; }); useEffect( () => { console.log('useEffect ') const checkForUpdates = () => { const newMappedState = mapStateFn(); if (!_.isEqual(newMappedState, lastRenderedMappedState.current)) { setMappedState(newMappedState); } }; checkForUpdates(); }, [store, mapState], ); return mappedState } // 組件內使用 const ResourceReducer = useStoreState(state => state.ResourceReducer) const dispatch = useDispatch()
他的功能已經足夠了,在使用的地方使用函數便可,很方便
可是也有一些不足的地方是在根源上的,即 context,
在同一個頁面中 若是有多個使用 context 的地方
那麼若是一旦dispatch ,其餘的全部地方也會觸發render 形成資源的浪費,小項目還好,大項目仍舊不可
取
(除非 react 的 context 函數添加 deps)函數
原理就是存儲一個全局變量 ,經過 import 引入;
我本身寫了一個例子:https://github.com/Grewer/react-hooks-store
想要基礎的實現只須要 30+ 行的代碼便可this
class Modal { private value: any; private prevValue: any; private reducers: (state, action) => {}; private queue: any = []; private dispatch: (action) => void; constructor(reducers) { this.reducers = combineReducers(reducers) // combineReducers 來自於 reudx ,能夠引入也能夠本身寫一個(後續我會寫一個庫,會包含此函數) this.value = this.reducers({}, {}) this.dispatch = action => { this.prevValue = this.value; this.value = this.reducers(this.value, action) this.onDataChange() } } useModal = (deps?: string[]) => { const [, setState] = useState(this.value); useEffect(() => { const index = this.queue.push({setState, deps}); // 訂閱 return () => { // 組件銷燬時取消 this.queue.splice(index - 1, 1); }; }, []); return [this.value, this.dispatch] } onDataChange = () => { this.queue.forEach((queue) => { const isRender = queue.deps ? queue.deps.some(dep => this.prevValue[dep] !== this.value[dep]) : true isRender && queue.setState(this.value) }); } }
// 初始化 reducersspa
const modal = new Modal({ countReducer: function (state = 0, action) { console.log('count Reducer', state, action) switch (action.type) { case "ADD": console.log('trigger') return state + action.payload || 1 default: return state } }, listReducer: function (state = [] as any, action) { console.log('list Reducer', state, action) switch (action.type) { case "ADD_LIST": console.log('trigger') state.push(action.payload) return [...state] default: return state } }, personReducer: function (state = {name: 'lll', age: 18} as any, action) { console.log('person Reducer', state, action) switch (action.type) { case "CHANGE_NAME": return Object.assign({}, state, {name: action.payload}) default: return state } } }) // 導出 useModal export const useModal = modal.useModal
簡單的使用:
function Count(props) { const [state, dispatch] = useModal(['countReducer']) // 非 countReducer 的更新 不會觸發此函數 render console.warn('render Count', state, dispatch) return <div> <button onClick={() => dispatch({type: "ADD", payload: 2})}>+</button> </div> }
固然你也能夠本身寫一個,本身想要的方案
hooks 的存儲方案基本就這 3 類,能夠用現成的,也能夠使用本身寫的方案