1.组件的定义方式
- 函数组件Functional Component
- 类组件Class Component
2.类组件
export class Profile extends Component {
render() {
console.log(this.context);
return (
<div>Profile</div>
)
}
}
- 组件的名称是大写字符开头(无论类组件还是函数组件)
- 类组件需要继承自 React.Component
- 类组件必须实现render函数
- constructor是可选的,我们通常在constructor中初始化一些数据
render函数的返回值
当 render 被调用时,它会检查 this.props 和 this.state 的变化并返回以下类型之一:
- React 元素:通常通过 JSX 创建
- 数组或 fragments:使得 render 方法可以返回多个元素
- Portals:可以渲染子节点到不同的 DOM 子树中
- 字符串或数值类型:它们在 DOM 中会被渲染为文本节点
- 布尔类型或 null:什么都不渲染
3.函数组件
函数组件是使用function来进行定义的函数,只是这个函数会返回和类组件中render函数返回一样的内容。
函数组件有自己的特点:
- 没有生命周期,也会被更新并挂载,但是没有生命周期函数
- this关键字不能指向组件实例(因为没有组件实例)
- 没有内部状态(state)
4.生命周期
React内部为了告诉我们当前处于哪些阶段,会对我们组件内部实现的某些函数进行回调,这些函数就是生命周期函数:
- 实现componentDidMount函数:组件已经挂载到DOM上时,就会回调
- 实现componentDidUpdate函数:组件已经发生了更新时,就会回调
- 实现componentWillUnmount函数:组件即将被移除时,就会回调
不常用的生命周期
- getDerivedStateFromProps:state 的值在任何时候都依赖于 props时使用;该方法返回一个对象来更新state
- getSnapshotBeforeUpdate:在React更新DOM之前回调的一个函数,可以获取DOM更新前的一些信息(比如说滚动位置)
- shouldComponentUpdate:可进行state、props、context进行新旧对比,返回true或false进行是否更新组件
5.组件之间的通信
- 父组件通过 属性=值 的形式来传递给子组件数据
- 子组件通过 props 参数获取父组件传递过来的数据
父组件:
import React from "react"
// import HelloWorld from "./Components/HelloWorld"
import TabControl from './Components/父子组件通信案例'
// import NavBar from './Components/实现插槽一'
// import NavBar from './Components/实现插槽二'
// import TabControl from './Components/作用域插槽'
// import AppHome from "./Components/非父子组件通信-context/AppHome";
class App extends React.Component {
constructor() {
super()
console.log("hello react");
this.state = {
message: "hello react",
isShow: true,
titles: ['流行', '新款', '精选'],
curIndex: 0
}
}
changeText() {
this.setState({
message: 'hello hgf'
})
}
switchText() {
this.setState({
isShow: !this.state.isShow
})
}
switchTab(curIndex){
this.setState({curIndex})
}
render() {
const { titles, curIndex } = this.state
return (
<div>
<TabControl titles={titles} changeTab={(curIndex) => this.switchTab(curIndex)} />
{/* <h2>{titles[curIndex]}</h2> */}
{/* <h2>{message}</h2> */}
{/* <button onClick={e => this.switchText()}>切换</button> */}
{/* {isShow && <HelloWorld/>} */}
{/* <button onClick={e => this.changeText()}>修改文本</button> */}
{/* 插槽实现1 */}
{/* <NavBar>
<button>左边</button>
<h2>中间部分</h2>
<button>右边</button>
</NavBar> */}
{/* 插槽实现二 */}
{/* <NavBar leftSlot={<button>左按钮</button>} centerSlot={<h2>插槽实现二</h2>} rightSlot={<button>右按钮</button>} /> */}
{/* 作用域插槽 */}
{/* <TabControl titles={titles} itemTypes={(item) => <button>{item}</button>} changeTab={(curIndex) => this.switchTab(curIndex)} />
<h2>{titles[curIndex]}</h2> */}
{/* 非父子组件通信 */}
{/* <AppHome /> */}
</div>
)
}
componentDidMount() {
console.log("component Mount")
}
componentDidUpdate() {
console.log("component Update");
}
}
export default App
子组件:
import React, { Component } from 'react'
import "./tab-control.css"
export class index extends Component {
constructor() {
super()
this.state = {
curIndex: 0
}
}
changeCurIndex(index) {
this.setState({curIndex: index})
this.props.changeTab(index)
}
render() {
const {titles} = this.props
const {curIndex} = this.state
return (
<div>
<div className='tab-control'>
{
titles.map((item, index) => {
return (
<div className={`item ${index === curIndex ? 'active':''}`} key={item} onClick={e => this.changeCurIndex(index)}>
<span className='text'>{item}</span>
</div>
)
})
}
</div>
</div>
)
}
}
export default index
6.React中实现插槽
React对于这种需要插槽的情况非常灵活,有两种方案可以实现:
- 组件的children子元素
- props属性传递React元素
方式一:
import React, { Component } from 'react'
import "./style.css"
export class index extends Component {
render() {
const {children} = this.props
return (
<div className='nav-bar'>
<div className='left'>
{children[0]}
</div>
<div className='center'>
{children[1]}
</div>
<div className='right'>
{children[2]}
</div>
</div>
)
}
}
// index.propTypes = {
// children: PropTypes.element
// }
export default index
方式二:
import React, { Component } from 'react'
import "./style.css"
export class index extends Component {
render() {
const {leftSlot, centerSlot, rightSlot} = this.props
return (
<div className='nav-bar'>
<div className='left'>
{leftSlot}
</div>
<div className='center'>
{centerSlot}
</div>
<div className='right'>
{rightSlot}
</div>
</div>
)
}
}
// index.propTypes = {
// children: PropTypes.element
// }
export default index
7.context应用场景
非父子组件数据的共享:
对于有一些场景:比如一些数据需要在多个组件中进行共享(地区偏好、UI主题、用户登录状态、用户信息等)
- React提供了一个API:Context
- Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props
- Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言
Context相关API:
React.createContext
-
创建一个需要共享的Context对象
- 如果一个组件订阅了Context,那么这个组件会从离自身最近的那个匹配的 Provider 中读取到当前的context值
- defaultValue是组件在顶层查找过程中没有找到对应的Provider,那么就使用默认值
Context.Provider
- 每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化
- Provider 接收一个 value 属性,传递给消费组件
- 一个 Provider 可以和多个消费组件有对应关系
- 多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据
- 当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染
创建context
import React from "react";
const ThemeContext = React.createContext({nickname: 'curry', work: '前端'})
export default ThemeContext
import React from "react";
const UserContext = React.createContext()
export default UserContext
提供值
import React, { Component } from 'react'
import ThemeContext from './context/theme-context'
import UserContext from './context/user-context'
import Home from './context/Home'
import Profile from './context/Profile'
export class AppHome extends Component {
render() {
return (
<div>
App
<UserContext.Provider value={{name: 'hgf', age: 23}}>
<ThemeContext.Provider value={{color: 'red', size: '30px'}}>
<Home />
</ThemeContext.Provider>
</UserContext.Provider>
<Profile />
</div>
)
}
}
export default AppHome
获取和使用方式
import React, { Component } from 'react'
import ThemeContext from './theme-context'
export class Profile extends Component {
render() {
console.log(this.context);
return (
<div>Profile</div>
)
}
}
Profile.contextType = ThemeContext
export default Profile
import React, { Component } from 'react'
import ThemeContext from './theme-context';
import UserContext from './user-context';
export class HomeInfo extends Component {
render() {
console.log(this.context);
return (
<div>
HomeInfo
<UserContext.Consumer>
{
value => {
return <h2>InfoUser: {value.age}</h2>
}
}
</UserContext.Consumer>
</div>
)
}
}
HomeInfo.contextType = ThemeContext
export default HomeInfo