{"id":15479602,"url":"https://github.com/nealyang/study-redux","last_synced_at":"2025-04-16T14:27:30.077Z","repository":{"id":89427433,"uuid":"85140188","full_name":"Nealyang/study-redux","owner":"Nealyang","description":"study redux","archived":false,"fork":false,"pushed_at":"2017-03-22T07:07:25.000Z","size":491,"stargazers_count":80,"open_issues_count":1,"forks_count":77,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-19T04:11:10.508Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Nealyang.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-03-16T01:51:17.000Z","updated_at":"2020-12-08T15:40:53.000Z","dependencies_parsed_at":null,"dependency_job_id":"8ef2c46f-aae3-48e0-aa1a-825b843eef37","html_url":"https://github.com/Nealyang/study-redux","commit_stats":{"total_commits":20,"total_committers":2,"mean_commits":10.0,"dds":"0.050000000000000044","last_synced_commit":"2b995a5e8c6883b85022a29c44f943c815be4ec8"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nealyang%2Fstudy-redux","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nealyang%2Fstudy-redux/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nealyang%2Fstudy-redux/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Nealyang%2Fstudy-redux/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Nealyang","download_url":"https://codeload.github.com/Nealyang/study-redux/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249250420,"owners_count":21237894,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-02T04:22:04.306Z","updated_at":"2025-04-16T14:27:30.047Z","avatar_url":"https://github.com/Nealyang.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":" 关于Redux刚刚刚刚入门者对Redux的理解和使用\n---\n\n临危受命，一周各种熬夜突击redux，好吧，刚开始真的各种香菇蓝瘦，前天还熬到凌晨五点多，写了点demo，有点感觉，一点点记录下。这里简单介绍下Redux以及其与react结合的使用方法\n\n___\n最近学习东西太多太杂，经常与人交流讨论，新建的群，欢迎加入：\u003cbr\u003e\nNode.js技术交流群：209530601 \u003cbr/\u003e\nReact技术栈：398240621\n___\n说明：因为笔者也是入门，如有扯淡扯错了，还望各路大神指教。\n\n我们为什么需要Redux，什么是Redux\n===\n了解过react，我想大家对state都不陌生，而对于JavaScript单页面日趋复杂的今天，JavaScript需要管理越来越多的state，而这些state包括着各种乱七八糟途径来的数据。甚至有的应用的state会关系到另一个组件的状态。所以为了方便对这些state的管理以及对state变化的可控性。Redux这个东西就出来了！没错，就是为了让state的变化变得可预测！\n\n那么redux到底是什么呢？\n这里非权威的解释：就是一个应用的state管理库，甚至可以说是前端数据库。更包括的是管理数据。\n不懂？OK，不懂就对了。。。\n介绍完Redux和react-redux的基本概念我再通俗的解释下。\n\nRedux的基本概念\n===\nstate\n---\nstate才是真正的前端数据库，它存储着这个应用所有需要的数据。\n这里拿一个[简单的例子](./counter)说明下,为什么说简单的例子呢，因为简单到不应该使用redux。。。\n运行效果如图(学习redux这个例子被介绍烂了)：\n\n![Counter](./resources/counter.png)\n项目的运行效果大家应该能猜到哈，猜不到的clone下项目几几运行下:-)\n所以这里的如说问你，这个应用应该存在数据里什么数据呢？对的，就一个count，所以数据库就存一个count就可以了，同理，这个应用的state其实就酱紫：\n![img](./resources/counter1.png)\n注释错了，是count值为3  .╥﹏╥...\n\n这里展示的不是很明显，可以拿我们下一篇博客的demo来查看下它的 state\n\n![async](./resources/async.png)\n\n\n所以说道这里，大家对于state就已经明白了，需要说明一下的是，一个应用只应该有一个state。对，不管多大，就一个！\n\naction\n---\n既然这些state已经有了，那么我们是如何实现管理这些state中的数据的呢，当然，这里就要说到action了。\n什么是action？E:action,中：动作。\n是的，就是这么简单。。。\u003cbr\u003e\n只有当某一个动作发生的时候才能够触发这个state去改变，那么，触发state变化的原因那么多，比如这里的我们的点击事件，还有网络请求，页面进入，鼠标移入。。。所以action的出现，就是为了把这些操作所产生或者改变的数据从应用传到store中的有效载荷。\n需要说明的是，action是state的唯一来源。\u003cbr\u003e\naction也没有什么神秘，本质上就是一个JavaScript对象，但是约定的包含type属性（你总得告诉你这个action是啥嘛），可以理解成每个人都要有名字一般。除了type属性，别的属性，都可以DIY~\u003cbr\u003e\n那么这么多action一个个手动创建必然不现实，一般我们会写好action creator，即action的创建函数。调用action creator，给你返回一个action。\n\n比如这个counter应用，我们就有两个action，一个decrement，一个increment。\n所以这里的action creator写成如下：\n\n    export function decrement() {\n        return{\n            type:DECREMENT_COUNTER\n        }\n    }\n    \n    export function increment(){\n        return{\n            type:INCREMENT_COUNTER\n        }\n    }\n\n好吧，藏也藏不住了，你也发现了这里有另外两个按钮，干嘛的？奇数是点击+1、延迟+1，对应的action creator：\n\n    export function incrementIfOdd(){\n        return(dispatch,getState)=\u003e{\n            const {counter} = getState();\n            if(counter%2==0) {\n                return;\n            }\n            dispatch(increment());\n        }\n    }\n    \n    export function incrementAsync() {\n        return dispatch =\u003e {\n            setTimeout(() =\u003e {\n                dispatch(increment());\n            }, 1000);\n        };\n    }\n\n 好吧，又被你发现了，不是说action creator返回的是一个action对象么，你这返回的什么鬼？这里留个疑惑好吧，简单的解释，\n 为什么可以这么用呢，因为我用了中间件呀~（后续会介绍）\n \n 为了减少样板代码，我们使用单独的模块或文件来定义 action type 常量\n \n     export const INCREMENT_COUNTER = 'INCREMENT_COUNTER';\n     export const DECREMENT_COUNTER = 'DECREMENT_COUNTER';\n\n这么做不是必须的，在大型应用中把它们显式地定义成常量还是利大于弊的。\n\n如果我都快把action说完了你还是不懂action是什么的话，你就理解成，一个可能！改变state的动作包装。\n\nreducer\n---\n既然这个可能改变state的动作已经包装好了，那么我们怎么去判断并且对state做相应的改变呢？对，这就是reducer干的事情了。\n从一开始我们就说明下一个概念:\n\n\u003cb\u003ereducer决定了state的最终格式。\u003c/b\u003e\n\nreducer是一个纯函数，也就是说，只要传入参数相同，返回计算得到的下一个 state 就一定相同。没有特殊情况、没有副作用，没有 API 请求、没有变量修改，单纯执行计算。\n\nreducer对传入的action进行判断，然后返回一个通过判断后的state，这就是reducer的全部职责。\n从代码可以简单地看出：\n\n    import {INCREMENT_COUNTER,DECREMENT_COUNTER} from '../actions';\n    \n    export default function counter(state = 0,action) {\n        switch (action.type){\n            case INCREMENT_COUNTER:\n                return state+1;\n            case DECREMENT_COUNTER:\n                return state-1;\n            default:\n                return state;\n        }\n    }\n这里我们就是对增和减两个之前在action定义好的常量做了处理。\n\n对于一个比较大一点的应用来说，我们是需要将reducer拆分的，最后通过redux提供的combineReducers方法组合到一起。\n如此项目上的：\n\n    const rootReducer = combineReducers({\n        counter\n    });\n    \n    export default rootReducer;\n    \n 虽然这里我们就一个counter    (ಥ_ಥ)   但是道理你懂得。\n \n 这里你要明白：每个 reducer 只负责管理全局 state 中它负责的一部分。每个 reducer 的 state 参数都不同，分别对应它管理的那部分 state 数据。\n combineReducers() 所做的只是生成一个函数，这个函数来调用你的一系列 reducer，每个 reducer 根据它们的 key 来筛选出 state 中的一部分数据并处理，\n 然后这个生成的函数再将所有 reducer 的结果合并成一个大的对象。\n \n store\n ---\n store是对之前说到一个联系和管理。具有如下职责\n + 维持应用的 state；\n + 提供 getState() 方法获取 state\n + 提供 dispatch(action) 方法更新 state；\n + 通过 subscribe(listener) 注册监听器;\n + 通过 subscribe(listener) 返回的函数注销监听器。\n \n 后面两个不怎么用哈~\n \n再次强调一下 Redux 应用只有一个单一的 store。当需要拆分数据处理逻辑时，你应该使用 reducer 组合 而不是创建多个 store。\nstore的创建通过redux的createStore方法创建，这个方法还需要传入reducer，很容易理解：毕竟我需要dispatch一个action来改变state嘛。\n应用一般会有一个初始化的state，所以可选为第二个参数，这个参数通常是有服务端提供的，传说中的Universal渲染。后面会说。。。\n第三个参数一般是需要使用的中间件，通过applyMiddleware传入。\n\n\n说了这么多，action，store，action creator，reducer关系就是这么如下的简单明了：\n![guanxitu](./resources/pic.png)\n\n接合react-redux的使用\n===\n说到react-redux，必然想到react和redux，是的，react-redux正是redux和react的桥梁工具。\n\nreact-redux将组建分成了两大类，UI组建和容器组建。\n简单的说，UI组建负责美的呈现，容器组件负责来帮你盛着，给你\"力量\"\n\n好吧，官方点：\nUI 组件有以下几个特征：\n+ 只负责 UI 的呈现，不带有任何业务逻辑\n+ 没有状态（即不使用this.state这个变量）\n+ 所有数据都由参数（this.props）提供\n+ 不使用任何 Redux 的 API\n\n如：\n\n    export default class Counter extends Component{\n        render(){\n            const {counter,increment,decrement,incrementIfOdd,incrementAsync} = this.props;\n            return(\n                \u003cp\u003e\n                    Clicked:{counter} times\n                    {'  '}\n                    \u003cbutton onClick={increment}\u003e+\u003c/button\u003e\n                    {'  '}\n                    \u003cbutton onClick={decrement}\u003e-\u003c/button\u003e\n                    {'  '}\n                    \u003cbutton onClick={incrementIfOdd}\u003eincrement if Odd\u003c/button\u003e\n                    {'  '}\n                    \u003cbutton onClick={incrementAsync}\u003eincrement async\u003c/button\u003e\n                \u003c/p\u003e\n            )\n        }\n    }\n    \n容器组件特性则恰恰相反：\n+ 负责管理数据和业务逻辑，不负责 UI 的呈现\n+ 带有内部状态\n+ 使用 Redux 的 API\n\n如：\n\n    class App extends Component{\n        render(){\n            const {counter,increment,decrement,incrementIfOdd,incrementAsync} = this.props;\n            return(\n                \u003cCounter\n                    counter={counter}\n                    increment={increment}\n                    decrement={decrement}\n                    incrementIfOdd={incrementIfOdd}\n                    incrementAsync={incrementAsync}/\u003e\n            )\n        }\n    }\n    \n    App.propTypes = {\n        counter:PropTypes.number.isRequired,\n        increment:PropTypes.func.isRequired,\n        decrement:PropTypes.func.isRequired,\n        incrementIfOdd:PropTypes.func.isRequired,\n        incrementAsync:PropTypes.func.isRequired\n    };\n    \n    export default connect(\n        state=\u003e({\n            counter:state.counter\n        }),\n        ActionCreators\n    )(App);\n\n说到这里大家应该都懂，那么问题来了，redux和react如何产生点关系呢？？不难想到，如果产生关系肯定只要跟容器组件产生关系就可以了，毕竟他是react这些组件的老祖宗。\n那么如何产生关系呢？？对的，就是上面代码里的，react-redux中的connect方法。\n\nconnect方法接受两个参数：mapStateToProps和mapDispatchToProps。它们定义了 UI 组件的业务逻辑。前者负责输入逻辑，即将state映射到 UI 组件的参数（props），\n后者负责输出逻辑，即将用户对 UI 组件的操作映射成 Action。\n\n比如这样：\n\n    export default connect(\n      state =\u003e ({ counter: state.counter }),\n      ActionCreators\n    )(Counter);\n 因为作为组件，我们只要能拿到值，能发出改变值得action就可以了，所以mapStateToProps和mapDispatchToProps正是满足这个需求的。\n \n 当时对这个connect也是好一顿理解\n \n 这么通俗的说你该明白了吧\n ===\n 可以吧所有的组件想象成装在一个罐子里，这个罐子使用container做的，然后这个罐子的唯一的口就是里面的东西想要去改变的唯一途径。\n 说白了，这个口，就是connect，而redux中的所有的组件都是在罐子外面的。\n \n reducer是改变state的，state就可以可以理解成组件的粮食，需要的时候redux就把粮食通过dispatch投入到罐子里。\n 那么我怎么知道你需要呢？所以我们之间得有个约束，你喊一声饿了，我就知道你要吃饭了，你喊一声渴了，我就知道你要喝水了。\n 这些动作，就是你发出的action，喊得词语，饿了，渴了，就是action.type，然后redux拿给军事reducer解读下，到底给罐子里投入什么。\n \n 不知道这么通俗的解释有没有说明白   (*T_T*) \n \n 所以这样看来，组件通过container包装以后和redux就可以说是完全隔绝了，组件就是做组件的事情，redux就是做redux的事情。中间的枢纽是connect。\n 这也就说明了，redux并不是只服务我们react哒~也即是我这一套逻辑在罐子外面，罐子里面是什么其实我并不是很在意。。。只要我们预定好action和state就可以了。\n \n 所以。。。redux也并没有特别神秘的地方。\n 当然，这里只是简单的说了下redux的简单概念，并没有特别深入的讲解。\n 关于redux的异步操作，以及在服务端的运行（node），universal渲染，结合react-router的使用等等等的功能，咱再慢慢了解慢慢研究慢慢总结哈~ \n  \n  \n  \n  \n  ヾ(^▽^ヾ)\n \n \n ","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnealyang%2Fstudy-redux","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnealyang%2Fstudy-redux","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnealyang%2Fstudy-redux/lists"}