如何做好公司网站建设,重庆百度seo公司,徐家汇网站建设,地方网站做相亲赢利点在哪React组件化#xff08;以Ant-Design为例#xff09;
组件化编程#xff0c;只需要去安装好对应的组件#xff0c;然后通过各式各样的组件引入#xff0c;实现快速开发 我们这里学习的是 Ant-design #xff08;应该是这样#xff09;#xff0c;它有很多的组件供我们…React组件化以Ant-Design为例
组件化编程只需要去安装好对应的组件然后通过各式各样的组件引入实现快速开发 我们这里学习的是 Ant-design 应该是这样它有很多的组件供我们使用 引入各种组件可以方便开发省着自己去二次封装组件同时也更好看了 快速起步
安装antd组件库
npm install antd随便一个组件里面引入
import { Button } from antd;class App extends React.Component {render() {divFlex gapsmall wrapwrapButton typeprimaryPrimary Button/ButtonButtonDefault Button/ButtonButton typedashedDashed Button/ButtonButton typetextText Button/ButtonButton typelinkLink Button/Button/Flex/div}
}
export default App;
运行一下
大概就是这么个东西一定学会看官方文档尤其这种组件类的不看文档就啥也做不了 AntDesign官方文档
其他UI但是是国外的 material-ui
大概就是这些东西还有自定义主题的一些方法因为每个版本的官方修改方式都不一样所以建议现用现查
Redux
非必须学习的项目但是如果项目里用了就得学
Redux简介
1.redux是一个专门用于做状态管理的JS库(不是react插件库只是名字像)。 2.它可以用在react, angular, vue等项目中, 但基本与react配合使用。 3.作用: 集中式管理react应用中多个组件共享的状态。
也类似于VueX
什么时候需要用Redux
首先Redux是在有很多很多组件的情况下才有可能需要用到Redux的如果是单个组件就自然没有很复杂的传值需求了
Redux 适用于多交互、多数据源的场景。简单理解就是复杂
从组件角度去考虑的话当我们有以下的应用场景时我们可以尝试采用 Redux 来实现
某个组件的状态需要共享时一个组件需要改变其他组件的状态时一个组件需要改变全局的状态时
除此之外还有很多情况都需要使用 Redux 来实现还没有学 hook或许还有更好的方法 Redux工作流程
store
store 是 Redux 的核心可以理解为是 Redux 的数据流向指挥我们可以将任何我们想要存放的数据放在 store 中在我们需要使用这些数据时我们可以从中取出相应的数据。因此我们需要先创建一个 store 在 Redux 中可以使用 createStore API 来创建一个 store
store是无法直接进行操作的需要借助Redux进行操作 store是一个调度者store是指挥者不干活。需要任何操作或者动作都会去分发走找到对应的担当来做。如果越过了storeaction直接去找reducers就有点类似去餐厅点餐不找前台点餐直奔后厨要吃的
action
action 是 store 中唯一的数据来源一般来说我们会通过调用 store.dispatch 将 action 分发到 store
我们需要传递的 action 是一个对象它必须要有一个 type 值如果是初始化就传init代表要初始化data值在第一次传参的时候可以为undefined然后由Reducers来初始化赋值。
reducers
在 Reducers 中我们需要指定状态的操作类型type要做怎样的数据更新因此这个类型是必要的。
因为Reducers中会有很多具体处理的Reducer所以这里Reducers代表很多处理Reducer的集合。
reducer 会根据 action 的指示对 state 进行对应的操作然后返回操作后的 state
另外Reducer可以加工状态加工的前提是有上一次的状态值如果没有状态值就要初始化一个状态值。有了状态值之后就可以进行下一步的加工。 手写一个Redux精简版
这里只是一个简化忽略了很多东西比如Creators创建Action的过程侧重于展示storereducer之间的关系 store.js文件 创建一个store的js导出供外界使用
import { createStore } from redux;
//引入为store服务的Reducer
import countReducer from ./count_reducer;
//手动创建一个store
const store createStore(countReducer);//全局只暴露这一个store对象
export default store;count_reducer.js文件 我们创建专门计数的reducer简单来说就是专门负责处理某件事的function这里取名叫作count_reducer
所有的action通过dispatch传进去之后类型数据都托管于store这个中心调用的组件只需要把 typedata传进去剩下的只需要等着从store中获取结果即可
/*作为一个reducer应该有如下功能Store传来previousStateactionReducer接收到参数做出判断previousState判断是否是空是空就得初始化。非空就按照action进行下一步操作action处理完之后把处理好的newState(previousState处理之后的版本)返回给store等待Store返回给React组件所以以上的这些操作只能用function。所以Reducers里的Reducer本质就是函数动作完成后将数据暂存给store等待后续组件获取值即可
*/export default function countReducer(previousState, action) {// 从Action对象里传来的action对象里面包含type和data所以我们需要解构出来。等待后续处理const { type, data } action;if (previousState undefined) {// 如果之前的状态是空的就初始化之后还给store中心// 或者可以这么写给参数给默认值// function countReducer(previousState0, action) {return 0;}// 判断传入功能类型switch (type) {case add://这里用原来的值新传入的值得到新值return previousState data * 1;case sub://这里用原来的值-新传入的值得到新值return previousState - data * 1;// 提一嘴这里都是return所以不用breakdefault:return previousState;}
}
Count.jsx文件 调用方组件
import React, { Component } from react;
// 获取store用于获取store中的状态
import store from ../redux/store;export default class Count extends Component {// 之前准备的state存储值count 都不用存在了因为已经托管给store了// 但是自己组件内部的值其实是还需要放在state的因为store只需要托管复杂共享的情况state {// 这个实际上就不要了因为保存在state中// countNumber: ,// 但是比如car这个属性只有自己用就没必要兜一圈放redux中了放自己组件里就挺好car: 威朗Pro GS,};add () {const { value } this.count;// store是分发中心告诉reducer要干的事情以及传入的数值// 我们只需要将 type要做的事情value原始值 告诉store 。让store去找对应的reducer操作即可// 对应的reducer拿到数值做出判断以及相应动作处理数据处理好之后return值在store中等待组件get// 但注意仅仅调用dispatch是不够的因为redux是第三方js。无法触发页面刷新// 所以需要检测Redux里状态改变时就去调用render。这里用到了订阅subscribe// 你都不用自己再解构接收值所有的函数处理值存储都在redux中做好了只需要我们从store中get结果即可// 获取store的地方因为被监听所以自动刷新了触发renderstore.dispatch({ type: add, data: value });store.subscribe(() {// 只要Redux里状态改变时就去调用render。这里用到了订阅subscribe。手动触发一下就行// 借助setState传个空值进去就可以触发render重新渲染// 当然我们也可以在index.js的根标签监听这个。监听整个App组件利用diffing算法全部更新避免性能下降this.setState({});});};sub () {const { value } this.count;store.dispatch({ type: sub, data: value });store.subscribe(() {// 手动监听触发页面重新渲染this.setState({});});};render() {console.log(store);return (div当前结果h1{store.getState()}/h1选择内容select ref{(c) (this.count c)}option value{1}1/optionoption value{2}2/optionoption value{3}3/option/selectbutton onClick{this.add}/buttonbutton onClick{this.sub}-/button/div);}
}总结一下精简案例
(1).去除Count组件自身的状态
(2).src下建立:-redux-store.js-count_reducer.js(3).store.js1).引入redux中的createStore函数创建一个store2).createStore调用时要传入一个为其服务的reducer3).记得暴露store对象(4).count_reducer.js1).reducer的本质是一个函数接收preState,action返回加工后的状态2).reducer有两个作用初始化状态加工状态3).reducer被第一次调用时是store自动触发的传递的preState是undefined,传递的action是:{type:REDUX/INIT_a.2.b.4}( _a.2.b.4是随机数为了避免和自己写的type名有重合)(5).在index.js中监测store中状态的改变一旦发生改变重新渲染App/备注redux只负责管理状态至于状态的改变驱动着页面的展示要靠我们自己写。完整版Redux
上面的案例没有写action的创建所以这里补齐就成为了完整版。 该文件专门为Count组件需要用的Reducer去生成action对象封装好type这样在调用组件传一个data进来就可以了
新增文件1.count_action.js 专门用于创建action对象2.constant.js 放置容易写错的type值store.js文件 创建一个store的js导出供外界使用。这个和上面的没区别这里不赘述了
count_action.js文件
/*该文件专门为Count需要用的Reducer去生成action对象封装好type这样在调用组件传一个data进来就可以了
*/
import { ADD } from ./constant.js;// 注意这里有个坑就是我希望在这里返回对象的箭头函数最外侧不能是花括号
// (data) { type: ADD, data }; 如果是这样会把 type: ADD, data 这部分识别为函数体就没法返回对象了
// 所以我们用括号给括起来就自动返回对象了
export const createAddAction (data) ({ type: ADD, data });
// 这两种写法等价
export function createIncrementAction(data) {// 要返回Action对象返回type和data数据return { type: ADD, data };
}count_reducer.js文件 本案例中主要是引入常量来表示Type避免了容易拼错的问题
import { ADD, SUB } from ./constant.js;export default function countReducer(previousState, action) {// 从Action对象里传来的action对象里面包含type和data所以我们需要解构出来。等待后续处理const { type, data } action;if (previousState undefined) {// 如果之前的状态是空的就初始化之后还给store中心// 或者可以这么写给参数给默认值// function countReducer(previousState0, action) {return 0;}// 判断传入功能类型switch (type) {case ADD://这里用原来的值新传入的值得到新值return previousState data * 1;case SUB://这里用原来的值-新传入的值得到新值return previousState - data * 1;// 提一嘴这里都是return所以不用breakdefault:return previousState;}
}
Count.jsx文件 调用方组件这里主要强调变化的部分省略了部分代码
import React, { Component } from react;
// 传入action函数我们只需要传入data即可
import { createAddAction } from ../redux/count_action;
// 获取store用于获取store中的状态
import store from ../redux/store;export default class Count extends Component {...省略add () {const { value } this.count;// 引入了action后就不需要我们手动定义type了在action中已经做好定义了我们只需要传入data即可// store.dispatch({ type: add, data: value });// 引入并且直接用count_action所封装好的函数来传入参数store.dispatch(createAddAction(value * 1));store.subscribe(() {// 监听store值变化重新renderthis.setState({});});};sub () {省略...};render() {return (省略...);}
}异步action版
action有两种类型根据返回值类型来区分
action的值为Object则为同步actionstore可以直接接收处理
//返回一个对象
export const createSubAction (data) ({ type: SUB, data });action的值为function则为异步action只有function才能开启异步任务并且store不可以直接接收处理异步action需要通过中间件处理后才能接收
export const createAddAsync (data,time) {return (){setTimeout(() {...省略}, time);}
};之前的异步add方法其本质还是在组件里写的可以看到等待的异步过程没有在Redux里面写
addAsync () {const { value } this.count;setTimeout(() {store.dispatch(createAddAction(value * 1));}, 5000);
};我们现在要把异步等待的操作放在Redux的Action Creators里面不放在组件里面等待了 调用方组件Count
addAsync () {const { value } this.count;//常规调用看似没问题store.dispatch(createAddAsync(value, 5000));store.subscribe(() {// 手动监听触发页面重新渲染也可以去监听App组件this.setState({});});
};action组件
export const createAddAsync (data, time) {return () {// 这里其实只套了一个定时操作setTimeout(() {// 通过store调用已经定义好的增加action省着我们再写了store.dispatch(createAddAction(data));}, time);};
};但实际上这是有问题的运行代码会报错
翻译过来就是store不直接接收action的值为function异步action。想要接收必须去用一个中间件让store允许接收函数 引入中间件npm install redux-thunk需要我们安装一下 引入完成之后就需要在store.js里面修改一下用于支持异步action。做的这一切只是为了让store可以接收异步action返回的函数 store.js
//引入store创建以及中间件
import { createStore, applyMiddleware } from redux;
//引入为store服务的Reducer
import countReducer from ./count_reducer;
// 引入thunk给applyMiddleware中间件用
import thunk from redux-thunk;
//手动创建一个store传入applyMiddleware(thunk)
const store createStore(countReducer, applyMiddleware(thunk));//全局只暴露这一个store对象
export default store;此时我们再看修改后的组件
调用方组件Count没有变化
addAsync () {const { value } this.count;store.dispatch(createAddAsync(value, 5000));store.subscribe(() {// 手动监听触发页面重新渲染也可以去监听App组件this.setState({});});
};action组件
// 异步action就是只action的值为函数在异步action中一般都会调用同步action异步action不是必须要用的
export const createAddAsync (data, time) {return () {// 这里其实只套了一个定时操作setTimeout(() {// 调用已经定义好的增加actionstore.dispatch(createAddAction(data));console.log(data, time);}, time);};// 不用store调用dispatch也可以因为dispatch会自动传进来一个这两种完全等价// return (dispatch) {// // 这里其实只套了一个定时操作// setTimeout(() {// // 调用已经定义好的增加action// dispatch(createAddAction(data));// console.log(data, time);// }, time);// };
};此时再测试不再报错。
配置完毕之后的store
如果我们给store传入一个普通类型的Object actionstore就会直接找Reducer去做处理
如果给store传入一个异步类型的Function action这个函数store就会帮你调用总结下来虽然异步的action调用的时候返回值是函数但是最后一般都会调用同步action来完成数据的操作
React18版本的store监听刷新
之前给的store subscribe监听刷新是React17版本的React18版本的可以参考这个 ReactDOM.render is no longer supported in React 18. 改造之后的index.js
// 引入React核心库
import React from react;
import { createRoot } from react-dom/client;
// 引入App标签
import App from ./App;
import store from ./redux/store;// React18版本监听并刷新页面
const root createRoot(document.getElementById(root));
root.render(App /);//监测redux中状态的改变如redux的状态发生了改变那么重新渲染App组件
store.subscribe(() {root.render(App /);
});总结异步action (1).明确延迟的动作不想交给组件自身想交给action(2).何时需要异步action想要对状态进行操作但是具体的数据靠异步任务返回。(3).具体编码1).npm install redux-thunk并配置在store中2).创建action的函数不再返回一般对象而是一个函数该函数中写异步任务。3).异步任务有结果后分发一个同步的action去真正操作数据。(4).备注异步action不是必须要写的完全可以自己等待异步任务的结果了再去分发同步action。React-Redux基础
安装npm install react-redux 安装不上去就用这个npm install react-redux --legacy-peer-deps 还不行就把package.json里面的这俩都删了在安装React-Redux
redux: ^4.2.1,
redux-thunk: ^2.4.2,引言
React-Redux是React专门出的Redux属于官方出品 宗旨在于将UI与redux分隔开所有的操作都要经由容器组件
引言-简化版连接UI组件与容器组件 前置知识
redux里的唯一的apiconnect;
使用的时候一般都是这么用connect()();
简单来说得分开看connect()是一个返回一个函数的方法 connect()()是在connect()返回函数后继续再次调用这个返回的函数 就有点类似下面这个调用connect()()最后会触发a里的输出OK 当然a也可以返回一个返回值
connect(){return a();
}a(){console.log(OK)
}首先明确文件应该放在哪个包下面 UI组件components包下 容器组件containers包下
且UI组件的里面不能有任何关于Redux相关的APIstoreactionCreatordispatch… 这些API都不能引入了。只能有UI组件以及UI组件动作相关的东西。在components里创建countTemplate.jsx countTemplate.jsx
import React, { Component } from react;export default class CountTemplate extends Component {// 纯UI组件页面操作//加法increment () {// 这个是获取页面的选择值const { value } this.selectNumber;};...//异步加incrementAsync () {const { value } this.selectNumber;};render() {//console.log(UI组件接收到的props是,this.props);return (divh1当前求和为{???}/h1select ref{(c) (this.selectNumber c)}option value11/optionoption value22/optionoption value33/option/selectnbsp;button onClick{this.increment}/buttonnbsp;button onClick{this.decrement}-/buttonnbsp;button onClick{this.incrementIfOdd}当前求和为奇数再加/buttonnbsp;button onClick{this.incrementAsync}异步加/buttonnbsp;/div);}
}
简化版里面容器组件更像是一个桥梁负责连接 UI组件与Redux 的交互。所以在Container里面创建一个count.jsx
在这个桥梁count.jsx里我们可以导入UI组件等待连接理论上应该导入Redux的Store就完成了但是不行store必须从App里面通过props传进来
我在container容器里面引入了store仍然报找不到的错这里是错误示范
//引入Count的UI组件
import CountTemplate from ../components/CountTemplate;
// 引入store
import { store } from ../../redux/store;
// 引入connect的API
import { connect } from reat-redux;// 导入connect并且连接UI
export default connect()(CountTemplate);我已经引进来store了但是还是提示找不到store这里不是因为我没有在connect里连接而是不允许这种用法。只能在调用Container组件的组件里传值用props进去。
注意react-redux不允许直接引入只能从Container组件被调用的那一级组件里传进来 App组件调用Container的组件。这样就不报错了
import React from react;
import Count from ./pages/Count;
import ./App.css;
import store from ./redux/store;
class App extends React.Component {render() {return (div{/* 只能用props传递store进入Container组件 */}Count store{store}/Count/div);}
}
export default App;省略store.subscribe
首先就是之前的store.subscribe之前我们在Redux里面由于组件和Redux之间没有直接监听更新的手段。所以这里需要手动去监听渲染组件
// 引入React核心库
import React from react;
import { createRoot } from react-dom/client;
// 引入App标签
import App from ./App;
import store from ./redux/store;// React18版本监听并刷新页面
const root createRoot(document.getElementById(root));
root.render(App /);//监测redux中状态的改变如redux的状态发生了改变那么重新渲染App组件
store.subscribe(() {root.render(App /);
});而在React-Redux中connect就自动集成了监听并更新的功能所以我们不必再手动监听。删掉即可测试完美替换不影响功能。
// 引入React核心库
import React from react;
import { createRoot } from react-dom/client;
// 引入App标签
import App from ./App;// React18版本监听并刷新页面
const root createRoot(document.getElementById(root));
root.render(App /);React-Redux的基本使用
上面说到Container想接受到store只能通过props形式传递但是Container本身并不是标签形式的所以就用不了props来传递数据。要想把Container接收到的props传给UI组件就只能用connect的API通过函数返回对象的形式来传递 首先看这个Container组件是没有办法去写子组件传值的 也就是CountTemplate key1{value1} ...这种是没有机会写的 所以我们只能依靠connect传值
//引入Count的UI组件
import CountTemplate from ../../components/Count/CountTemplate;
// 引入connect的API
import { connect } from react-redux;// 使用connect()()创建并暴露一个Count的容器组件
export default connect()(CountTemplate);props是key-value的形式所以我们要来模仿这种形式 这里就采用了对象的形式来模拟这种key-value {key:value}或者{key:(){函数体}} 也就是说props的形式都可以模拟 修改一下Container的Count组件
//引入Count的UI组件
import CountTemplatefrom ../../components/Count/CountTemplate;
// 引入connect的API
import { connect } from react-redux;function a() {// a函数返回的对象中的key就作为传递给UI组件props的keyvalue就作为传递给UI组件props的value这个value是状态return { key1: value1 };
}function b() {// a函数返回的对象中的key就作为传递给UI组件props的keyvalue就作为传递给UI组件props的value这个value是函数return {key2: () {console.log(OK);},};
}
// 使用connect建并暴露一个Count的容器组件传ab函数的返回值给UI组件
export default connect(a, b)(CountTemplate);
UI组件获取参数 在UI组件打印一下console.log(UI组件接收到的props是, this.props); 所以想获取就更简单了直接this.props.key即可
注意a传值的时候有点小坑我们应该专注于状态的传递。所以优化一下
function a(state) {// 这里直接传state即可这个state是从App传过来的// 所有的状态就直接用这个state给传过去了注意是所有的state// key1是自定义的不设限return { key1: state };
}同样b里如果要调用dispatch函数本应该import store
import store from ../../redux/store;
import { createAddAction } from ../../redux/count_action;function b() {// 返回值返回value为函数的对象// add是自定义的不设限仅代表当前返回值的函数return {add: (number) store.dispatch(createAddAction(number)),};
}但是由于Container里面不应该引入store并且dispatch可以自动传入所以就不用通过导入store来调用dispatch了。和上面的state一样自动传入dispatch 改造后
//这个是自定义的action函数不是redux的函数
import { createAddAction } from ../../redux/count_action;function b(dispatch) {// 自动传入了dispatchreturn {// 不再需要用store调用dispatch // 这种就可以简写了add: (number) store.dispatch(createAddAction(number)),add: (number) dispatch(createAddAction(number)),};
}案例总结
demo结构重点主要集中在Container上
这四个文件照之前的没有任何变化所以不赘述了 UI组件 CountTemplate.jsx
import React, { Component } from react;export default class Count extends Component {add () {// add操作const { value } this.selectNumber;// 找到传入函数并传参this.props.add(value * 1);};addNotOdd () {// 奇数add操作const { value } this.selectNumber;if (this.props.key1 % 2 ! 0) {this.props.add(value * 1);}};addAsync () {// 异步add操作const { value } this.selectNumber;setTimeout(() {this.props.add(value * 1);}, 500);};render() {return (divdiv当前求和:{this.props.key1}/divselect ref{(c) (this.selectNumber c)}option value11/optionoption value22/optionoption value33/option/selectdivbutton onClick{this.add}add/buttonbutton onClick{this.sub}sub/buttonbutton onClick{this.addNotOdd}addNotOdd/buttonbutton onClick{this.addAsync}addAsync/button/div/div);}
}Container组件 Countainer.jsx
import { connect } from react-redux;
import CountTemplatefrom ../../components/count/CountTemplate;
import { add } from ../../redux/count_action;function a(store) {return { key1: store };
}function b(dispatch) {// 允许传递多个函数return {// add操作的函数add: (data) dispatch(add(data)),// sub操作的函数sub: (data) dispatch(sub(data)),};
}// 传入func a负责state传递 func b负责函数动作传递
// 最后桥梁连接CountTemplate组件
export default connect(a, b)(CountTemplate);App.jsx
import React from react;
import Container from ./pages/container/Container;
import store from ./redux/store;
import { BrowserRouter } from react-router-dom;class App extends React.Component {render() {return (BrowserRouter{/* 在App向Container传递storeprops形式 */}Container store{store}/Container/BrowserRouter);}
}
export default App;index.js
// 引入React核心库
import React from react;
import { createRoot } from react-dom/client;
// 引入App标签
import App from ./App;
import store from ./redux/store;// React18版本监听并刷新页面
const root createRoot(document.getElementById(root));
root.render(App /);//监测redux中状态的改变如redux的状态发生了改变那么重新渲染App组件
store.subscribe(() {root.render(App /);
});以上就可以完成计算组件 Container命名优化引出规定的函数名
传值的函数可以看到实际上所有的state和function的参数传递通过一次就可以完全传递完。 所以react-redux里面就提供好了函数名专门传递state和function就不需要我们再去单独定义了。更加规范了。
// 函数命名不规范
function a(store) {return { key1: store };
}
// 函数命名不规范
function b(dispatch) {// 允许传递多个函数return {// add操作的函数add: (data) dispatch(add(data)),// sub操作的函数sub: (data) dispatch(sub(data)),};
}// 使用connect建并暴露一个Count的容器组件传ab函数的返回值给UI组件
export default connect(a, b)(CountUI);connect 方法是一个连接器用于连接容器组件和 UI 组件它第一次执行时接收4个参数这些参数都是可选的它执行的执行的结果还是返回一个函数第二次执行接收一个 UI 组件
connect方法第一次执行时connect()的四个参数mapStateToProps 、mapDispatchToProps 、mergeProps、options connect方法第二次执行时传入的UI组件connect()(UI组件)
这里先说传state和传方法的两个函数 mapStateToProps函数返回的是一个对象对象value是statemapStateToProps用于传递状态 mapDispatchToProps函数返回的是一个对象对象value是functionmapDispatchToProps用于传递操作状态的方法
这只是官方推荐的API定义方式后面还有更简写的方式来传递state和function
官方解释mapStateToPropsmapDispatchToProps
具体在UI获取时候的key还是需要看return的对象把key定义成什么才能用this.props来获取key 同时之前的定时加操作是在UI组件里面做的并没有放在count_action.js里面所以把定时加操作搬进count_action.js优化之后 Container.jsx:
import { connect } from react-redux;
import CountUI from ../../components/count/CountUI;
import { add, sub, addAsync } from ../../redux/count_action;// 传递state
function mapStateToProps(store) {return { key1: store };
}// 传递dispatch
function mapDispatchToProps(dispatch) {// 允许传递多个函数 通过dispatch通知Redux执行函数return {// add操作的函数add: (data) dispatch(add(data)),// sub操作的函数sub: (data) dispatch(sub(data)),// addAsync操作的函数addAsync操作搬进action里addAsync: (data, time) dispatch(addAsync(data, time)),};
}// 传入func mapStateToProps负责state传递 func mapDispatchToProps负责函数动作传递
// 最后传入CountTemplate组件完成连接
export default connect(mapStateToProps, mapDispatchToProps)(CountUI);ConuntTemplate.jsx:
import React, { Component } from react;export default class Count extends Component {...addAsync () {const { value } this.selectNumber;this.props.addAsync(value * 1, 500);};render() {return (div.../div);}
}count_action.js:
/*
该文件专门为Count需要用的Reducer去生成action对象封装好type这样在调用组件传一个data进来就可以了
*/
import { ADD, SUB, ADD_ASYNC } from ./constant.js;export function add(data) {// 要返回Action对象返回type和data数据return { type: ADD, data };
}export function sub(data) {// 要返回Action对象返回type和data数据return { type: SUB, data };
}// 把这个定时操作集成在action里面 addAsync
// setTimeout(() {
// this.props.add(value * 1);
// }, 500);
export function addAsync(data, time) {// dispatch为自动传入return (dispatch) {setTimeout(() {// 通过dispatch调用add直接调用是不可以的dispatch(add(data));}, time);};
}其他的文件没啥变化不赘述了
写到这里其实 connect 已经比较完善了但是你可以仔细想想 redux 的工作流程 似乎少了点什么我们在这里调用了函数创建了 action 对象但是好像 store 并没有执行 dispatch 那是不是断了呢执行不了呢
其实这里 react-redux 已经帮我们做了优化当调用 Action Creator 的时候会立即发送 action 给 store 而不用手动的 dispatch。后面马上会用到这个。
总结React-Redux的基本使用
(1).明确两个概念1).UI组件:不能使用任何redux的api只负责页面的呈现、交互等。2).容器组件负责和redux通信将结果交给UI组件。
(2).如何创建一个容器组件————靠react-redux 的 connect函数connect(mapStateToProps,mapDispatchToProps)(UI组件)-mapStateToProps:映射状态返回值是一个对象对象以k:v形式保存state-mapDispatchToProps:映射操作状态的方法返回值是一个对象对象以k:v形式保存多个方法
(3).备注1容器组件中的store是靠props传进去的而不是在容器组件中直接引入
(4).备注2mapDispatchToProps也可以是一个对象只传递一个方法如果多个方法就需要多个k:v对象React-Redux优化
之前的案例很多功能都是杂糅到一起的如果体量大起来会让人不知道该怎么办所以这里在这个案例里面对之前的功能代码进行拆分操作分文件处理。优化文件结构。
前置知识
例子1 function mapStateToProps(store) {return { key1: store };}可以用箭头函数来写
const mapStateToProps (store) {return { key1: store };}如果箭头函数只有一句并且默认return只有一个对象就可以省略return直接箭头指一个括号就代表return值了 ({ key1: store }) 最后优化完
const mapStateToProps (store) ({ key1: store })例子2
function mapDispatchToProps(dispatch) {// 多个方法传递return {add: (data) dispatch(add(data)),sub: (data) dispatch(sub(data)),addAsync: (data, time) dispatch(addAsync(data, time)),};
}按照上面的说法反正只有一个return这里就可以直接优化成
mapDispatchToProps(dispatch){add: (data) dispatch(add(data)),sub: (data) dispatch(sub(data)),addAsync: (data, time) dispatch(addAsync(data, time)),}带入到connect里面甚至不用写属性直接把函数体丢进去即可
简写 mapStateToProps mapDispatchToProps
对于connect来说第一次调用connect()传入什么名字的函数不重要传入的位置很重要connect只认位置上的传入的值。 简写完前后对比
/*
// 传统写法function mapStateToProps(store) {return { key1: store };}function mapDispatchToProps(dispatch) {// 多个方法传递return {add: (data) dispatch(add(data)),sub: (data) dispatch(sub(data)),addAsync: (data, time) dispatch(addAsync(data, time)),};}
*/// 引入connect生成一个容器组件连接好React-Redux和UI组件并暴露
// UI组件中依旧是通过this.props.xxxxxxx读取和操作状态
// export default connect(mapStateToProps, mapDispatchToProps)(CountUI);
export default connect(// 对于第一个参数位置来说首次调用可以传四个参数[ mapStateToProps 、mapDispatchToProps 、mergeProps、options ]//store没有React-Redux给你自动调用所以这里要自己写(state) ({ count: state }), // 等价于mapStateToProps //mapDispatchToProps的简写key:函数名甚至不需要标注参数列表//dispatch有React-Redux给你自动调用所以这里不用写了{ add: add, sub: sub, addAsync: addAsync }//等价于mapDispatchToProps
)(CountUI);完整开发
目录结构
首先就是把Contrainer和UI组件放在一起合并成一个Container文件叫Count。虽然文件是在一起但是功能和类都是分开的。只是文件放一起了对外依旧暴露ContainerUI这次彻底不用export了。
import { connect } from react-redux;
import { add, sub, addAsync } from ../../redux/count_action;
import React, { Component } from react;// 这次CountUI彻底不用暴露了暴露的任务交给connect来做。connect生成一个对外的Container同时包含了数据和UI组件
// export default class Count extends Component {
class CountUI extends Component {add () {const { value } this.selectNumber;this.props.add(value * 1);};sub () {const { value } this.selectNumber;if (this.props.key1 0 || this.props.key1 value) {return;}this.props.sub(value * 1);};addNotOdd () {const { value } this.selectNumber;if (this.props.key1 % 2 ! 0) {this.props.add(value * 1);}};addAsync () {const { value } this.selectNumber;this.props.addAsync(value * 1, 500);};render() {console.log(this.props);return (divdiv当前求和:{this.props.key1}/divselect ref{(c) (this.selectNumber c)}option value11/optionoption value22/optionoption value33/option/selectdivbutton onClick{this.add}add/buttonbutton onClick{this.sub}sub/buttonbutton onClick{this.addNotOdd}addNotOdd/buttonbutton onClick{this.addAsync}addAsync/button/div/div);}
}/*function mapStateToProps(store) {return { key1: store };}function mapDispatchToProps(dispatch) {// 多个方法传递return {add: (data) dispatch(add(data)),sub: (data) dispatch(sub(data)),addAsync: (data, time) dispatch(addAsync(data, time)),};}
*/// 引入connect生成一个容器组件连接好React-Redux和UI组件并暴露
// UI组件中依旧是通过this.props.xxxxxxx读取和操作状态
// export default connect(mapStateToProps, mapDispatchToProps)(CountUI);
export default connect((state) ({ key1: state }), // 状态{ add: add, sub: sub, addAsync: addAsync } // 方法不再需要注明参数列表
)(CountUI);
Provider组件使用
首先看个之前的store传递很麻烦需要手动传递如果每个标签都传递都需要使用 store 时很麻烦的
Count store{store}/
{/* 示例 */}
Demo1 store{store}/
Demo1 store{store}/
Demo1 store{store}/
Demo1 store{store}/
Demo1 store{store}/所以官方就提供了一个大组件Provider自动可以寻找需要用store的标签自动精准传递store进去
我们可以这么做在 src 目录下的 index.js 文件中引入 Provider 直接用 Provider 标签包裹 App 组件将 store 写在 Provider 中即可
import { Provider } from react-redux;ReactDOM.render(Provider store{store}App //Provider,document.getElementById(root)
);这样我们在 App.jsx 文件中组件无需手写指定 store 即可使用 store 非常方便
总结React-Redux优化
1.容器组件和UI组件整合一个文件
2.若有多个容器组件无需自己给每个容器组件传递store给包裹一个Provider store{store}即可。整体就贡献同一个store了。
3.使用了react-redux后也不用再自己检测redux中状态的改变了容器组件可以自动完成这个工作。
4.mapDispatchToProps也可以简单的写成一个对象因为react-redux可以自动dispatch
5.一个组件要和react-redux“打交道”要经过哪几步
1定义好UI组件—不暴露2引入connect生成一个容器组件并暴露写法如下
connect(state ({key:value}), //映射状态{key:xxxxxAction} //映射操作状态的方法
)(UI组件)3在UI组件中通过this.props.定义好的key名 读取和操作状态
React-Redux综合案例多组件组合共享
引言
在写完了基本的 Redux 案例之后我们可以尝试一些更实战性的操作比如我们可以试试多组件间的状态传递相互之间的交互 如上动图所示我们想要实现上面的案例采用纯 React 来实现是比较困难的我们需要很多层的数据交换才能实现但是我们如果采用 Redux 来实现会变得非常简单
因为 Redux 打通了组件间的隔阂完成了复杂组件的数据交互我们可以自由的进行数据交换所有存放在 store 中的数据都可以实现共享那我们接下来看看如何实现的吧~
整合UI组件与容器组件
如果UI组件和容器组件都分开写那么实际上文件数量就得成倍增长为了优化就整合一下两个文件把UI组件和容器组件 放在一起。实际开发中也是整合的。
首先一个jsx文件里面可以定义多个组件其实思想上还是容器组件往UI组件传props只不过两个组件放在了同一个文件里对外只暴露一个Container接口。
这里演示一下两个或者多个组件在同一个文件里 创建一个Test组件
import React, { Component } from react;// 默认对外暴露的容器组件
export default class Test extends Component {render() {return divTest/div;}
}// UI组件不对外暴露但是可以在自己文件里面用
class Test2 extends Component {render() {return divUI组件/div;}
}
// 他俩之间的连接靠connect
// 这是个简单的connect后面有详细的描述
export default connect((state) ({对象}),{// action动作}
)(CountUI);所以以后就可以只留一个Container文件夹放容器组件
UI组件只要想去拿Redux的状态就直接找内部的connect因为这个connect已经拿到了Redux的所有state 后面有详细的描述
重新安排Redux文件结构
如果有Person和Count的组件每个组件对应的action和reducer这两个文件都是成倍的增长所以肯定不能这么一大堆罗列于此就要把Redux文件夹分层 分层后的Redux将action文件和reducer文件分开
就不用再叫 组件_action.js或 组件_reducer.js 的这种了因为已经在action文件夹下面了根据文件夹就能分辨出来
React-Redux结构 合并Reducer
React-Redux结构
Redux里用的存储结构 如果是只存了一个值比如数字这种那就是不用key去取直接就this.store即代表着当前对象。
如果是存了两个值以上n个对象那就是Redux作为一个大对象里面存了n多个小对象通过key来获取目标对象key是合并reducer时定义的
合并Reducer
之前的store中只注册了一个Reducer并导出对于多个Reducer来说只导出一个肯定是不行的所以我们要将其他Reducer也注册到store里面并且命名好key来帮助后续获取
这里提一个前置知识combineReducers({对象})传入的对象就是Redux保存的总对象 重要事情说三遍~ combineReducers({对象})传入的所有对象就是Redux保存的总对象 combineReducers({对象})传入的所有对象就是Redux保存的总对象 combineReducers({对象})传入的所有对象就是Redux保存的总对象 合并后的store.js
//引入store创建以及中间件
import { combineReducers,createStore } from redux;
//引入为store服务的Reducer
import countReducer from ../redux/reducer/count;
import personReducer from ../redux/reducer/person;// 之前的store只能注册一个reducer也就是count的countReducer
// 没法注册person的reducer所以我们需要用函数把两个函数都注册了
// const store createStore(countReducer, applyMiddleware(thunk));// 合并
const allReducer combineReducers({// sum就是countReducer的keysum: countReducer,// persons就是personReducer的keypersons: personReducer,
});
//全局只暴露这个Reducers同样需要用createStore函数创建
export default createStore(allReducer);Container通过key获取对应的状态 Count组件的connect
// 使用connect建并暴露一个Count的容器组件传ab函数的返回值给UI组件
/*(state) ({ sumNumber: state.sum })对应的含义(默认传入的全局Redux对象) ({ 自定义的在UI组件使用的名字: 默认传入的全局Redux对象.目标对象的key })
*/
export default connect(// 这里还获取了Person组件的人数长度通过Redux获取其他组件的值// 这里可以传入多个 k-v 用逗号分隔(state) ({ sumNumber: state.sum, personLength: state.persons.length }),{// action动作和以前一样传递即可add: add,sub: sub,addAsync: addAsync,}
)(CountUI);Person组件的connect
/*(state) ({ personList: state.persons }),对应的含义(默认传入的全局Redux对象) ({ 自定义的在UI组件使用的名字: 默认传入的全局Redux对象.目标对象的key }),
*/
export default connect(// 这里还获取了Count组件的求和值通过Redux获取其他组件的值// 这里可以传入多个 k-v 用逗号分隔(state) ({ personList: state.persons, countSum: state.sum }),{// 这里绑定的action该咋传咋传addPerson: addPerson,}
)(PersonUI);整个案例代码
先看结构 容器组件Count.jsx
// 引入connect的API
import { connect } from react-redux;
import { add, sub, addAsync } from ../../redux/action/count;import React, { Component } from react;class CountUI extends Component {add () {const { value } this.count;this.props.add(value);};sub () {const { value } this.count;this.props.sub(value);};addNotOdd () {if (this.props.sumNumber % 2 ! 0) {this.props.add(value);}};addAsync () {const { value } this.count;this.props.addAsync(value, 500);};render() {console.log(this.props);return (divh1当前结果{this.props.sumNumber}下方组件总人数为{this.props.personLength}/h1选择内容select ref{(c) (this.count c)}option value{1}1/optionoption value{2}2/optionoption value{3}3/option/selectbutton onClick{this.add}/buttonbutton onClick{this.sub}-/buttonbutton onClick{this.addNotOdd}当前奇数加/buttonbutton onClick{this.addAsync}非同步加/button/div);}
}// 使用connect建并暴露一个Count的容器组件传ab函数的返回值给UI组件
/*(state) ({ sumNumber: state.sum })对应的含义(默认传入的全局Redux对象) ({ 自定义的在UI组件使用的名字: 默认传入的全局Redux对象.目标对象的key })
*/
export default connect(// 这里还获取了Person组件的人数长度通过Redux获取其他组件的值// 这里可以传入多个 k-v 用逗号分隔(state) ({ sumNumber: state.sum, personLength: state.persons.length }),{// action动作和以前一样传递即可add: add,sub: sub,addAsync: addAsync,}
)(CountUI);
容器组件Person.jsx
import React, { Component } from react;
import { addPerson } from ../../redux/action/person;
import { nanoid } from nanoid;
import { connect } from react-redux;class PersonUI extends Component {addPerson () {const name this.name.value;const age this.age.value;if (name || age ) {return;}const newPeson { id: nanoid(), name, age };this.props.addPerson(newPeson);};render() {return (divh1我是person组件上方组件求和为{this.props.countSum}/h1inputref{(name) {this.name name;}}/inputinputref{(age) {this.age age;}}/inputbutton onClick{this.addPerson}add New Person/buttonul{this.props.personList.map((p) {return (li key{p.id}name: {p.name}---- age: {p.age}/li);})}/ul/div);}
}/*(state) ({ personList: state.persons }),对应的含义(默认传入的全局Redux对象) ({ 自定义的在UI组件使用的名字: 默认传入的全局Redux对象.目标对象的key }),
*/
export default connect(// 这里还获取了Count组件的求和值通过Redux获取其他组件的值// 这里可以传入多个 k-v 用逗号分隔(state) ({ personList: state.persons, countSum: state.sum }),{// 这里绑定的action该咋传咋传addPerson: addPerson,}
)(PersonUI);Action下的count.js import { ADD, SUB } from ../constant.js;
import store from ../store.js;export const add (data) ({ type: ADD, data });
export const sub (data) ({ type: SUB, data });// 异步action就是只action的值为函数在异步action中一般都会调用同步action异步action不是必须要用的
// 能在这里写函数是因为在store做了redux-thunk的设置
export const addAsync (data, time) {return () {// 这里其实只套了一个定时操作setTimeout(() {// 调用已经定义好的增加actionstore.dispatch(add(data));}, time);};
};Action下的person.js
import { ADD_PERSON } from ../constant;export const addPerson (data) ({ type: ADD_PERSON, data });reducer下的count.js
import { ADD, SUB } from ../constant.js;export default function countReducer(previousState, action) {// 从Action对象里传来的action对象里面包含type和data所以我们需要解构出来。等待后续处理const { type, data } action;if (previousState undefined) {return 0;}// 判断传入功能类型switch (type) {case ADD://这里用原来的值新传入的值得到新值return previousState data * 1;case SUB:if (previousState data * 1) {return previousState;}//这里用原来的值-新传入的值得到新值return previousState - data * 1;// 提一嘴这里都是return所以不用breakdefault:return previousState;}
}reducer下的person.js
import { ADD_PERSON } from ../constant.js;const initPersonList [{ id: 123456, name: Tom, age: 20 }];export default function personReducer(previousState initPersonList, action) {const { type, data } action;switch (type) {case ADD_PERSON:// 把之前的展开之前的数组是没展开的把新来的加进去return [data, ...previousState];default:return previousState;}
}常量constant.js
export const ADD add;
export const SUB sub;
export const ADD_PERSON addPerson;store.js
//引入store创建以及中间件
import { applyMiddleware, combineReducers, createStore } from redux;
//引入为store服务的Reducer
import countReducer from ../redux/reducer/count;
import personReducer from ../redux/reducer/person;
import thunk from redux-thunk;// 之前的store只能注册一个reducer也就是count的countReducer
// 没法注册person的reducer所以我们需要用函数把两个函数都注册了
// const store createStore(countReducer, applyMiddleware(thunk));// 合并
const allReducer combineReducers({// sum就是countReducer的keysum: countReducer,// persons就是personReducer的keypersons: personReducer,
});// 全局只暴露这个Reducers同样需要用createStore函数创建
// 同时允许接收函数的applyMiddleware(thunk)也不能丢了
export default createStore(allReducer, applyMiddleware(thunk));App.jsx
class App extends React.Component {render() {return (divProvider store{store}{/* 用Provider组件的props传递store进入Container组件 */}// 被Provider组件包裹的子组件都能自动接收到store// 这俩都是容器组件Count/CountPerson/Person/Provider/div);}
}
export default App;发现个小bug 原因是没有弄redux-thunk做中间件支持再详细可以看这个redux-thunk
总结React-Redux综合案例
(1).定义一个Pserson组件和Count组件通过redux共享数据。
(2).为Person组件编写reducer、action配置constant常量。
(3).重点Person的reducer和Count的Reducer要使用combineReducers进行合并合并后的总状态是一个对象
(4).交给store的是总reducer最后注意在组件中取出状态的时候记得“取到位”。纯函数概念
就是一个概念没有编码
引言
1.一类特别的函数: 只要是同样的输入(实参)必定得到同样的输出(返回)
2.必须遵守以下一些约束
1)不得改写参数数据2)不会产生任何副作用例如网络请求输入和输出设备3)纯函数里面不能调用Date.now()或者Math.random()等不纯的方法
3.redux的reducer函数必须是一个纯函数
例子
纯函数要求输入和输出相同 比如
// a是纯函数
function a(data) {return data;
}
// b不是纯函数
function b(data) {let c 1;return c;
}再拿一个reducer举个例子 这里的 return [data, ...previousState];。因为地址引用发生了变化所以就能触发页面更新。因为Redux是浅比较不比较对象内容发现return的引用地址变化了就自动更新了。
export default function personReducer(previousState initPersonList, action) {const { type, data } action;switch (type) {case ADD_PERSON:// 把之前的展开之前的数组是没展开的把新来的加进去return [data, ...previousState];default:return previousState;}
}如果是非纯函数比如这样。这就无法生效因为不是纯函数
export default function personReducer(previousState initPersonList, action) {const { type, data } action;
switch (type) {case ADD_PERSON:var newArray previousState;newArray.push(data);return newArray;default:return previousState;}
}Redux-DevTools
顾名思义开发者工具
需要插件npm库的依赖 npm install redux-devtools-extension --legacy-peer-depsstore需要导入redux-devtools-extension的依赖并且在暴露store的connect函数的第二个参数位置传入对应的api 如果之前有applyMiddleware(thunk)的中间件操作可以选择将中间件传入其api
...省略
// redux-devtools api
import { composeWithDevTools } from redux-devtools-extension;// 合并
const allReducer combineReducers({// sum就是countReducer的keysum: countReducer,// persons就是personReducer的keypersons: personReducer,
});export default createStore(allReducer,// redux-devtools apicomposeWithDevTools(applyMiddleware(thunk))
);
再次启动项目就可以在浏览器的插件里面看redux存进去的东西了 总结工具使用
(1).yarn add redux-devtools-extension
(2).store中进行配置import {composeWithDevTools} from redux-devtools-extensionconst store createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))Reducers优化
之前store中所有的Reducers都是积压在store中进行的合并如果是大型项目将难以维护所以我们一般把Reducers的合并放在一个单独的js文件中去做完成导出导出完毕之后在store中引入即可 在reducer下专门新建一个index文件完成汇总合并操作
/*该文件用于汇总所有的reducer为一个总的reducer
*/
import { combineReducers } from redux;//引入为store服务的Reducer
import countReducer from ./count;
import personReducer from ./person;
// 合并
const allReducer combineReducers({// sum就是countReducer的keysum: countReducer,// persons就是personReducer的keypersons: personReducer,
});// 导出
export default allReducer;精简之后的store文件非常清爽
//引入store创建以及中间件
import { applyMiddleware, createStore } from redux;
import thunk from redux-thunk;
import { composeWithDevTools } from redux-devtools-extension;// 引入reducers
import allReducer from ./reducer;export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk))
);总结
(1).所有变量名字要规范尽量触发对象的简写形式。
(2).reducers文件夹中编写index.js专门用于汇总并暴露所有的reducer