一、认识高阶函数和高阶组件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0gpKJOwy-1649342396303)(img/高阶函数.png)]
2.应用–可以增加原有组件没有的props
在这里面,把home类组件变成了高阶组件,并且给它增加了region这个属性
import React, { PureComponent } from 'react'
// 定义一个高阶函数
function enhanceComponent(WrappedComponent) {
return class NewComponent extends PureComponent {
render() {
return <WrappedComponent {...this.props} region="韩国" />
}
}
}
class Home extends PureComponent {
render() {
return (
<div>
<h2>home: {`昵称${this.props.name} 年龄: ${this.props.age} 地区:${this.props.region}`}</h2>
</div>
)
}
}
const EnhanceHome = enhanceComponent(Home)
export default class App extends PureComponent {
render() {
return (
<div>
App
<EnhanceHome name="coderwhy" age={18} region='中国'></EnhanceHome>
<Home name="coderwhy" age={18} region='中国'></Home>
</div>
)
}
}
3.应用–高阶组件劫持context对象里面的数据,将其增加到相应组件的props里面
import React, { PureComponent, createContext } from 'react'
const userContext = new createContext({
name: 'coderwhy',
age: 18,
region: '中国'
})
// 定义一个高阶函数
function enhanceComponent(WrappedComponent) {
return class NewComponent extends PureComponent {
render() {
return <WrappedComponent {...this.props} region="韩国" />
}
}
}
// 因为给每一个组件都增加context属性很麻烦,所以可以使用高阶组件,将他们包裹,增加props
function WithUser(WrappedComponent) {
return class NewComponent extends PureComponent{
render() {
return (
<userContext.Consumer>
{
(user) => {
return (
<WrappedComponent {...this.props} {...user}/>
)
}
}
</userContext.Consumer>
)
}
}
}
// class Home extends PureComponent {
// render() {
// return (
// <userContext.Consumer>
// {
// (user) => {
// return (
// <div>
// <h2>home: {`昵称${user.name} 年龄: ${user.age} 地区:${user.region}`}</h2>
// </div>
// )
// }
// }
// </userContext.Consumer>
// )
// }
// }
class Home extends PureComponent {
render() {
return (
<div>
<h2>home: {`昵称${this.props.name} 年龄: ${this.props.age} 地区:${this.props.region}`}</h2>
</div>
)
}
}
// Home.contextType = userContext
class About extends PureComponent {
render() {
return (
<div>
<h2>about: {`昵称${this.props.name} 年龄: ${this.props.age} 地区:${this.props.region}`}</h2>
</div>
)
}
}
const UserHome = WithUser(Home)
const UserAbout = WithUser(About)
const EnhanceHome = enhanceComponent(Home)
export default class App extends PureComponent {
render() {
return (
<div>
<h2>App</h2>
enhanceHome
<EnhanceHome></EnhanceHome>
about&&home
<userContext.Provider value={{ name: 'sasha', age: 18, region: '中国' }}>
<About></About>
<UserHome></UserHome>
<Home></Home>
<UserAbout></UserAbout>
</userContext.Provider>
</div>
)
}
}
4.应用–渲染判断鉴权—有点像路由守卫
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cQaT3edQ-1649342396306)(img/渲染判断鉴权.png)]
5.应用–生命周期劫持
利用高阶函数来劫持生命周期,在生命周期中来完成自己的逻辑
import React, { PureComponent } from 'react'
// 高阶组件
function getRenderTime(WrappedComponent) {
// 这里只能用类组件,因为类组件里面可以使用this
return class NewComponent extends PureComponent {
UNSAFE_componentWillMount() {
this.beginTime = Date.now()
}
componentDidMount() {
this.endTime = Date.now()
const interval = this.endTime - this.beginTime
// 可以使用.name属性来获取组件名称
console.log(WrappedComponent.name,' render时间: ', interval)
}
render() {
return <WrappedComponent></WrappedComponent>
}
}
}
// 类组件
class Home extends PureComponent {
render() {
return (
<div>Home</div>
)
}
}
// 函数组件
function About() {
return (
<div>
About
</div>
)
}
const RenderTimeHome = getRenderTime(Home)
const RenderTimeAbout = getRenderTime(About)
export default class App extends PureComponent {
render() {
return (
<div>
<RenderTimeHome/>
<RenderTimeAbout/>
</div>
)
}
}
6.高阶组件的意义
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pk9p8VqJ-1649342396308)(img/高阶函数的意义.png)]
二、组件其他内容补充
1.ref的转发
类组件上使用ref可以获得类组件上的state.props,refs,方法等信息
函数组件上不可以直接使用ref,会报错,需要使用react.forwardRef将这个函数组件包裹起来,这样的话,函数组件就可以有两个参数,第一个为props,接受父组件传过来的数据信息,第二个为ref,接受父组件传过来的ref信息,接受之后可以将它赋值给该函数组件上需要使用的元素,如下about组件里面赋值给了div元素
import React, { PureComponent, createRef, forwardRef } from 'react'
class Home extends PureComponent {
constructor() {
super()
this.state = {
counter: 0
}
}
render() {
return (
<div ref="homediv">
Home{this.state.counter}
</div>
)
}
}
// function About() {
// return (
// <div>
// about
// </div>
// )
// }
// forwardRef已经帮我们封装好了一个高阶组件, 有两个参数,可以帮助我们转发ref
const About = forwardRef(function (props, ref) {
return (<div ref={ref}> about</div>)
})
export default class App extends PureComponent {
constructor(props) {
super(props)
this.titleRef = createRef()
this.homeRef = createRef()
this.aboutRef = createRef()
}
render() {
return (
<div>
<h2 ref={this.titleRef}>hello world</h2>
<Home ref={this.homeRef}></Home>
{/* 函数组件不可以被赋予ref对象,并且ref不会被认为是props */}
<About ref={this.aboutRef}></About>
<button onClick={() => this.printRef()}>打印ref</button>
</div>
)
}
printRef() {
console.log(this.titleRef.current);
console.log(this.homeRef.current);
console.log(this.aboutRef.current);
}
}
2.React.Portals的使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HyOgsyA5-1649342396309)(img/Portals的使用.png)]
import React, { PureComponent } from 'react'
import ReactDOM from 'react-dom'
class Model extends PureComponent {
render() {
// 他的功能是将Model组件渲染到id为model的标签里面
return ReactDOM.createPortal(this.props.children, document.getElementById('model'))
}
}
export default class Home extends PureComponent {
render() {
return (
<div>
<h2>Home</h2>
<Model>
<h2>Title</h2>
</Model>
</div>
)
}
}
3.React.Fragment
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NrZ6IIyJ-1649342396310)(img/fragment的使用.png)]
4.React.StrictMode
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JKf72vMM-1649342396311)(img/严格模式.png)]
三、react中的css
1.react–css中出现的问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GzoZwvG5-1649342396312)(img/React中的css.png)]
2.内联样式
import React, { PureComponent } from 'react'
export default class App extends PureComponent {
constructor() {
super()
this.pstyle = {
color: 'orange',
fontSize: '20px'
}
}
render() {
return (
<div>
{/* 内联样式必须是驼峰标识 */}
<h2 style={{ color: 'red', fontSize: '14px' }}>你好 react</h2>
{/* 可以在状态里面来维护样式 */}
<p style={this.pstyle}>你是最棒的!</p>
</div>
)
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ojnABZrw-1649342396312)(img/内联样式.png)]
3.普通的css
很麻烦—会相互层叠
-
普通的css我们通常会编写到一个单独的文件,之后再进行引入。
-
这样的编写方式和普通的网页开发中编写方式是一致的:
- 如果我们按照普通的网页标准去编写,那么也不会有太大的问题;
- 但是组件化开发中我们总是希望组件是一个独立的模块,即便是样式也只是在自己内部生效,不会相互影响;
- 但是普通的css都属于全局的css,样式之间会相互影响;
-
这种编写方式最大的问题是样式之间会相互层叠掉;
4.css modules
1.给style文件命名必须是.module.css等,必须有module
2.引入之后,使用css必须在类名中使用,以{style.className}的方式来书写
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SHWQV3NY-1649342396313)(img/css modules.png)]
import React, { PureComponent } from 'react'
import homeStyle from './style.module.css'
export default class Home extends PureComponent {
render() {
return (
<div className={homeStyle.title}>Home</div>
)
}
}
5.css in js
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iti7uL6M-1649342396314)(img/css in js.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MvJcUUF2-1649342396315)(img/styled-components.png)]
5.1三个特点
import React, { PureComponent } from 'react'
import styled from 'styled-components'
/**
* 特点:
* 1.props穿透
* 2.attrs的使用
* 3.传入state作为props属性
*/
const IptStyle = styled.input.attrs({
placeholder: 'Nihaoya',
// 可以定义变量
bcolor: 'blue',
})`
background-color: pink;
border-color: ${props => props.color}
`
export default class About extends PureComponent {
constructor() {
super()
this.state = {
color: 'red'
}
}
render() {
return (
<div>
<IptStyle type="text" color={this.state.color} />
<input type="text"></input>
</div>
)
}
}
5.2可以实现继承
import React, { PureComponent } from 'react'
import Home from '../home/index'
import About from '../about/index'
import appStyle from 'styled-components'
const AppWrapper = appStyle.div`
font-size: 20px;
color: pink;
`
const SButton = appStyle.button`
padding: 10px 20px;
color: white;
`
// 可以实现继承,padding和color属性继承自SButton
const SPrimaryButton = appStyle(SButton)`
// padding: 10px 20px;
// color: red;
background-color: green;
`
export default class App extends PureComponent {
render() {
return (
<AppWrapper>
<h2>app</h2>
<p className='banner'>app</p>
<Home></Home>
<About></About>
<SButton>我是普通的按钮</SButton>
<SPrimaryButton>我是主要的按钮</SPrimaryButton>
</AppWrapper>
)
}
}
5.3可以定制主题,这样就可以把公共的样式放在theme里面
Sdiv和input有相同的黄色
import React, { PureComponent } from 'react'
import styled, { ThemeProvider } from 'styled-components'
/**
* 特点:
* 1.props穿透
* 2.attrs的使用
* 3.传入state作为props属性
*/
const IptStyle = styled.input.attrs({
placeholder: 'Nihaoya',
// 可以定义变量
bcolor: 'blue',
})`
background-color: pink;
color:${props => props.theme.themeColor};
border-color: ${props => props.theme.themeColor};
`
const Sdiv = styled.div`
color: ${props => props.theme.themeColor}
`
export default class About extends PureComponent {
constructor() {
super()
this.state = {
color: 'red'
}
}
render() {
return (
<ThemeProvider theme={{themeColor: 'yellow'}}>
<IptStyle type="text" color={this.state.color} />
<input type="text"></input>
<Sdiv>
<h2>about</h2>
</Sdiv>
</ThemeProvider>
)
}
}
6.在react中添加class
传入数组也可以
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KYlA75w5-1649342396315)(img/react中添加class.png)]
四、AntDesign组件库的使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LMQPYTIB-1649342396316)(img/介绍.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l1SkQb5u-1649342396317)(img/兼容性.png)]
2.AntD安装
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-maIinAT2-1649342396317)(img/AntDesign的安装.png)]
3.认识craco-- 用来修改webpack中的配置
1.安装craco的库
2.修改package.json里面的js脚本
3.在项目根目录下增加craco.config.js的文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S2aTte7e-1649342396318)(img/认识craco.png)]
3.1配置主题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t9m83bvc-1649342396319)(img/配置主题.png)]
3.2配置别名
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gbLK588D-1649342396319)(img/配置别名.png)]
4.书写案例
见代码,注意comment里面的actions是一个reactDOM数组
五、axios库的使用
1.前端网络请求的几种选择
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ehJ2E2dG-1649342396320)(img/网络请求几种方式.png)]
2.axios的基本使用
index.js文件里面
import App from './axios的使用/App'
// axios的基本配置
import axios from 'axios'
axios.defaults.baseURL = 'https://httpbin.org'
axios.defaults.timeout = 5000
// 设置全局默认的token
axios.defaults.headers.common['token'] = 'sasha'
// 只给post请求配置默认的token
axios.defaults.headers.post['token'] = 'sashapost'
请求拦截器
axios.interceptors.request.use(config => {
// 1.发送网络请求时, 在界面中间显示loading的组件
// 2.某一些请求需要用户携带token,如果没有携带,那么直接跳到登录页面
// params和data序列化的操作
config.headers.token = 'interceptor'
return config
}, err => {
return err
})
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-85jb0gOh-1649342396321)(img/axios的配置信息.png)]
3.axios的二次封装
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7roXeFnQ-1649342396322)(img/axios的二次封装.png)]
六、react过渡动画和纯函数的使用
1.react-transition-group介绍
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-awFARlIl-1649342396322)(img/react-transition-group.png)]
2.主要组件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uq9fzsza-1649342396323)(img/react-transition-group组件.png)]
2.1、CSSTransition
1.将需要过渡动画的组件或者片段用<CSSTransition></CSSTransition>
包裹起来
2.给该标签添加三个属性
- in:布尔值,用来控制元素隐藏和显示
- timeout:用来控制过渡时间,单位毫秒,一定主要是number类型的,所以需要用花括号包裹,而不是引号
- classNames:添加类名,定义相应的样式表
- appear:布尔值,让其初次渲染的时候也有过渡动画,需要在css中设置相关动画
- unmountOnExit:布尔值,让隐藏的时候dom元素消失
- 常见钩子函数,见下面的图
组件
import React, { PureComponent } from 'react'
import { CSSTransition } from 'react-transition-group'
import { Card, Avatar, Button } from 'antd';
import { EditOutlined, EllipsisOutlined, SettingOutlined } from '@ant-design/icons';
// 引入css样式
import './CSSTranistion.css'
const { Meta } = Card;
export default class CSSTransitionDemo extends PureComponent {
constructor() {
super()
this.state = {
isShow: true
}
}
render() {
return (
<div>
<Button type="primary" onClick={() => this.changeShow()}>显示/隐藏</Button>
{/* <div style={{display: this.state.isShow? 'block':'none'}}> */}
<CSSTransition
in={this.state.isShow}
classNames='card'
timeout={3000}
unmountOnExit={true}
appear={true}>
<Card
style={{ width: 300 }}
cover={
<img
alt="example"
src="https://gw.alipayobjects.com/zos/rmsportal/JiqGstEfoWAOHiTxclqi.png"
/>
}
actions={[
<SettingOutlined key="setting" />,
<EditOutlined key="edit" />,
<EllipsisOutlined key="ellipsis" />,
]}
>
<Meta
avatar={<Avatar src="https://joeschmoe.io/api/v1/random" />}
title="Card title"
description="This is the description"
/>
</Card>
</CSSTransition>
{/* </div> */}
</div>
)
}
changeShow() {
this.setState({
isShow: !this.state.isShow
})
}
}
对应的样式
.card-enter, .card-appear{
opacity: 0;
transform: scale(0.6);
}
.card-enter-active, .card-appear-active{
opacity: 1;
transform: scale(1);
transition: all 300ms;
}
/* .card-enter-done{
} */
.card-exit{
opacity: 1;
transform: scale(1);
}
.card-exit-active{
opacity: 0;
transform: scale(0.6);
transition: all 300ms;
}
.card-exit-done{
opacity: 0;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3EF3GWE8-1649342396324)(img/CSSTransition.png)]
2.2、SwitchTransition
内容切换的时候需要使用的动画
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GyQs0BCg-1649342396324)(img/switchTransition.png)]
组件
import { Button } from 'antd'
import React, { PureComponent } from 'react'
import { SwitchTransition, CSSTransition } from 'react-transition-group'
import './SwitchTransition.css'
export default class SwitchTransitionDemo extends PureComponent {
constructor() {
super()
this.state = {
isOn: true
}
}
render() {
const { isOn } = this.state
return (
<div>
<SwitchTransition mode="out-in">
<CSSTransition
key={isOn ? 'OFF' : 'ON'}
classNames='btn'
timeout={2000}>
{/* <button
style={{marginLeft: '200px'}}
onClick={() => this.setState({ isOn: !isOn })}>
{isOn ? 'OFF' : 'ON'}
</button> */}
<Button
type="primary"
style={{marginLeft: '200px', marginTop: '200px'}}
onClick={() => this.setState({ isOn: !isOn })}>
{isOn ? 'OFF' : 'ON'}
</Button>
</CSSTransition>
</SwitchTransition>
</div>
)
}
}
css
.btn-enter{
opacity: 0;
transform: translateX(-100%);
}
.btn-enter-active{
opacity: 1;
transform: translateX(0);
transition: opacity 2000ms, transform 2000ms;
}
.btn-exit{
opacity: 1;
transform: translateX(0);
}
.btn-exit-active{
opacity: 0;
transform: translateX(100%);
transition: opacity 2000ms, transform 2000ms;
}
2.3、TransitionGroup
当我们有一组动画时,需要将这些CSSTransition放入到一个TransitionGroup中来完成动画:
组件
import { Button } from 'antd'
import React, { PureComponent } from 'react'
import { CSSTransition, TransitionGroup } from 'react-transition-group'
import './TransitionGroup.css'
export default class TransitionGroupDemo extends PureComponent {
constructor(props) {
super(props)
this.state = {
names: ['coderwhy', 'kobe', 'lilei']
}
}
render() {
return (
<div style={{ textAlign: 'center' }}>
<TransitionGroup>
{
this.state.names.map((item, index) => {
return (
<CSSTransition
key={index}
timeout={1000}
classNames="name"
appear={true}>
<h2>{item}</h2>
</CSSTransition>
)
})
}
</TransitionGroup>
<Button type="primary" onClick={e => this.addName()}>添加新名字</Button>
</div>
)
}
addName() {
const newNames = [...this.state.names]
newNames.push('lisa')
this.setState({
names: newNames
})
}
}
css
.name-enter, .name-appear{
opacity: 0;
transform: scale(0.6);
}
.name-enter-active, .name-appear-active{
opacity: 1;
transform: scale(1);
transition: all 1000ms;
}
.name-enter-done{
color: pink;
}
.name-exit{
opacity: 1;
transform: scale(1);
}
.name-exit-active{
opacity: 0;
transform: scale(.6);
transition: all 1000ms;
}
.name-exit-done{
opacity: 0;
}
3.javascript纯函数
3.1含义
产生副作用的意思是修改了外部传过来的对象的值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WMUp7WyX-1649342396325)(img/纯函数.png)]
3.2纯函数分析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7v85mgTJ-1649342396326)(img/纯函数分析.png)]
七、redux的使用
1.为什么需要redux
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h8Dfcvo2-1649342396327)(img/为什么需要redux.png)]
2.核心理念
2.1.store&action
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RxoxB0qq-1649342396327)(img/redux核心理念.png)]
2.2.reducer(类似vue的mutations)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9EU0yx7J-1649342396328)(img/reducer.png)]
3.Redux的三大原则
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fNXX2j2u-1649342396329)(img/redux的三大原则.png)]
4.Redux测试项目搭建
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tvLSEgvg-1649342396329)(img/redux测试项目搭建.png)]
5.redux使用流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DZnAZ8tg-1649342396330)(img/Redux的使用流程.png)]
6.redux融入react
6.1自定义connect高阶函数–返回一个高阶组件
import { PureComponent } from 'react'
// import store from '../store/index'
import { StoreContext } from './context'
export function connect(mapStateToProps, mapDispatchToProp) {
return function enhanceHOC(WrappedComponent) {
class NewEnhanceComponent extends PureComponent {
constructor(props, context) {
super(props)
this.state = {
storeState: mapStateToProps(context.getState())
}
}
// 订阅监听
componentDidMount() {
this.unsubscribe = this.context.subscribe(() => {
this.setState({
storeState: mapStateToProps(this.context.getState())
})
})
}
// 取消订阅
componentWillUnmount() {
this.unsubscribe()
}
render() {
return <WrappedComponent
{...mapStateToProps(this.context.getState())}
{...mapDispatchToProp(this.context.dispatch)}
{...this.props} />
}
}
NewEnhanceComponent.contextType = StoreContext
return NewEnhanceComponent
}
}
// 返回一个高阶组件
在组件中使用connect函数–返回一个高阶组件,将组件的方法和状态都变成属性来使用
import React from 'react'
import { connect } from '../utills/connect.js'
import { subAction } from '../store/actionCreators'
function Home(props) {
return (
<div>
<h2>home</h2>
<h2>当前计数: {props.counter}</h2>
<button onClick={() => props.decrement()}>-1</button>
<button onClick={() => props.subNumber(5)}>-5</button>
</div>
)
}
const mapStateToProps = (state) => {
return {
counter: state.counter
}
};
const mapDispatchToProps = (dispatch) => {
return {
decrement: function() {
return dispatch(subAction(1))
},
subNumber: function(num) {
return dispatch(subAction(num))
}
}
};
export default connect(mapStateToProps, mapDispatchToProps)(Home)
6.2自定义context函数–目的是把connect中对store的依赖分离出来
import React from 'react'
export const StoreContext = React.createContext()
6.3context处理store
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nbumpRoX-1649342396330)(img/context处理store.png)]
import React from 'react';
import ReactDOM from 'react-dom';
import App from './04.redux的使用/App.js'
import store from './04.redux的使用/store/index'
import { StoreContext } from './04.redux的使用/utills/context'
// value值是必须的
ReactDOM.render(
// <React.StrictMode>
<StoreContext.Provider value={store}>
<App />
</StoreContext.Provider>,
// </React.StrictMode>,
document.getElementById('root')
);
八.组件中的异步操作—访问后台数据的时候,需要在组件生命周期函数中发起异步请求
import React, { PureComponent } from 'react'
// import { connect } from '../utills/connect.js'
import { connect } from 'react-redux'
import { subAction,changeBanners, changeRecommends } from '../store/actionCreators'
import axios from 'axios'
class Home extends PureComponent {
// 发起异步请求
componentDidMount() {
axios({
url: 'http://123.207.32.32:8000/home/multidata',
method: 'get'
}).then(res => {
const data = res.data.data
this.props.changeBanners(data.banner.list)
this.props.changeRecommends(data.recommend.list)
})
}
render() {
return (
<div>
<h2>home</h2>
<h2>当前计数: {this.props.counter}</h2>
<button onClick={() => this.props.decrement()}>-1</button>
<button onClick={() => this.props.subNumber(5)}>-5</button>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
counter: state.counter
}
};
const mapDispatchToProps = (dispatch) => {
return {
decrement: function() {
return dispatch(subAction(1))
},
subNumber: function(num) {
return dispatch(subAction(num))
},
changeBanners(banners) {
dispatch(changeBanners(banners))
},
changeRecommends(recommends) {
dispatch(changeRecommends(recommends))
}
}
};
export default connect(mapStateToProps, mapDispatchToProps)(Home)
用来访问后台数据,coderwhy老师的服务器http://123.207.32.32:8000/home/multidata
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zamgdynv-1649342396331)(img/redux中的异步操作.png)]
1.redux中的异步操作–redux-thunk的使用
需要使用中间件来操作,官方推荐redux-chunk
它的主要作用是将dispatch(action)中的action可以是一个函数来使用,因为我们之前的action默认是一个js对象
store/actionCreate.js
import { ADD_NUMBER, SUB_NUMBER, CHANGE_BANNER, CHANGE_RECOMMEND, FETCH_HOME_MULTIDATA } from "./constants.js"
import axios from 'axios'
export const addAction = function (num) {
return {
type: ADD_NUMBER,
num: num
}
}
export const subAction = function (num) {
return {
type: SUB_NUMBER,
num: num
}
}
// 轮播图和推荐的action
export const changeBanners = (banners) => {
return {
type: CHANGE_BANNER,
banners: banners
}
}
export const changeRecommends = (recommends) => {
return {
type: CHANGE_RECOMMEND,
recommends: recommends
}
}
// redux-thunk中的函数
export const getHomeMultidata = (dispatch) => {
// console.log('redux-thunk');
axios({
url: 'http://123.207.32.32:8000/home/multidata',
method: 'get'
}).then(res => {
const data = res.data.data
dispatch(changeBanners(data.banner.list))
dispatch(changeRecommends(data.recommend.list))
})
}
// redux-saga拦截的action
export const fetchHomeMultidataAction = {
type: FETCH_HOME_MULTIDATA
}
组件中的使用,其实还是在生命周期函数中调用
import React, { PureComponent } from 'react'
// import { connect } from '../utills/connect.js'
import { connect } from 'react-redux'
import { subAction,changeBanners, changeRecommends, getHomeMultidata } from '../store/actionCreators'
// import axios from 'axios'
class Home extends PureComponent {
componentDidMount() {
// axios({
// url: 'http://123.207.32.32:8000/home/multidata',
// method: 'get'
// }).then(res => {
// const data = res.data.data
// this.props.changeBanners(data.banner.list)
// this.props.changeRecommends(data.recommend.list)
// })
this.props.getHomeMultidata()
}
render() {
return (
<div>
<h2>home</h2>
<h2>当前计数: {this.props.counter}</h2>
<button onClick={() => this.props.decrement()}>-1</button>
<button onClick={() => this.props.subNumber(5)}>-5</button>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
counter: state.counter
}
};
const mapDispatchToProps = (dispatch) => {
return {
decrement: function() {
return dispatch(subAction(1))
},
subNumber: function(num) {
return dispatch(subAction(num))
},
changeBanners(banners) {
dispatch(changeBanners(banners))
},
changeRecommends(recommends) {
dispatch(changeRecommends(recommends))
},
getHomeMultidata() {
dispatch(getHomeMultidata)
}
}
};
export default connect(mapStateToProps, mapDispatchToProps)(Home)
2.redux-devtools
1.浏览器下载edux-devtools插件
2.项目store/index.js配置中间件
import * as redux from 'redux'
import reducer from './reducer.js'
// 1.引入saga库
import createSagaMiddleware from 'redux-saga'
// 2.创建saga中间件
const sagaMiddleware = createSagaMiddleware()
// 3.应用一些中间件
// redux.applyMiddleware(中间件1, 中间件2, 中间件3)
const storeenhancer = redux.applyMiddleware(sagaMiddleware)
// composeEnhancer函数,使用redux-devtools
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || redux.compose;
const store = redux.createStore(reducer, composeEnhancers(storeenhancer))
// 4.开启saga中间件
sagaMiddleware.run(saga)
export default store
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DR53u7aS-1649342396333)(img/redux-devtools.png)]
3.generator
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vUHqSRkN-1649342396333)(img/es6-generator.png)]
4.redux-saga的使用
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8JXT9wF5-1649342396334)(img/redux-saga的使用.png)]
4.1配置redux-saga库
store/index.js
import * as redux from 'redux'
import reducer from './reducer.js'
import saga from './saga'
// 1.引入saga库
import createSagaMiddleware from 'redux-saga'
// 2.创建saga中间件
const sagaMiddleware = createSagaMiddleware()
// 3.应用一些中间件
// redux.applyMiddleware(中间件1, 中间件2, 中间件3)
const storeenhancer = redux.applyMiddleware(sagaMiddleware)
// composeEnhancer函数,使用redux-devtools
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || redux.compose;
const store = redux.createStore(reducer, composeEnhancers(storeenhancer))
// 4.开启saga中间件
sagaMiddleware.run(saga)
export default store
4.2定义actionCreate对象
// redux-saga拦截的action
export const fetchHomeMultidataAction = {
type: FETCH_HOME_MULTIDATA
}
4.3定义saga函数
store/saga.js
import axios from 'axios'
import { FETCH_HOME_MULTIDATA } from './constants'
import { takeEvery, put, all } from 'redux-saga/effects'
import { changeBanners, changeRecommends } from './actionCreators'
function* fetchHomeMultidata(action) {
const res = yield axios.get('http://123.207.32.32:8000/home/multidata')
// // console.log(res);
// yield put(changeBanners(res.data.data.banner.list))
// yield put(changeRecommends(res.data.data.recommend.list))
// 以下也可以,和上面一样的作用
yield all([
yield put(changeBanners(res.data.data.banner.list)),
yield put(changeRecommends(res.data.data.recommend.list))
])
}
// 拦截每一个类型为FETCH_HOME_MULTIDATA的action, 并且执行fetchHomeMultidata这个生成器函数
function* mySaga() {
// takeEvery(执行每一个) takeLastest(只会执行最后一个)
yield takeEvery(FETCH_HOME_MULTIDATA, fetchHomeMultidata)
}
// export default 生成器函数
export default mySaga
5.中间件的实现方式—后面看,没有时间了
coderwhy-react-17节:01:20:30暂停
6.reducer代码拆分
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3r2Bszql-1649342396335)(img/reducer代码拆分.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UstLKIPI-1649342396335)(img/combineReducer函数.png)]
九、状态管理方案
方案
1.组件内部自己维护state
2.redux
3.cintext上下文共享
推荐
-
ui相关的组件内部可以维护的状态,在组件自己的内部使用
-
只要是需要共享的状态,都交给redux来处理’
-
服务器请求过来的数据,交给redux处理
后续会补充的东西–项目中补充
redux和immutableJS结合使用
一些性能更新
``
4.3定义saga函数
store/saga.js
import axios from 'axios'
import { FETCH_HOME_MULTIDATA } from './constants'
import { takeEvery, put, all } from 'redux-saga/effects'
import { changeBanners, changeRecommends } from './actionCreators'
function* fetchHomeMultidata(action) {
const res = yield axios.get('http://123.207.32.32:8000/home/multidata')
// // console.log(res);
// yield put(changeBanners(res.data.data.banner.list))
// yield put(changeRecommends(res.data.data.recommend.list))
// 以下也可以,和上面一样的作用
yield all([
yield put(changeBanners(res.data.data.banner.list)),
yield put(changeRecommends(res.data.data.recommend.list))
])
}
// 拦截每一个类型为FETCH_HOME_MULTIDATA的action, 并且执行fetchHomeMultidata这个生成器函数
function* mySaga() {
// takeEvery(执行每一个) takeLastest(只会执行最后一个)
yield takeEvery(FETCH_HOME_MULTIDATA, fetchHomeMultidata)
}
// export default 生成器函数
export default mySaga
5.中间件的实现方式—后面看,没有时间了
coderwhy-react-17节:01:20:30暂停
6.reducer代码拆分
[外链图片转存中…(img-3r2Bszql-1649342396335)]
[外链图片转存中…(img-UstLKIPI-1649342396335)]
九、状态管理方案
方案
1.组件内部自己维护state
2.redux
3.cintext上下文共享
推荐
-
ui相关的组件内部可以维护的状态,在组件自己的内部使用
-
只要是需要共享的状态,都交给redux来处理’
-
服务器请求过来的数据,交给redux处理
后续会补充的东西–项目中补充
redux和immutableJS结合使用
一些性能更新