React-组件进阶
组件进阶
组件通讯
多个组件之间需要共享某些数据,为了实现这个功能,打破组件的独立封闭性,让其与外界沟通,这个过程就是组件通讯
组件的props
作用:接收传递给组件的数据
传递数据:给组件标签添加属性
接收数据:函数组件通过参数props接收数据,类组件通过this.props接收数据
//props使用 //函数组件接收数据 const Hello = (props) => { //props是一个对象 console.log(props) return ( <div> <h1>props:{props.name}</h1> </div> ) } ReactDOM.render(<Hello name="jack" age={20} />, document.getElementByI
//类组件接收数据 class Hello extends React.Component { render() { console.log(this.props); return ( <div> <h1>props:{this.props.name}</h1> </div> ) } } ReactDOM.render(<Hello name="jack" age={20} />, document.getElementById('root'));
特点
可以传递任意类型的数据
props是只读对象,只能读取属性的值,无法修改对象
使用类组件是,如果写了构造函数,应该将props传递给super(),否则无法在构造函数中获取到props
class Hello extends React.Component { render() { console.log(this.props); this.props.fn() return ( <div> <h1>props:{this.props.name}</h1> {this.props.tag} </div> ) } } ReactDOM.render(<Hello name="jack" age={20} colors={['red', 'blue']} fn={() => { console.log(123); }} tag={<p>123</p>} />, document.getElementById('root'));
组件通讯的三种方式
1.父组件传递数据给子组件
//父组件 class Parent extends React.Component { state = { lastName: '王' } render() { return ( <div className="parent"> 父组件: <Child name={this.state.lastName} /> </div> ) } } //子组件 const Child = (props) => { return ( <div className="child"> <p>子组件,接收到父组件的数据{props.name}</p> </div> ) } ReactDOM.render(<Parent />, document.getElementById('root'))
2.子组件传递数据给父组件
//父组件 class Parent extends React.Component { state = { parentMsg: '' } //回掉函数 用来接收数据 getChildMsg = data => { console.log('接收到子组件传输的数据', data); this.setState({ parentMsg: data }) } render() { return ( <div className="parent"> 父组件:{this.state.parentMsg} <Child getMsg={this.getChildMsg} /> </div> ) } } //子组件 class Child extends React.Component { state = { Msg: 'dddddddddddd' } handleClick = () => { //子组件调用父组件的回掉函数 this.props.getMsg(this.state.Msg) } render() { return ( <div className="child"> 子组件:<button onClick={this.handleClick}>点击此处,传递数据给父组件</button> </div> ) } } ReactDOM.render(<Parent />, document.getElementById('root'))
3.兄弟组件
//父组件 class Counter extends React.Component { //共享状态 state = { count: 0 } //提供修改状态的方法 onIncrement = () => { this.setState({ count: this.state.count + 1 }) } render() { return ( <div> <Child1 count={this.state.count} /> <Child2 onIncrement={this.onIncrement} /> </div> ) } } const Child1 = (props) => { return ( <h1>计数器:{props.count}</h1> ) } const Child2 = (props) => { return <button onClick={() => props.onIncrement()}>+1</button> } ReactDOM.render(<Counter />, document.getElementById('root'))
Context
作用:跨组件传递数据(比如:主题、语言)
调用React.createContext()创建Provider(提供数据)和Consumer(消费数据)两个组件。
//创建context组件 const { Provider, Consumer } = React.createContext() class App extends React.Component { render() { return ( <Provider value="pink"> <div className="app"> <Node /> </div> </Provider> ) } } const Node = (props) => { return ( <div className="node"> <SubNode /> </div> ) } const SubNode = (props) => { return ( <div className="subname"> <Child /> </div> ) } const Child = (props) => { return ( <div className="child"> <Consumer>{ (data) => <span>我是子节点---{data}</span> }</Consumer> </div> ) } ReactDOM.render(<App />, document.getElementById('root'))
props深入
1.children属性
组件标签的子节点,当组件标签有子节点时,props就会有该属性
children属性与普通的props一样,值可以为任意值(文本、React元素,组件,函数等)
const App = props => { console.log(props); return ( <div> <h1>组件标签的子节点:</h1> {props.children} </div> ) } ReactDOM.render(<App>我是子节点</App>, document.getElementById('root'))
const Test = () => <button>我是button组件</button> const App = props => { console.log(props); return ( <div> <h1>组件标签的子节点:</h1> {props.children} </div> ) } ReactDOM.render(<App> {/* <p>我是p标签</p> */} <Test /> </App>, document.getElementById('root'))
const Test = () => { return (<button>我是button组件</button>) } const App = props => { console.log(props); props.children() return ( <div> <h1>组件标签的子节点:</h1> {/* {props.children} */} </div> ) } ReactDOM.render(<App> {() => console.log('这是一个函数子节点')} </App>, document.getElementById('root'))
2.props校验
安装prop-types包:npm i prop-types
const App = props => { const arr = props.colors const lis = arr.map((item, index) => <li key={index}>{item}</li>) return <ul>{lis}</ul> } //添加props校验 App.propTypes = { colors: PropTypes.array } ReactDOM.render(<App colors={['red', 'blue']} />, document.getElementById('root'))
//添加props校验 //属性a的类型:数值(number) //属性fn的类型:函数(func)并且为必填项 //属性tag的类型:React元素(element) //属性filter的类型:对象({area:'上海',price:1999}) App.propTypes = { colors: PropTypes.array, a: PropTypes.number, fn: PropTypes.func.isRequired, tag: PropTypes.element, filter: PropTypes.shape({ area: PropTypes.string, price: PropTypes.number }) } ReactDOM.render(<App fn={() => { }} />, document.getElementById('root'))
props的默认值
场景:分页组件-》每页显示的条数
const App = props => { console.log(props) return ( <div> <h1>此处展示props的默认值:{props.pagesize}</h1> </div> ) } //添加props默认值 App.defaultProps = { //当不传入pagesize是,则显示默认的10 pagesize: 10 } ReactDOM.render(<App pagesize={20} />, document.getElementById('root'))
组件的生命周期
组件从被创建到挂载到页面中运行,再到组件不用时写在的过程
生命周期的每个阶段总伴随着一些方法调用,这些方法就是生命周期的钩子函数
钩子函数的作用,为开发人员在不同阶段操作组件提供了时机。
只有类组件才有生命周期
生命周期的三个阶段
1.创建时(挂载阶段)

