这是我参与更文挑战的第3天,活动详情查看: 更文挑战
一、概念
1. 无状态组件
无状态组件(展示组件,函数式组件):就是一个函数没有props,没有生命周期, 就是一个简单的视图函数,没有业务逻辑更纯粹的展示UI。例子:
function NullStatus() {
return (
<div>无状态组件</div>
)
}
2. 有状态组件
有状态组件(容器组件,类组件) :标准使用模式此函数继承了React里面的组件和props,可以使用生命周期可以再里面写业务逻辑, 可以再里面做任何事情.例子:
class HocComponent extends React.Component {
constructor() {
super();
this.state = {
name: '张三'
}
}
render() {
return (
<div>有状态组件</div>
)
}
}
3. 在有状态组件里使用无状态组件
import React from 'react'
// import Css from './assets/css/app.css'
// 无状态组件
function NoStatusComponent(props) {
return (
<div>
{props.title}
<button type={"button"} onClick={props.sendParent.bind(this,'我是無狀態組件传过来的值')}>给父组件传值</button>
</div>
)
}
class App extends React.Component {
// 构造器
constructor() {
super();
this.state = {
};
}
// 处理子组件中的值
handleChildV(e) {
console.log(e)
}
// 渲染dom
render() {
return (
<div className="">
<NoStatusComponent title={'无状态组件'} sendParent={this.handleChildV.bind(this)}>
</NoStatusComponent>
</div>
)
}
}
export default App;
3. 高阶组件
- 高阶组件: 其实就是高阶函数, 我们第一一个函数, 里面返回一个有状态组件, 就是高阶组件
- 高阶组件就像我们吃火锅的锅底, 可以在里面加羊肉、牛肉、蔬菜等各种食物。锅底相当于业务逻辑,食物相当于UI展示,这样可以使我们的业务逻辑层和UI层分类,代买更清晰, 更适合多人开发和维护。
- 例子, 在html中函数中返回一个函数, 再调用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
function hoc() {
return function hocComponent(args) {
console.log(args)
}
}
hoc()('参数')
</script>
</body>
</html>
- 效果
3.1 高阶组件的分类
属性代理模式
属性代理最常见的高阶组件的使用方式。它通过做一些操作,将被包裹组件的props和新生产的props一起传递给辞组件,这称之未属性代理。
反向继承的方式
这种方式返回的React组件继承了被传入的组件,所以它能够访问到的区域,权限更更多,相比属性代理方式,他更像带入组织内部,对其进行修改。
3.2 在react中使用代理模式
新建一个hoc组件, 返回的是一个有状态组件,和一个title
import React from 'react'
export default function Hoc(WithComponent,title) {
return class HocComponent extends React.Component {
render() {
return (
<React.Fragment>
<div>{title}</div>
<WithComponent title={'我是代理高阶组件'} name={'张三'}></WithComponent>
</React.Fragment>
)
}
}
}
在App.js中调用
import React from 'react'
// import Css from './assets/css/app.css'
import Proxy from './component/hoc/proxy'
// 无状态组件
function NoStatusComponent(props) {
return (
<div>
{props.title}
<button type={"button"} onClick={props.sendParent.bind(this,'我是無狀態組件传过来的值')}>给父组件传值</button>
</div>
)
}
class App extends React.Component {
// 构造器
constructor() {
super();
this.state = {
};
}
// 处理子组件中的值
handleChildV(e) {
console.log(e)
}
// 渲染dom
render() {
return (
<div className="">
<NoStatusComponent title={'无状态组件'} sendParent={this.handleChildV.bind(this)}>
</NoStatusComponent>
{this.props.title}
<br/>
{this.props.name}
</div>
)
}
}
export default Proxy(App,'我是属性代理高阶组件的参数');
代理出来了, 通过proxy(App,title) title给代理组件传值, 通过 WithComponent给被代理的组件传值
3.3 在react中使用继承模式
class 组件不要继承react 继承传入的组件就可以了
export default function Hoc(WithComponent,title) {
return class HocComponent extends WithComponent {
render() {
return (
<React.Fragment>
<div>{title}</div>
<WithComponent title={'我是被继承高阶组件'} name={'张三'}></WithComponent>
</React.Fragment>
)
}
}
}
效果
区别
继承比proxy更强大, 可以获取被继承组件中的属性, 例如在继承组件中获取父组件的state
<div>{this.state.age}</div>
效果
4. 使用ES6的方式使用代理的高阶hoc组件
App.js中 使用箭头函数代表一个组件
import React from 'react'
import Hoc from './component/hoc/hoc'
let Login = Hoc(
(props) => {
console.log(props)
return (
<React.Fragment>
<h1>会员登录</h1>
<form>
<label>
用户名:
<input value={props.username} onChange={e => {
console.log(e)}}/>
</label>
<br/>
<div style={{marginTop:' 10px'}}></div>
<label>
密码:
<input value={props.password} onChange={e => {
console.log(e)
}}/>
</label>
</form>
</React.Fragment>
)
}
)
class App extends React.Component {
// 构造器
constructor() {
super();
this.state = {
};
}
// 渲染dom
render() {
return (
<div className="">
<Login username={'张三'} password={'123456'} />
</div>
)
}
}
export default App;
hoc.js中 使用 扩展运算符(...)接收所有参数
import React from 'react'
export default function Hoc(WithComponent,title) {
return class HocComponent extends React.Component {
render() {
console.log(this.props)
return (
<React.Fragment>
<div>{title}</div>
<WithComponent {...this.props}></WithComponent>
</React.Fragment>
)
}
}
}
效果一个简单的登录就被渲染出来了
ps: ES5和ES6接收参数的区别
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
function demo() {
console.log(arguments,'es5中的伪数组参数')
console.log(arguments[0],'es5中的伪数组参数的第一个元素')
console.log('----伪数组是无法遍历的----')
console.log(Array.prototype.slice.call(arguments),'es5中伪数组转数组')
console.log(Array.from(arguments),'es6中的伪数组转数组')
console.log('-----华丽的分割线----')
}
let demoEs6 = (...args) => {
console.log('----es6中没有arguments对象----')
console.log(args,'es6接收的就是一个真数组')
}
demo(1,2,3,4,5)
demoEs6(1,2,3,4,5)
</script>
</body>
</html>
效果
在hoc中使用插槽
<Login username={'张三'} password={'123456'} >
<li>会员一</li>
<li>会员二</li>
</Login>
用es6和hoc封装一个简单的表单校验组件吧
App.js
import React from 'react'
import Hoc from './component/hoc/hoc'
let Login = Hoc(
(props) => {
console.log(props)
return (
<React.Fragment>
<h1>会员登录</h1>
<form>
<label>
用户名:
<input {...props.username}/> <span style={{color:'deeppink'}}>{props.isUsernameValid ? '' : '请输入正确的用户名'}</span>
</label>
<br/>
<div style={{marginTop:' 10px'}}></div>
<label>
密码:
<input {...props.password} type={'password'} /> <span style={{color:'deeppink'}}>{props.isPasswordValid ? '' : '请输入正确的密码'}</span>
</label>
<br/>
<button type={'button'} onClick={props.submit.bind(this, () => {
alert(`提交成功!: 用户名: ${props.username.value} 密码: ${props.password.value}`)
})}>登录</button>
</form>
<hr/>
<div>插槽</div>
{props.children}
</React.Fragment>
)
}
)
let Login2 = Hoc(
(props) => {
console.log(props)
return (
<React.Fragment>
<h1>会员登录2</h1>
<form>
<label>
用户名:
<input {...props.username}/> <span style={{color:'green'}}>{props.isUsernameValid ? '' : '请输入正确的用户名'}</span>
</label>
<br/>
<div style={{marginTop:' 10px'}}></div>
<label>
密码:
<input {...props.password} type={'password'} /> <span style={{color:'green'}}>{props.isPasswordValid ? '' : '请输入正确的密码'}</span>
</label>
<br/>
<button type={'button'} onClick={props.submit.bind(this, () => {
alert(`提交成功!: 用户名: ${props.username.value} 密码: ${props.password.value}`)
})}>登录</button>
</form>
<hr/>
<div>插槽</div>
{props.children}
</React.Fragment>
)
}
)
class App extends React.Component {
// 构造器
constructor() {
super();
this.state = {
};
}
// 渲染dom
render() {
return (
<div className="">
<Login >
<li>会员一</li>
<li>会员二</li>
</Login>
<br/>
<Login2>
</Login2>
</div>
)
}
}
export default App;
hoc.js
import React from 'react'
export default function Hoc(WithComponent,title) {
return class HocComponent extends React.Component {
constructor() {
super();
this.state = {
username: '',
password: '',
isUsernameValid: true,
isPasswordValid: true,
}
}
// 改变用户名
changeUsername(e) {
this.setState({
username: e.target.value
})
}
// 改变密码
changePassword(e) {
this.setState({
password: e.target.value
})
}
// 校验数据
validData() {
let flag = true
// 数据校验
// 用户名不能为空且大于6位
if(this.state.username.match(/^\s*$/) || this.state.username.length < 6) {
flag = false
this.setState({isUsernameValid: false})
// return flag
} else {
this.setState({isUsernameValid: true})
}
// 密码不能为空且大于6位
if(this.state.password.match(/^\s*$/) || this.state.password.length < 6) {
flag = false
this.setState({isPasswordValid: false})
// return flag
} else {
this.setState({isPasswordValid: true})
}
return flag
}
// 登录
submitData(callback) {
let pass = this.validData()
if(!pass) return
// 执行成功回调
if(typeof callback === 'function') {
callback()
}
}
render() {
console.log(this.props)
let newProps = {
username: {
value: this.state.username,
onChange: this.changeUsername.bind(this),
onBlur: this.validData.bind(this)
},
password: {
value: this.state.password,
onChange: this.changePassword.bind(this),
onBlur: this.validData.bind(this)
},
}
return (
<React.Fragment>
<div>{title}</div>
<WithComponent {...this.props} {...this.state} {...newProps} submit={this.submitData.bind(this)}></WithComponent>
</React.Fragment>
)
}
}
}