React 使用指南

本文记录React框架入门知识

1、安装及启动

npm i -g create-react-app             //全局安装react的cli
create-react-app hello-react             //hello-react是项目名
cd hello-react & npm start                //启动项目

初始完后,src/index.js内容如下:

import React from 'react'      //必须引入
import ReactDOM from 'react-dom'              //做浏览器应用时必须引入
import './index.css'                                        //引入样式文件
class Header extends Component {
  render () {                                                   // 必须有render函数,返回的是JSX形式模板
    return (
      <div>
        <h1>Hello React</h1>
      </div>
    )
  }
}

ReactDOM.render(                                     //ReactDOM帮忙渲染到指定节点
  <Header />,
  document.getElementById('root')
)

所以基本有三个必要步骤:
1、引库;
2、写组件的render函数;
3、ReactDOM渲染到页面。

2、基本语法

  • JSX实际是把html转成JS对象保存,再借助ReactDOM构建真正DOM渲染到页面。原因有两点:1、方便渲染到其他平台,如RN,就是利用这个JS对象,只是不用ReactDOM来渲染而已;2、组件变动时方便做Diff,提升效率


    页面渲染过程
  • render函数必须返回jsx,并且必须被一个最外层jsx容器包裹,不能平行返回多个(和vue的template一样),return一般还用小括号包裹
...
render () {         //ES6函数简写形式
  return (
    <div>
      <div>第一个</div>
      <div>第二个</div>
    </div>
  )
}
...
  • jsx中可以用'{}'来插值,内部可以放任何 JavaScript 的代码,包括变量、表达式计算、函数执行等等
render () {
  const word = 'is good'
  return (
    <div>
      <h1>Hello World {word}</h1>      // vue中是双大括号'{{}}'
    </div>
  )
}
  • 表达式插入不仅仅可以用在标签内部,也可以用在标签的属性上
render () {
  const className = 'header'
  return (
    <div className={className}>
      <h1>Hello World</h1>
    </div>
  )
}
  • JSX元素本身可转为JS对象,因此可将JSX元素看成变量
render () {
  const isGoodWord = true
  const goodWord = <strong> is good</strong>    // 引号都不能加,加了就当字符串了,最重要的是这里可以嵌套插值!!!!
  const badWord = <span> is not good</span>
  return (
    <div>
      <h1>
        Hello World
        {isGoodWord ? goodWord : badWord}
      </h1>
    </div>
  )
}
  • 自定义的组件都必须要用大写字母开头,普通的 HTML 标签都用小写字母开头
  • 事件监听
    1、给需要监听事件的元素加上类似于 onClick、onKeyDown 这样的属性即可,本质上也是用了插值运算符,此外,在render里掉方法时都要用this来调。
class Title extends Component {
  handleClickOnTitle () {
    console.log('Click on title.')
  }
  render () {
    return (
      <h1 onClick={this.handleClickOnTitle}>Hello World</h1>
    )
  }
}

2、事件属性名都必须要用驼峰命名法,且只能用在普通的 HTML 的标签上,不能用在组件标签上,如:<Header onClick={this.handleClick} />是无效的。
3、event对象会被传入处理函数,该对象经过React自己的封装,与原生event对象差异不大
4、一般在某个类的实例方法里面的 this 指的是这个实例本身,但react不是,也就是在实例方法里拿不到当前实例,所以需要借助bind来实现。这个模式在react中非常常见。
还可以利用bind来传参数,注意此时事件处理器参数顺序:先接受参数,最后是event

class Title extends Component {
  handleClickOnTitle (param,e) {
    console.log(this)
  }
  render () {
    return (
      <h1 onClick={this.handleClickOnTitle.bind(this,param)}>Hello World</h1>
    )
  }
}
  • state & setState
    一个组件的显示形态是可以由它数据状态和配置参数决定的
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import './index.css'
class LikeButton extends Component {
  constructor () {
    super()                                           //构造函数中调父元素初始化方法
    this.state = {                                  // 添加自身状态属性
          name: 'Tomy',
          isLiked: false
     }         
  }

