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一起传递给租价
传递方式
posted @   终究还是避免不了遗憾  阅读(49)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示