Gathering detailed insights and metrics for react-hooks-redux
Gathering detailed insights and metrics for react-hooks-redux
Gathering detailed insights and metrics for react-hooks-redux
Gathering detailed insights and metrics for react-hooks-redux
react-tracked
State usage tracking with Proxies. Optimize re-renders for useState/useReducer, React Redux, Zustand and others.
hooks-proxy-store
React proxyStore hooks
react-hooks-easy-redux
Easy React bindings for Redux with Hooks API
react-redux-hooks
The easiest way to connect redux. Power by react hooks
npm install react-hooks-redux
Typescript
Module System
Node Version
NPM Version
72.9
Supply Chain
98.1
Quality
75.9
Maintenance
100
Vulnerability
100
License
JavaScript (100%)
Verify real, reachable, and deliverable emails with instant MX records, SMTP checks, and disposable email detection.
Total Downloads
10,758
Last Day
1
Last Week
3
Last Month
8
Last Year
834
38 Stars
50 Commits
4 Forks
1 Watchers
1 Branches
1 Contributors
Updated on Aug 29, 2023
Minified
Minified + Gzipped
Latest Version
0.10.4
Package Id
react-hooks-redux@0.10.4
Unpacked Size
433.99 kB
Size
152.13 kB
File Count
13
NPM Version
6.4.1
Node Version
8.12.0
Cumulative downloads
Total Downloads
Last Day
0%
1
Compared to previous day
Last Week
200%
3
Compared to previous week
Last Month
-92.9%
8
Compared to previous month
Last Year
21.2%
834
Compared to previous year
53
react-hooks 是 react 官方新的编写推荐,我们很容易在官方的 useReducer 钩子上进行一层很简单的封装以达到和以往 react-redux \ redux-thunk \ redux-logger 类似的功能,并且大幅度简化了声明。
react-hooks 的更多信息请阅读 reactjs.org/hooks;
这 70 行代码是一个完整的逻辑, 客官可以先阅读,或许后续的说明文档也就不需要阅读了。
1import React from 'react'; 2 3function middlewareLog(lastState, nextState, action, isDev) { 4 if (isDev) { 5 console.log( 6 `%c|------- redux: ${action.type} -------|`, 7 `background: rgb(70, 70, 70); color: rgb(240, 235, 200); width:100%;`, 8 ); 9 console.log('|--last:', lastState); 10 console.log('|--next:', nextState); 11 } 12} 13 14function reducerInAction(state, action) { 15 if (typeof action.reducer === 'function') { 16 return action.reducer(state); 17 } 18 return state; 19} 20 21export default function createStore(params) { 22 const { isDev, reducer, initialState, middleware } = { 23 isDev: false, 24 reducer: reducerInAction, 25 initialState: {}, 26 middleware: params.isDev ? [middlewareLog] : undefined, 27 ...params, 28 }; 29 const AppContext = React.createContext(); 30 const store = { 31 isDev, 32 _state: initialState, 33 useContext: function() { 34 return React.useContext(AppContext); 35 }, 36 dispatch: undefined, 37 getState: function() { 38 return store._state; 39 }, 40 initialState, 41 }; 42 let isCheckedMiddleware = false; 43 const middlewareReducer = function(lastState, action) { 44 let nextState = reducer(lastState, action); 45 if (!isCheckedMiddleware) { 46 if (Object.prototype.toString.call(middleware) !== '[object Array]') { 47 throw new Error("react-hooks-redux: middleware isn't Array"); 48 } 49 isCheckedMiddleware = true; 50 } 51 for (let i = 0; i < middleware.length; i++) { 52 const newState = middleware[i](store, lastState, nextState, action); 53 if (newState) { 54 nextState = newState; 55 } 56 } 57 store._state = nextState; 58 return nextState; 59 }; 60 61 const Provider = props => { 62 const [state, dispatch] = React.useReducer(middlewareReducer, initialState); 63 if (!store.dispatch) { 64 store.dispatch = async function(action) { 65 if (typeof action === 'function') { 66 await action(dispatch, store._state); 67 } else { 68 dispatch(action); 69 } 70 }; 71 } 72 return <AppContext.Provider {...props} value={state} />; 73 }; 74 return { Provider, store }; 75}
reducer-in-action 是一个 reducer 函数,这 6 行代码就是 reducer-in-action 的全部:
1function reducerInAction(state, action) { 2 if (typeof action.reducer === 'function') { 3 return action.reducer(state); 4 } 5 return state; 6}
它把 reducer 给简化了,放置到了每一个 action 中进行 reducer 的处理。我们再也不需要写一堆 switch,再也不需要时刻关注 action 的 type 是否和 redcer 中的 type 一致。
reducer-in-action 配合 thunk 风格,可以非常简单的编写 redux,随着项目的复杂,我们只需要编写 action,会使得项目结构更清晰。
安装 react-hooks-redux, 需要 react 版本 >= 16.7
1yarn add react-hooks-redux
我们用了不到 35 行代码就声明了一个完整的 react-redux 的例子, 拥抱 hooks。
1import React from 'react'; 2import ReactHookRedux from 'react-hooks-redux'; 3 4// 通过 ReactHookRedux 获得 Provider 组件和一个 sotre 对象 5const { Provider, store } = ReactHookRedux({ 6 isDev: true, // 打印日志 7 initialState: { name: 'dog', age: 0 }, 8}); 9 10function actionOfAdd() { 11 return { 12 type: 'add the count', 13 reducer(state) { 14 return { ...state, age: state.age + 1 }; // 每次需要返回一个新的 state 15 }, 16 }; 17} 18 19function Button() { 20 function handleAdd() { 21 store.dispatch(actionOfAdd()); //dispatch 22 } 23 return <button onClick={handleAdd}>add</button>; 24} 25 26function Page() { 27 const state = store.useContext(); 28 return ( 29 <div> 30 {state.age} <Button />{' '} 31 </div> 32 ); 33} 34 35export default function App() { 36 return ( 37 <Provider> 38 <Page /> 39 </Provider> 40 ); 41}
总结一下:
我们阅读这个小例子会发现,没有对组件进行 connect, 没有编写 reducer 函数, 这么简化设计是为了迎合 hooks, hooks 极大的简化了我们编写千篇一律的类模板,但是如果我们还是需要对组件进行 connect, 我们又回到了编写模板代码的老路。
绝大部分情况,你不需要编写 middleware, 不过它也极其简单。middleware 是一个一维数组,数组中每个对象都是一个函数, 传入了参数并且如果返回的对象存在, 就会替换成 nextState 并且继续执行下一个 middleware。
我们可以使用 middleware 进行打印日志、编写 chrome 插件或者二次处理 state 等操作。
我们看看 middleware 的源码:
1let nextState = reducer(lastState, action); 2for (let i = 0; i < middleware.length; i++) { 3 const newState = middleware[i](lastState, nextState, action, isDev); 4 if (newState) { 5 nextState = newState; 6 } 7} 8return nextState;
性能(和实现上)上最大的区别是,react-hooks-redux 使用 useContext 钩子代替 connect 高阶组件进行 dispatch 的派发。
在传统的 react-redux 中,如果一个组件被 connect 高阶函数进行处理,那么当 dispatch 时,这个组件相关的 mapStateToProps 函数就会被执行,并且返回新的 props 以激活组件更新。
而在 hooks 风格中,当一个组件被声明了 useContext() 时,context 相关联的对象被变更了,这个组件会进行更新。
理论上性能和 react-redux 是一致的,由于 hooks 相对于 class 有着更少的声明,所以应该会更快一些。
所以,我们有节制的使用 useContext 可以减少一些组件被 dispatch 派发更新。
如果我们需要手动控制减少更新 可以参考 useMemo 钩子的使用方式进行配合。
如果不希望组件被 store.dispatch() 派发更新,仅读取数据可以使用 store.getState(), 这样也可以减少一些不必要的组件更新。
以上都是理论分析,由于此库和此文档是一个深夜的产物,并没有去做性能上的基准测试,所以有人如果愿意非常欢迎帮忙做一些基准测试。
随着工作的进展,完善了一些功能, 代码量也上升到了300行,有兴趣的可以去仓库看看:
1import React from 'react'; 2import ReactHookRedux, { 3 reducerInAction, 4 middlewareLog, 5} from 'react-hooks-redux'; 6 7// 通过 ReactHookRedux 获得 Provider 组件和一个 sotre 对象 8const { Provider, store } = ReactHookRedux({ 9 isDev: true, // default is false 10 initialState: { count: 0, asyncCount: 0 }, // default is {} 11 reducer: reducerInAction, // default is reducerInAction 所以可省略 12 middleware: [middlewareLog], // default is [middlewareLog] 所以可省略 13 actions: {}, // default is {} 所以可省略 14 autoSave: { 15 item: 'localSaveKey', 16 keys: ['user'], // 需要缓存的字段 17 }, 18}); 19 20// 模拟异步操作 21function timeOutAdd(a) { 22 return new Promise(cb => setTimeout(() => cb(a + 1), 500)); 23} 24 25const actions = { 26 // 如果返回的是一个function,我们会把它当成类似 react-thunk 的处理方式,并且额外增加一个ownState的对象方便获取state 27 asyncAdd: () => async (dispatch, ownState) => { 28 const asyncCount = await timeOutAdd(ownState.asyncCount); 29 dispatch({ 30 type: 'asyncAdd', 31 // if use reducerInAction, we can add reducer Function repeat reducer 32 reducer(state) { 33 return { 34 ...state, 35 asyncCount, 36 }; 37 }, 38 }); 39 }, 40}; 41 42function Item() { 43 const state = store.useContext(); 44 return <div>async-count: {state.asyncCount}</div>; 45} 46 47function Button() { 48 async function handleAdd() { 49 // 使用 async dispatch 50 await store.dispatch(actions.asyncAdd()); 51 } 52 return <button onClick={handleAdd}>add</button>; 53} 54 55export default function App() { 56 return ( 57 <Provider> 58 <Item /> 59 <Button /> 60 </Provider> 61 ); 62}
1import React, { useCallback } from 'react'; 2import ReactHookRedux from 'react-hooks-redux'; 3import { Map } from 'immutable'; 4 5const { Provider, store } = ReactHookRedux({ 6 initialState: Map({ products: ['iPhone'] }), // 请确保immutable是一个Map 7 isDev: true, // 当发现对象是 immutable时,middleware会遍历属性,使用getIn做浅比较打印 diff的对象 8}); 9 10function actionAddProduct(product) { 11 return { 12 type: 'add the product', 13 reducer(state) { 14 return state.update('products', p => { 15 p.push(product); 16 return [...p]; 17 }); 18 }, 19 }; 20} 21 22let num = 0; 23function Button() { 24 function handleAdd() { 25 num += 1; 26 store.dispatch(actionAddProduct('iPhone' + num)); //dispatch 27 } 28 return <button onClick={handleAdd}>add-product</button>; 29} 30 31function Page() { 32 const state = store.useContext(); 33 // 从immutable获取对象,如果products未改变,会从堆中获取而不是重新生成新的数组 34 const products = state.get('products'); 35 36 return useCallback( 37 <div> 38 <Button /> 39 {products.map(v => ( 40 <div>{v}</div> 41 ))} 42 </div>, 43 [products], // 如果products未发生改变,不会进行进行重渲染 44 ); 45} 46 47export default function App() { 48 return ( 49 <Provider> 50 <Page /> 51 </Provider> 52 ); 53}
1function mapStateToProps(state) { 2 return { 3 imgList: state.getIn(['data', 'images', 'hits']), 4 }; 5} 6 7function mapDispatchToProps(dispatch) { 8 return { 9 asyncGetImages: async function() { 10 return await dispatch(await actions.asyncGetImages()); 11 }, 12 }; 13} 14 15export default store.connect( 16 Compontent, 17 mapStateToProps, 18 mapDispatchToProps, 19); 20
谢谢阅读。
MIT License
Copyright (c) 2013-present, Facebook, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
Found 0/30 approved changesets -- score normalized to 0
Reason
no SAST tool detected
Details
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
license file not detected
Details
Reason
branch protection not enabled on development/release branches
Details
Reason
129 existing vulnerabilities detected
Details
Score
Last Scanned on 2025-03-03
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More