  handleClickOnLikeButton () {
    this.setState({                     //只能通过setState来改变状态,不能直接this.isLiked=××
      isLiked: !this.state.isLiked   //只设置需要更新的属性,其他属性保持不变,如name
    })
  }
  render () {
    return (
      <button onClick={this.handleClickOnLikeButton.bind(this)}>
        {this.state.isLiked ? '取消' : '点赞'} 👍
      </button>
    )
  }
}

setState接受函数参数和对象参数。当是对象参数时,多个setState不是实时更新,而是react帮你存在队列里一起更新(为了效率);当是函数参数时,setState操作是实时更新的。

  handleClickOnLikeButton () {
    this.setState((prevState) => {
      return { count: 0 }
    })
    this.setState((prevState) => {
      return { count: prevState.count + 1 } // 上一个 setState 的返回是 count 为 0,当前返回 1
    })
    this.setState((prevState) => {
      return { count: prevState.count + 2 } // 上一个 setState 的返回是 count 为 1,当前返回 3
    })
    // 最后的结果是 this.state.count 为 3
  }

当进行多次 setState,实际上组件只会重新渲染一次,而不是多次;这是因为在 React.js 内部会把 JavaScript 事件循环中的消息队列的同一个消息中的 setState 都进行合并以后再重新渲染组件

  • props & defaultProps
class LikeButton extends Component {
  static defaultProps = {
    likedText: '取消',
    unlikedText: '点赞'
  }

  constructor () {
    super()
    this.state = { isLiked: false }
  }

  handleClickOnLikeButton () {
    this.setState({
      isLiked: !this.state.isLiked
    })
  }

  render () {
    return (
      <button onClick={this.handleClickOnLikeButton.bind(this)}>
        {this.state.isLiked
          ? this.props.likedText
          : this.props.unlikedText} 👍
      </button>
    )
  }
}

1、为了使得组件的可定制性更强,在使用组件的时候,可以在标签上加属性来传入配置参数。
2、组件可以在内部通过 this.props 获取到配置参数,组件可以根据 props 的不同来确定自己的显示形态,达到可配置的效果。
3、可以通过给组件添加类属性 defaultProps 来配置默认参数。
4、props 一旦传入,你就不可以在组件内部对它进行修改。但是你可以通过父组件主动重新渲染的方式来传入新的 props,从而达到更新的效果。

  • state Vs props
    state 在组件内部初始化,可以被组件自身修改,而外部不能访问也不能修改。你可以认为 state 是一个局部的、只能被组件自身控制的数据源。state 中状态可以通过 this.setState 方法进行更新,setState 会导致组件的重新渲染。
    props 的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参数,组件内部无法控制也无法修改。除非外部组件主动传入新的 props,否则组件的 props 永远保持不变。
    state 是让组件控制自己的状态,props 是让外部对组件自己进行配置。

无状态组件(函数式组件):只能接收props为参数,只提供render的返回值

const HelloWorld = props=> {
  const sayHi = event => alert('Hello World')
  return (
    <div onClick={sayHi}>Hello World</div>
  )
}
  • 渲染列表常用做法(map)
class Index extends Component {
  render () {
    return (
      <div>
        {users.map((user,index) => {            //map不改变原数组
          return (
            <div key={index}>
              <div>姓名:{user.username}</div>
              <div>年龄:{user.age}</div>
              <div>性别:{user.gender}</div>
              <hr />
            </div>
          )
        })}
      </div>
    )
  }
}
  • ref
    ref 属性可以帮助我们获取已经挂载的元素的 DOM 节点
class AutoFocusInput extends Component {
  componentDidMount () {
    this.input.focus()
  }

  render () {
    return (
      <input ref={(input) => this.input = input} />
    )
  }
}

ReactDOM.render(
  <AutoFocusInput />,
  document.getElementById('root')
)

给input 元素加了一个 ref 属性,这个属性值是一个函数。当 input 元素在页面上挂载完成以后,React.js 就会调用这个函数,并且把这个挂载以后的 DOM 节点传给这个函数。在函数中我们把这个 DOM 元素设置为组件实例的一个属性,这样以后我们就可以通过 this.input 获取到这个 DOM 元素。
注意:ref对应值是函数,函数以dom为参数;一般只用于普通html标签,不用于自定义组件上。

  • props.children
    使用自定义组件的时候,可以在其中嵌套 JSX 结构。嵌套的结构在组件内部都可以通过 props.children 获取到,这种组件编写方式在编写容器类型的组件当中非常有用。
