# 一、组件
# 1、组件分类
通常情况下,组件可以分为:
- 函数式组件、类组件
- 无状态组件、有状态组件
- 展示型组件、容器型组件
# 2、类组件
关于类组件的高级部分,可以关注 类组件的 render() 返回值类型:
import React from 'react'
class Demo extends React.Component {
//
render() {
// 返回 jsx
return <h1>Hello, world!</h1>
// 返回数组 或 fragments
return (
<>
<h1>今日新闻</h1>
<p>lencamo正在学习React</p>
</>
)
return (
<React.Fragment>
<h1>今日新闻</h1>
<p>lencamo正在学习React</p>
</React.Fragment>
)
return [<h1>今日新闻</h1>, <p>lencamo正在学习React</p>]
// 返回文本节点(字符串、数值)
return 'Hello, world!'
return 123
// 当然,返回undefined、null、Boolean类型会显示为空值
}
}
export default Demo
# 3、函数式组件
16.8 后伴随着 hooks 的出现,函数式组件的使用率逐渐提高(类似于 vue 中的 components API)
简单使用:
function Demo(props) {
// 其支持的返回类型和类组件一样
return <h1>Hello, world!</h1>
}
export default Demo
缺点:
- 没有生命周期
- 没有组件实例(this 关键字指向问题)
- 没有内部状态
# 4、受控组件
在大多数情况下,我们推荐使用 受控组件 来处理表单数;简单的说,就是让相关数据交给 this.state 控制。
当我们对表单组件加上 value 属性,并且其值为 this.state 中的某个值(可以设置默认值),此时该组件为受控组件(由 react 控制)
多表单元素数据监控
- 批量处理多个输入
class Demo extends React.PureComponent {
handleChange(event) {
const value = event.target.type === 'checkbox' ? event.target.checked : event.target.value
this.setState({
// 批量监控
[event.target.name]: value
})
}
//
render() {
return (
<div>
<input
type="text"
name="useranme"
value={username}
onChange={(e) => this.handleChange(e)}
/>
<input
type="password"
name="password"
value={password}
onChange={(e) => this.handleChange(e)}
/>
<select value={fruit} onChange={(e) => this.handleChange(e)}>
<option value="apple">苹果</option>
<option value="orange">橘子</option>
<option value="banana">香蕉</option>
</select>
<input
type="checkbox"
name="checkbox"
checked={isSelect}
onChange={(e) => this.handleChange(e)}
/>
</div>
)
}
}
- 关于 CheckBox 多选
直接弄一个对象数组数据,然后根据该数据进行 map 创建 jsx
class Demo extends React.PureComponent {
constructor() {
super()
this.state = {
username: ''
}
}
inputChange(event) {
// console.log(event.target.value)
this.setState({
username: event.target.value
})
}
//
render() {
const { username } = this.state
return (
<div>
<input type="text" value={username} onChange={(e) => this.inputChange(e)} />
用户名:{username}
</div>
)
}
}
拓展:非受控组件
class Demo extends React.PureComponent {
constructor() {
super()
this.state = {
username: ''
}
this.usernameRef = React.createRef()
}
componentDidMount() {
// this.usernameRef.current.addEventListener
}
//
render() {
const { username } = this.state
return (
<div>
<input type="text" defaultValue={username} ref={this.usernameRef} />
用户名:{this.}
</div>
)
}
}
# 5、高阶组件 ✨
高阶组件本身不是一个组件,而是一个函数。它接收一个组件作为它的参数,并返回一个新的组件。
高阶组件的英文 Higher-Order Components,简称 HOC。本质上就是对原组件进行一次拦截处理。
function hoc(Cpn) {
class NewCpn extends React.PureComponent {
//
render() {
return <Cpn name="lencamo" />
}
}
// 可以使用displayName修改组件名
NewCpn.displayName = 'myComponent'
return NewCpn
}
export default hoc
高阶组件的应用场景非常广泛,场景的有(第三方库 ):
- redux 中的 connect
- react-router 中的 withRouter
提示
向我们前面提到的在函数式组件中可以使用的 函数memo
、函数forwardRef
等,本质上也是一个高阶组件
应用 1:对原组件注入 props(props 增强)
- 编写高阶组件
function enhancedUserInfo(OriginComponent) {
class NewComponent extends React.PureComponent {
constructor() {
super()
this.state = {
userInfo: {
name: 'lencamo',
level: 20
}
}
}
render() {
// 加上 {...this.props} 是因为高阶组件的调用者 Demo 的调用者App等可能会向Demo传递props
return <OriginComponent {...this.props} {...this.state.userInfo} />
}
}
return NewComponent
}
export default enhancedUserInfo
- 使用高阶组件
import from '@/hoc/enhanced_props.js'
class Demo extends React.PureComponent {
//
printProps() {
console.log(this.props.name, this.props.level)
}
render() {
return (
<button onClick={() => this.printProps()} >查看props</button>
)
}
}
// 对当前组件增强
export default enhancedUserInfo(Demo)
- Demo 组件的调用者
class App extends React.Component {
//
render() {
return <Demo />
}
}
export default App
应用 2:xxx.Consumer 封装(Context 共享)
- 编写高阶组件
import ThemeContext from '@/context/theme-context.js'
// function withTheme(OriginComponent) {
// return (props) => {
// return (
// <ThemeContext.Consumer>
// {(value) => {
// return <OriginComponent {...value} {...props} />
// }}
// </ThemeContext.Consumer>
// )
// }
// }
function withTheme(OriginComponent) {
class NewComponent extends React.PureComponent {
//
render() {
return (
<ThemeContext.Consumer>
{(value) => {
// 将value作为 props 传递出去
return <OriginComponent {...value} {...this.props} />
}}
</ThemeContext.Consumer>
)
}
}
return NewComponent
}
export default withTheme
- 使用高阶组件
import from '@/hoc/with_theme.js'
class Demo extends React.PureComponent {
//
render() {
// 使用 Provider 传来的value数据
return <p>this.props.xxx</p>
}
}
// 对当前组件增强
export default withTheme(Demo)
应用 3:登录鉴权
function loginAuth(OriginComponent) {
// 从 应用2 中我们可以看到 在没有状态的情况下,使用函数会更加简洁
return (props) => {
const token = localStorage.getItem('token')
if (token) {
return <OriginComponent {...props} />
} else {
return <h2>请先登录</h2>
}
}
}
export default loginAuth
# 二、其他
下面,简单介绍一些 react 中内置的一些高阶组件:
# 1、StrictMode 组件
StrictMode 和 Fragment 一样,不会渲染任何可见的 UI。要注意的是严格模式检查仅在开发模式下运行;它们不会影响生产构建。
# 2、Fragment 组件
和 vue3 中的 Fragment 作用一样,没什么好说的。
# 3、Portals 组件
其实 Portals 的作用类似于 vue3 中的 内置组件 Teleport
ReactDOM.createPortal(child, container)
Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案
import React from 'react'
import { createPortal } from 'react-dom'
class App extends React.PureComponent {
//
render() {
return (
<h1>Hello, world!</h1>
{
createPortal(<h2>放在其他地方</h2>, document.querySelector("#root2"))
}
)
}
}
export default App
应用:居中弹窗
- 编写弹窗
import { createPortal } from 'react-dom'
class Modal extends React.PureComponent {
render() {
// #modal提前编写了居中样式
return createPortal(this.props.children, document.querySelector('#modal'))
}
}
export default Modal
- 使用弹窗
class Demo extends React.PureComponent {
render() {
return (
<div>
{
<Modal>
<h2>我是标题</h2>
<p>我是内容,呵呵哈哈哈</p>
</Modal>
}
</div>
)
}
}