class App extends React.Component { constructor(props) { super(props) //初始化state this.state = { count: 0 } //处理this指向问题 console.warn('生命周期钩子函数:constructor'); //获取不到,因为没有渲染结束,结果为null // const title = document.getElementById('title'); // console.log(title); } //进行dom操作 //能够发送ajax请求,获取远程的数据 componentDidMount() { console.warn('生命周期钩子函数:componentDidMount'); //渲染结束之后,可以获取title // const title = document.getElementById('title'); // console.log(title); } render() { // //错误提示 不要在render中调用setState // this.setState({ // count: 1 // }) console.warn('生命周期钩子函数:render'); return ( <div> <h1 id="title">统计打豆豆的次数</h1> <button id="btn">打豆豆</button> </div> ) } } ReactDOM.render(<App />, document.getElementById('root'))
2.更新时
执行时机:1.setState() 2forceUpdate 3.组件接收到新的props
class App extends React.Component { constructor(props) { super(props) //初始化state this.state = { count: 0 } } handleClick = () => { // this.setState({ // count: this.state.count + 1 // }) this.forceUpdate() } componentDidUpdate() { console.warn('---子组件生命周期钩子函数 componentDidUpdate'); //获取dom const title = document.getElementById('title') console.log(title); } render() { console.warn('生命周期钩子函数 render'); return ( <div> <Counter count={this.state.count} /> <button onClick={this.handleClick}>打豆豆</button> </div> ) } } class Counter extends React.Component { render() { console.warn('---子组件生命周期钩子函数 render'); return <h1 id="title">统计豆豆被打的次数{this.props.count}</h1> } } ReactDOM.render(<App />, document.getElementById('root'))
3.卸载时
class App extends React.Component { constructor(props) { super(props) //初始化state this.state = { count: 0 } } handleClick = () => { this.setState({ count: this.state.count + 1 }) } render() { return ( <div> {this.state.count > 3 ? (<p>豆豆被打死了</p>) : (<Counter count={this.state.count} />) } <button onClick={this.handleClick}>打豆豆</button> </div> ) } } class Counter extends React.Component { componentDidMount() { //开启定时器 this.timeid = setInterval(() => { console.log('定时器正在执行'); }, 500) } render() { return <h1>统计豆豆被打的次数{this.props.count}</h1> } componentWillUnmount() { console.warn('生命周期钩子函数 componentWillUnmount'); //清理定时器 clearInterval(this.timeid) } } ReactDOM.render(<App />, document.getElementById('root'))
render-props和高阶组件
React组件复用概述
思考:如果两个组件中的部分功能相似或相同,该如何处理
处理方式:复用相似的功能
复用什么:1.state 2.操作state的方法
两种方式:1.render props模式 2.高阶组件(HOC)
注意:这两种方式不是新的API,而是利用React自身特点的编码技巧,演化成的固定模式。
Render props模式
问题1:如何难道该组件中复用的state?
在使用组件时,添加一个值为函数的prop,通过函数参数来获取(需要组件内部实现)
问题2:如何渲染任意的UI
使用该函数的返回值作为要渲染的UI内容(需要组件你饿不实现)
/render props模式 //mouse组件创建 class Mouse extends React.Component { propsTypes = { } state = { x: 0, y: 0 } handleMouseMove = e => { this.setState({ x: e.clientX, y: e.clientY }) } //监听鼠标移动事件 componentDidMount() { window.addEventListener('mousemove', this.handleMouseMove) } //在组件卸载时移除事件绑定 componentWillUnmount() { window.addEventListener('mousemove', this.handleMouseMove) } render() { return this.props.children(this.state) } } //添加props校验 Mouse.propTypes = { children: PropTypes.func.isRequired } class App extends React.Component { render() { return ( <div> <h1>render props 模式</h1> {/* <Mouse render={(mouser) => { return <p>鼠标当前位置{mouser.x},{mouser.y}</p> }} /> */} <Mouse> { mouser => { return (<p>鼠标当前位置{mouser.x},{mouser.y}</p>) } } </Mouse> {/* <Mouse render={mouser => { return <img id="img" src={img} alt="girl" style={{ position: 'absolute', top: mouser.y, left: mouser.x } } /> }}></Mouse> */} <Mouse> { mouser => ( <img id="img" src={img} alt="girl" style={{ position: 'absolute', top: mouser.y, left: mouser.x } } /> )} </Mouse> </div > ) } } ReactDOM.render(<App />, document.getElementById('root'))
高阶组件
目的:实现状态逻辑复用
采用包装模式,比如说手机壳
思路分析:
- 高阶组件(HOC)是一个函数,接收要包装的组件,返回增强后的组件
- 高阶组件内部创建一个类组件,在这个类组件中提供复用的状态逻辑,通过prop将复用的状态传递给被包装组件
//高阶组件 //创建高阶组件 function withMouse(WrappedComponent) { //该组件提供复用的状态逻辑 class Mouse extends React.Component { state = { x: 0, y: 0 } hanldemouseMove = e => { this.setState({ x: e.clientX, y: e.clientY }) } componentDidMount() { window.addEventListener('mousemove', this.hanldemouseMove) } componentWillUnmount() { window.removeEventListener('mousemove', this.hanldemouseMove) } render() { return <WrappedComponent {...this.state} /> } } return Mouse } const Position = props => ( <p> 鼠标当前位置:(x:{props.x},y:{props.y}) </p> ) //加入图片 const Girl = props => { return ( <img id="img" src={img} alt="girl" style={{ position: 'absolute', top: props.y, left: props.x } } />) } //获取增强后的组件 const Mouseposition = withMouse(Position) //调用图片迁移的高阶组件 const MouseGirl = withMouse(Girl) class App extends React.Component { render() { return ( <div> <h1>高阶组件</h1> <Mouseposition /> <MouseGirl /> </div> ) } } ReactDOM.render(<App />, document.getElementById('root'))
设置displayname
使用高阶组件存在的问题:得到两个组件名称相同
原因:默认情况下,React使用组件名称作为displayName
解决方式:为高阶组件设置displayName便于调试时区分不同的组件
display Name的作用:用于设置调试信息
//设置display Mouse.displayName = 'WithMouse' + getDisplayName(WrappedComponent) function getDisplayName(WrappedComponent) { return WrappedComponent.displayName || WrappedComponent.name || 'Component' }
传递props
问题:props丢失
原因:高阶组件没有往下传递props
解决方式:渲染时,将state和this.props一起传递给租价
传递方式
· 分享4款.NET开源、免费、实用的商城系统
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了