ReactDOM.render(
  <Card>
    <h2>React.js 小书</h2>
    <div>开源、免费、专业、简单</div>
    订阅:<input />
  </Card>,
  document.getElementById('root')
)
// Card组件
class Card extends Component {
  render () {
    return (
      <div className='card'>
        <div className='card-content'>
          {this.props.children}
        </div>
      </div>
    )
  }
}
  • dangerouslySetHTML 和 style 属性
    dangerouslySetHTML设置的值不会被转义
  render () {
    return (
      <div
        className='editor-wrapper'       //这里用className,而不是class
        dangerouslySetInnerHTML={{__html: this.state.content}} /> //这个content不会转义
    )
  }

style的特殊性:1、接受一个对象,这个对象里面是这个元素的 CSS 属性键值对;2、原来 CSS 属性中带 - 的元素都必须要去掉 - 换成驼峰命名,如 font-size 换成 fontSize

<h1 style={{fontSize: '12px', color: this.state.color}}>React.js 小书</h1>
  • 组件参数验证PropTypes
// npm install --save prop-types
import React, { Component } from 'react'
import PropTypes from 'prop-types'

class Comment extends Component {
  static propTypes = {
    comment: PropTypes.object.isRequired
  }

  render () {
    const { comment } = this.props
    return (
      <div className='comment'>
        <div className='comment-user'>
          <span>{comment.username} </span>:
        </div>
        <p>{comment.content}</p>
      </div>
    )
  }
}
//
PropTypes.array
PropTypes.bool
PropTypes.func
PropTypes.number
PropTypes.object
PropTypes.string
PropTypes.node
PropTypes.element
  • 高阶组件
    高阶组件就是一个函数,传给它一个组件,它返回一个新的组件。高阶组件的作用其实就是为了组件之间的代码复用。组件可能有着某些相同的逻辑,把这些逻辑抽离出来,放到高阶组件中进行复用。高阶组件内部的包装组件和被包装组件之间通过 props 传递数据
  • context 对象
    一个组件可以通过 getChildContext 方法返回一个对象,这个对象就是子树的 context,提供 context 的组件必须提供 childContextTypes 作为 context 的声明和验证。
    如果一个组件设置了 context,那么它的子组件都可以直接访问到里面的内容,它就像这个组件为根的子树的全局变量。任意深度的子组件都可以通过 contextTypes 来声明你想要的 context 里面的哪些状态,然后可以通过 this.context 访问到那些状态。
// 以Index里包含Header组件为例
class Index extends Component {
  static childContextTypes = {        // 1、声明state中属性类型,必须写
    themeColor: PropTypes.string
  }

  constructor () {
    super()
    this.state = { themeColor: 'red' }  //2、初始化state
  }

  getChildContext () {
    return { themeColor: this.state.themeColor }        //3、用state值生成后代可以共用的context
  }

  render () {
    return (
      <div>
        <Header />
      </div>
    )
  }
}

// 子组件是这样获取context的
class Header extends Component {
  static contextTypes = {        //只要声明contextTypes就可以通过this.context使用全局的context了
    themeColor: PropTypes.string
  }

  render () {
    return (
      <h1 style={{ color: this.context.themeColor }}>React.js 小书标题</h1>
    )
  }
}
  • 生命周期


    生命周期

    1、组件初始化 componentWillMount -> render -> componentDidMount
    2、组件更新(props change)componentWillReceiveProps -> shouldComponentUpdate -> componentWillUpdate -> componentDidUpdate
    3、组件更新 (state change) shoudlComponentUpdate -> componentWillUpdate -> componentDidUpdate
    4、组件卸载或销毁 componentWillUnmount

3、一些注意点

  • 事件处理
    1、传参注意点
class Popper extends React.Component{
    constructor(){
        super();
        this.state = {name:'Hello world!'};
    }
    
    preventPop(name, e){    //事件对象e要放在最后
        e.preventDefault();
        alert(name);
    }
    
    render(){
        return (
            <div>
                <p>hello</p>
                {/* Pass params via bind() method. */}
                <a href="https://reactjs.org" onClick={this.preventPop.bind(this,this.state.name)}>Click</a>
            </div>
        );
    }
}

2、怎样避免函数被调用太快或者太多次

