react学习笔记一:基础知识

react开发辅助工具

chrome扩展包

1、React Devtools:检视React组件树形结构:https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi

2、Redux Devtools:检视Redux数据流:https://chrome.google. com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibtkpmmfibljd

3、React Perf:发现React组件渲染的性能问题:https://google.com/webstore/detail/react-perf/hacmcodfllhbnekmghgdlplbdnahrnhmm

如果访问谷歌浏览商店哟困难,可以访问http://chrome-extension-downloader.com/,在输入框输入上面连接最后面的字符串,点击下载扩展包按钮即可

1、组件生命周期:

组件生命周期分为三个阶段:

创建过程(挂载)
更新过程
卸载过程

了解:每个阶段执行时机;每个阶段钩子函数执行顺序

创建过程

执行时机:页面加载时(组件创建时)

钩子函数执行顺序也按照书写顺序
constructor:触发时机-创建组件时最先执行;用于初始化state、为事件处理程序绑定this
getInitialState
getDefaultProps
componentWillMount,这里不用,就算在这里修改数据,但是渲染的时候也不会更新,初始化数据最好在constructor;
render:触发时机---每次组件渲染都会触发;用于渲染UI(不能调用 setState(),因为state改变之后,会触发render)
componentDidMount:触发时机---组件挂载后(完成DOM渲染);用于发送网络请求、DOM操作

1)constructor并不是类中必须定义的
需要用到state的时候才定义,用于初始化state
绑定成员函数的this环境
用到props的时候,不定义constructor,初始化super(props),能继承父的props吗????

1)getInitialState\\getDefaultProps在createClass中才调用

2)componentWillMount紧跟render前面执行,但是componentDidMount在整个页面的组件render完之后,在按照render顺序执行相应的componentDidMount

3)componentWillMount、componentDidMount区别
componentWillMount可以在浏览器和服务端调用;componentDidMountz只能在浏览器调用,因为装载是创建组件并放在DOM上,而服务端并不会产生DOM树,所以只能在浏览器上调用

4)render是React.component必须实现的方法
render不做世纪渲染动作,返回JSX描述结构,React操作渲染过程
render函数返回null  false,则这个组件不需要渲染任何元素
render函数应该是一个纯函数

更新过程
执行时机:setState()调用    forceUpdate()   组件接收到新的props

执行顺序按照下面的顺序:

componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
render:触发时机-----每次组件渲染都会触发;渲染UI
componentDidUpdate:触发时机-----组件更新后(完成DOM渲染);用于发送网络请求、DOM操作,如果要setState,要放在一个if里面,否则会导致递归更新

注意:并不是所有更新都会执行上面的方法
1)componentWillReceiveProps(nextProps)
触发时机:只要父组件的render调用,不管父组件传给子组件的props是否变化,都会触发组件的componentWillReceiveProps
通过setState触发的更新,不会调发该方法

卸载

执行时机:组件从页面消失

钩子函数:componentWillUnmount;

触发时机:组件卸载;

作用:执行清理工作(清定时器等)


2、数据:props (外部传给组件,不允许修改)  state(内部数据,动态修改状态)

1、props

1)可以通过propTypes设置和检查props的类型
className.propTypes = {
  caption: PropTypes.string.isRequired,
   initValue: PropTypes.number
}
这个类型检查只是在控制台报错,不会影响交互,所以,应该只在开发环境使用,生产就应该去掉,babel-react-optimize就可以,保证在生产上才使用

2)className.defaultProps可以给props设置默认值

当调用组件的时候,没有定义给属性,则会使用默认值

3、state
通过className.defaultProps可以给props设置默认值,在用props初始化state的时候就不需要写默认值了

关于react中数据通讯的三种方式

父组件传递数据给子组件、子组件传递数据给父组件、相邻组件传递数据、多层嵌套的的组件间传递数据

1)、父传给子组件数据

1)通过props传递数据给子组件,子组件引用上定义props,如name,值为父组件中在state上定义的变量为name

2)子函数组件通过props拿数据props.name,类组件通过this.props.name拿数据

2)、子组件传递数据给父组件

1)在父组件中定义方法handleData

2)在子组件引用上添加props,如onClick,值为handleData

3)在子组件中定义触发事件,调用函数组件调用props.handleData(),类组件调用this.props.handleData()

3)、相邻组件传递数据

1)通过在父组件的state中定义共享变量a

2)父组件中定义改变共享变量值的方法changeA()

3)将变量a和方法changeA作为props给子组件

从而达到相邻组件传递数据的目的

4)、深度嵌套组件传递数据

如A=》B=》C=》D,现在需要将数据从A传到D,除了props形式,还可以通过context形式传递

1) 组件Provider提高数据,组件Consumer 消费数据

const { Provider, Consumer } = React.createContext()

2) 使用Provider组件作为父节点,设置value属性表示要传递的数据

<Provider value="pinker">
....
</Provider>

3) 在要接收数据的子组件里面调用Consumer组件接收数据

<Consumer>
  {data => <span>接收到的数据为:{data}</span>}
</Consumer>

4、组件复用

复用:复用state,操作state的方法(组件状态逻辑)

组件复用两种方式:

1)组件为入参,返回为组件----高阶组件HOC

2)render props模式

render props模式

组件创建时,组件内部的的render方法使用this.props.render,即

render() {
  return this.props.render(this.state)
}

使用组件时,添加一个值为函数的props,通过函数参数来获取组件内部数据,根据组件在实际编写ui 

<Mouse render={(mouse) => (
   <p>数据为:{mouse.x}、{mouse.y}</p>
)}>