// 节流
import throttle from 'lodash.throttle';

class LoadMoreButton extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
    this.handleClickThrottled = throttle(this.handleClick, 1000);
  }

  componentWillUnmount() {
    this.handleClickThrottled.cancel();
  }

  render() {
    return <button onClick={this.handleClickThrottled}>Load More</button>;
  }

  handleClick() {
    this.props.loadMore();
  }
}

https://doc.react-china.org/docs/handling-events.html
https://doc.react-china.org/docs/faq-functions.html
https://doc.react-china.org/docs/events.html

  • ref
    1、创建ref
// 1、React v16.3 引入的 React.createRef() 
// 1.1、用在内置组件上
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  focusTextInput() {
    // 注意:通过 "current" 取得 DOM 节点
    this.myRef .current.XX;
  }
  render() {
    return <div ref={this.myRef} />;
  }
}
// 1.2、用在自定义组件上

// 2、低于v16.3的可以使用回调refs
class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);
    this.textInput = null;
    this.setTextInputRef = element => {
      this.textInput = element;
    };
    this.focusTextInput = () => {
      // 直接使用原生 API 使 text 输入框获得焦点
      if (this.textInput) this.textInput.focus();
    };
  }
  componentDidMount() {
    // 渲染后文本框自动获得焦点
    this.focusTextInput();
  }
  render() {
    // 使用 `ref` 的回调将 text 输入框的 DOM 节点存储到 React
    // 实例上(比如 this.textInput)
    return (
      <div>
        <input
          type="text"
          ref={this.setTextInputRef}
        />
        <input
          type="button"
          value="Focus the text input"
          onClick={this.focusTextInput}
        />
      </div>
    );
  }
}

2、你不能在函数式组件上使用 ref 属性,因为它们没有实例
https://doc.react-china.org/docs/refs-and-the-dom.html
https://doc.react-china.org/docs/forwarding-refs.html

  • 高阶组件
    高阶组件(HOC)是react中对组件逻辑进行重用的高级技术。高阶组件就是一个函数,一个没有副作用的纯函数,且该函数接受一个组件作为参数,并返回一个新的组件。
function logProps(WrappedComponent) {
  return class extends React.Component {
    componentWillReceiveProps(nextProps) {
      console.log('Current props: ', this.props);
      console.log('Next props: ', nextProps);
    }
    render() {
      // 用容器组件组合包裹组件且不修改包裹组件,这才是正确的打开方式。
      return <WrappedComponent {...this.props} />;
    }
  }
}

1、别将不相关的props属性传递给包裹组件
高阶组件应该传递与它要实现的功能点无关的props属性

render() {
  // 1、过滤掉与高阶函数功能相关的props属性
  const { extraProp, ...passThroughProps } = this.props;

  // 2、向包裹组件注入props属性,一般都是高阶组件的state状态或实例方法
  const injectedProp = someStateOrInstanceMethod;

  // 3、向包裹组件传递props属性(extraProp已被过滤)
  return (
    <WrappedComponent
      injectedProp={injectedProp}
      {...passThroughProps}
    />
  );
}

2、必须将静态方法做拷贝

// 定义静态方法
WrappedComponent.staticMethod = function() {/*...*/}
// 使用高阶组件
const EnhancedComponent = enhance(WrappedComponent);
// 增强型组件没有静态方法
typeof EnhancedComponent.staticMethod === 'undefined' // true

可以使用hoist-non-react-statics来帮你自动处理,它会自动拷贝所有非React的静态方法:

import hoistNonReactStatic from 'hoist-non-react-statics';
function enhance(WrappedComponent) {
  class Enhance extends React.Component {/*...*/}
  hoistNonReactStatic(Enhance, WrappedComponent);
  return Enhance;
}

3、Refs属性不能传递
一般来说,高阶组件可以传递所有的props属性给包裹的组件,但是不能传递refs引用。因为并不是像key一样,refs是一个伪属性,React对它进行了特殊处理。如果你向一个由高阶组件创建的组件的元素添加ref应用,那么ref指向的是最外层容器组件实例的,而不是包裹组件
https://doc.react-china.org/docs/higher-order-components.html

  • 受控与非受控组件
    1、在HTML当中,像<input>,<textarea>, 和 <select>这类表单元素会维持自身状态,并根据用户输入进行更新(浏览器默认行为,脱离了React的state控制)。但在React中,可变的状态通常保存在组件的state中,并且只能用 setState() 方法进行更新。React为了保证数据来源的单一性,提出了“受控组件”的技术
    2、所谓“受控组件”技术不过是对input等标签的使用方式不同而已,如input的value和onChange均由React自己控制
class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }
  handleChange(event) {
    this.setState({value: event.target.value});
  }
  handleSubmit(event) {
    alert('A name was submitted: ' + this.state.value);
    event.preventDefault();
  }
  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

// 同样的还有 textarea 和 select
<textarea value={this.state.value} onChange={this.handleChange} />
// select 受控用法
<select value={this.state.value} onChange={this.handleChange}>
      <option value="grapefruit">Grapefruit</option>
      <option value="lime">Lime</option>
      <option value="coconut">Coconut</option>
      <option value="mango">Mango</option>
</select>
// select 非受控用法
<select>
  <option value="grapefruit">Grapefruit</option>
  <option value="lime">Lime</option>
  <option selected value="coconut">Coconut</option>
  <option value="mango">Mango</option>
</select>

3、当你有处理多个受控的input元素时,你可以通过给每个元素添加一个name属性,来让处理函数根据 event.target.name的值来选择做什么

  • 非受控组件,多半会用到ref属性,(非受控组件将真实数据保存在 DOM 中)
class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.input.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" ref={(input) => this.input = input} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

4、在 React 的生命周期中,表单元素上的 value 属性将会覆盖 DOM 中的值。使用非受控组件时,通常你希望 React 可以为其指定初始值,但不再控制后续更新。要解决这个问题,你可以指定一个 defaultValue 属性而不是 value。同样,<input type="checkbox"> 和 <input type="radio"> 支持 defaultChecked,<select> 和 <textarea> 支持 defaultValue.

https://doc.react-china.org/docs/forms.html
https://doc.react-china.org/docs/uncontrolled-components.html

  • 与DOM异同
    1、在React中,所有的DOM特性和属性(包括事件处理函数)都是小驼峰命名法命名。aria-(无障碍访问相关)和data-属性是例外的,一律使用小写字母命名。
    2、在React中,使用className属性指定一个CSS类;而不是class
    3、dangerouslySetInnerHTML替代DOM中的innerHTML
    4、htmlFor替代DOM中的for (label标签)
    5、style属性接受一个键为小驼峰命名法命名的javascript对象作为值,而不是像css字符串.要注意,样式属性不会自动补齐前缀的。为了支持旧的浏览器,你需要手动支持相关的样式特性

4、写法技巧

  • render函数return的内容不要太复杂,而应该在return之前处理成简单的模板
//  把逻辑处理都提出来,最后return的内容很简单
render() {
        const renderSubmenu = submenuList => {
            const submenu = submenuList.map((sub, index) => (<Submenu.Option key={index} hash={sub.hash} content={sub.content} />))
            return submenu
        }
        const menu = this.props.menuList.map((first, index) => (
            <Submenu key={index} title={first.title} hash={first.hash} iconUrl={first.iconUrl}>
                {first.submenu && renderSubmenu(first.submenu)}
            </Submenu>))
        return (
            <div style={{ height: '100%', width: 'auto', float: 'left' }} >
                <div className={style.menu} style={{ height: '100%' }}>
                    {menu}
                </div>
            </div>
        )
    }
  • React类里的arrow函数、getter及属性校验及默认defaultProps的写法
import PropTypes from 'prop-types'
class Submenu extends React.Component {
   static defaultProps = {
     index:1
   }
    static propTypes = {
       index:PropTypes.object,
    }
    constructor(props) {
        super(props)
        this.state = {
            value:'',
        }
    }
   // this.isMainRoot 是一个getter
    get isMainRoot() {
        return this.state.index === 0 ? true : false
    }
  // 箭头函数写法
    toggle = _ => {
        this.setState({ index: 1 })
    }
    render() {
       
    }
}
  • classname的用法
import classNames from 'classnames';
import styles from './dialog.css';