注意,可以使用props.children代替props.render属性,即在使用组件的时候可以不再标签里面定义属性render

组件创建时候,将render函数改成下面形式

render() {
  return this.props.children(this.state)
}

组件使用时:

<Mouse>
  {
    (mouse => {
       return (
         <P>数据为:{mouse.x}、{mouse.y}</P>
       )
    })
  }
</Mouse>

高阶组件

HOC是一个函数,参数为组件,返回新的组件

displayName属性、传递props避免props丢失

给原组件添加新的props属性

class Mouse extends React.component{
  render () {
       return <WrappedComponent {...this.state} />
  }
}

使用步骤:

1)创建以with开头的函数

2)指定函数参数,参数以大写字母开头

3)在函数内部创建类组件,提供复用的状态逻辑代码

4)在该组件内,渲染参数组件,同时将状态通过props传递给参数组件

5)调用高阶组件,传入要增强的组件,通过返回值拿到增强的组件,并渲染到页面

// 创建高阶组件
function withMouse(WrappedComponent) {
  // 该组件提供服用的逻辑
  class Mouse extends ReadableByteStreamController.component {
    // 鼠标状态
    state = {
      x: 0,
      y: 0
    }
    handleMouseMove = (e) => {
      this.setState({
        x: e.clientX,
        y: e.clientY
      })
    }
    // 控制鼠标状态逻辑
    componentDidMount() {
      window.addEventListener('mousemove', this.handleMouseMove)
    }
    // 解绑事件
    componentWillUnmount() {
      window.removeEventListener('mousemove', this.handleMouseMove)
    }
    // 渲染
    render() {
      return (
        // 通过props,将数据传递给传入组件
        <WrappedComponent {...this.state}></WrappedComponent>
      )
    }
  }
  return Mouse
}

// 测试高阶组件
const Position = (props) => (
  <p>鼠标当前位置:(x: {props.x}, y: {props.y})</p>
)

const Cat = (props) => (
  <img style={
   
   {
    position: 'absolute',
    top: props.y - 64,
    left: props.x - 64
  }} alt="cat" />
)

// 获取增强后的组件
const MousePosition = withMouse(Position)
// 创建高阶组件
function withMouse(WrappedComponent) {
  // 该组件提供服用的逻辑
  class Mouse extends ReadableByteStreamController.component {
    // 鼠标状态
    state = {
      x: 0,
      y: 0
    }
    handleMouseMove = (e) => {
      this.setState({
        x: e.clientX,
        y: e.clientY
      })
    }
    // 控制鼠标状态逻辑
    componentDidMount() {
      window.addEventListener('mousemove', this.handleMouseMove)
    }
    // 解绑事件
    componentWillUnmount() {
      window.removeEventListener('mousemove', this.handleMouseMove)
    }
    // 渲染
    render() {
      return (
        // 通过props,将数据传递给传入组件
        <WrappedComponent {...this.state}></WrappedComponent>
      )
    }
  }
  return Mouse
}

// 测试高阶组件
const Position = (props) => (
  <p>鼠标当前位置:(x: {props.x}, y: {props.y})</p>
)

const Cat = (props) => (
  <img style={
   
   {
    position: 'absolute',
    top: props.y - 64,
    left: props.x - 64
  }} alt="cat" />
)

// 获取增强后的组件
const MousePosition = withMouse(Position)
const CatPosition = withMouse(Cat)

// 在页面中渲染MousePosition\CatPosition组件

问题:以上两个组件渲染出来的名称都是<Mouse>

原因:默认情况下,React使用组件名称作为displayName

解决方法:为高阶组件设置displayname,便于调试区分不同组件

设置方式:

// 设置displatname
Mouse.displayName = `With${getDisplayName(WrappedComponent)}`

function getDisplayName(WrappedComponent) {
  // 有设置属性displayName,WrappedComponent.name表示创建的新高阶组件名称
  return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}
// 创建高阶组件
function withMouse(WrappedComponent) {
  // 该组件提供服用的逻辑
  class Mouse extends ReadableByteStreamController.component {
    // 鼠标状态
    state = {
      x: 0,
      y: 0
    }
    handleMouseMove = (e) => {
      this.setState({
        x: e.clientX,
        y: e.clientY
      })
    }
    // 控制鼠标状态逻辑
    componentDidMount() {
      window.addEventListener('mousemove', this.handleMouseMove)
    }
    // 解绑事件
    componentWillUnmount() {
      window.removeEventListener('mousemove', this.handleMouseMove)
    }
    // 渲染
    render() {
      return (
        // 通过props,将数据传递给传入组件
        <WrappedComponent {...this.state}></WrappedComponent>
      )
    }
  }
  // 设置displatname
  Mouse.displayName = `With${getDisplayName(WrappedComponent)}`
  return Mouse
}
function getDisplayName(WrappedComponent) {
  // 有设置属性displayName,WrappedComponent.name表示创建的新高阶组件名称
  return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}

// 测试高阶组件
const Position = (props) => (
  <p>鼠标当前位置:(x: {props.x}, y: {props.y})</p>
)

const Cat = (props) => (
  <img style={
   
   {
    position: 'absolute',
    top: props.y - 64,
    left: props.x - 64
  }} alt="cat" />
)

// 获取增强后的组件
const MousePosition = withMouse(Position)
const CatPosition = withMouse(Cat)

// 在页面中渲染MousePosition组件

问题:传递props,避免props丢失

<MousePosition a={1} />

// 在Mouse中的render,state和props一起传递
<WrappedComponent {...this.state} {...this.props}></WrappedComponent>

猜你喜欢

转载自blog.csdn.net/tangxiujiang/article/details/113814680