export default class Dialog extends React.Component {
  render() {
    const cx = classNames({
      [styles.confirm]: !this.state.disabled,
      [styles.disabledConfirm]: this.state.disabled
    });

    return <div className={styles.root}>
      <a className={cx}>Confirm</a>
      ...
    </div>
  }
}
  • 不会改变的状态,不要放在state和render里,如果有公用的且不改变的变量(多个方法里要用到),直接在constructor里的this上就可以,不要放this.state;此外,对于依赖state的变量,也不要写在state里,可以写成get 函数,类似于vue中的computed
  • 不要hard code,即不要写死任何可能发生变化的变量,比如后端服务接口域名,用环境变量来动态区分会好很多
  • 多用解构,一次解构只解一层,不要解多层;对于多层才能解出来的,可以多做几次一层解构
  • 方法中不要修改传入的参数,可以用{...options} 重新构造一个
  • 写函数处理逻辑时,先写边界情况,最后写主要情况
  • 第三方组件的一些配置项直接写在JSX里,不必单独写在外面某个地方
                <ReactSwipe
                    containerClass={style.main}
                    slidesPerView="auto"
                    initialSlide={1}
                    key={this.state.randomId}
                    on={
                        {
                            slideChange: function() { self.onSlideChange(this) } // eslint-disable-line
                        }
                    }
                >
                    {/* 这里Course必须放在div里,这是这个插件的用法 */}
                    <div><Course type="0" nickname={nickname} /></div>
                    {courseList}
                </ReactSwipe>

5、一些高级话题

  • 高阶函数与装饰器

  • React.cloneElement
    React.cloneElement(
    element,
    [props],
    element.props.children,
    otherElement
    )

  • setState 同步异步问题
    setState异步、同步与进阶

6、一些使用经验记录

  • antd 按需加载
    如果不按需加载,则整个 antd.css 853KB 都会被加载,如下图:



    按需加载只会加载需要的样式:


按需加载配置如下:

      test: /\.(js|jsx)$/,
      loader: require.resolve('babel-loader'),
      options:{
        plugins:[
          [require.resolve('babel-plugin-import'),{libraryName:'antd',style:'css'}]
        ]
      },
      include: [resolve('src'), resolve('test')],
      exclude: [resolve('node_modules')]

此外对 css 的配置需包含 node_modules 目录

    {
      test: /\.css$/,
      use: ['style-loader', 'css-loader','sass-loader'],
      include: [resolve('src'),resolve('node_modules')]
    }
  • 一些 报错
    1、受控组件


    image.png

    原因:input 的 value 初始值为 undefined,给个默认值就好了
    2、Can't call setState (or forceUpdate) on an unmounted component
    1、未清除定时器
    2、A页面跳转到B页面后,A页面还有setState操作,如

register({ mobile, password, code: verifyCode })
      .then(() => {
        history.push('/updateUserInfo');
      })
      .catch(err => {
       
      })
      .finally(() => {
          this.setState({
            registering: false
          });
      });

最后的finally中的setState就会报这个错误。
解决方式:在 mount 时给个标志位 _mounted=true,unmounted时 _mounted=false;然后调用setState时 _mounted && setState
3、react-router-v4 使用 redirect 不生效


捕获.PNG

这个包在安装react-router-dom时应该自己安装好了,但有时没安上,需要重新手动安装一次

npm install path-to-regexp 

7、一些更新

1、在组件之间复用状态逻辑很难(目前的 render props || HOC 方案并不是很优雅)
2、复杂组件变得难以理解(不相关的代码被集中在生命周期中,监听与移除监听位于不同位置容易导致bug)
3、Class组件不易于理解,函数组件编译后代码体积更小

  • 内置 Hooks

1、useState
a、唯一参数就是state初始值,不一定是对象
b、返回 state 和用于改变state的函数

import React, { useState } from 'react';

function Example() {
  // 声明一个叫 “count” 的 state 变量。
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

2、useEffect
a、第一个参数是一个函数,函数内可以访问 props 和 state。第二个参数是依赖项,有变化时才会重新执行
b、每次组件更新后都会调用,包括第一次渲染
c、可通过返回一个函数来消除副作用,如取消订阅
d、同一组件内部可多次使用 useEffect,如有多个副作用


参考

React.js 小书

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,423评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,147评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,019评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,443评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,535评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,798评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,941评论 3 407
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,704评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,152评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,494评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,629评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,295评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,901评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,742评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,978评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,333评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,499评论 2 348