react

2020/06/02 react

react基础笔记整理

1、jsx

jsx本质上就是js代码,jsx是写结构的语法糖,会被Babel工具转变为JavaScript代码。

在jsx相关使用

  1. 可以使用表达式,比如三目,模板字符串,或者简单的数学运算,函数执行等,基本上和vue的差不多
  2. 根节点只能有一个,如果需要定义一组节点,还不想定义一个父节点包裹,可以使用<Fragment></Fragment>包裹,就不会生成了。 import React, {Fragment} from 'react';
  3. 可以嵌套
  4. 设置属性,比如style<p style=></p>.另外class需要写成className,连接属性要改成驼峰的,比如marginLeft

2、组件

react声明组件有两种方式,要么通过函数声明,要么通过类声明组件。

2.1 函数组件

import React from 'react';
import ReactDOM from 'react-dom';

ReactDOM.render(
  <div>
    你好react
    <People />
  </div>,
  document.getElementById('root')
)

function People(){
  return (
    <div>peoplddd</div>
  )
}

2.2 类组件

class声明的类组建,需要放到render上面,因为js的规则,function是可以的。

import React from 'react';
import ReactDOM from 'react-dom';

class Man extends React.Component {
  render() {
    return (
      <div>mannnnn</div>
    )
  }
}

ReactDOM.render(
  <div>
    你好react
    <Man/>
  </div>,
  document.getElementById('root')
)

3、组件传值

使用props来传递,和vue类似。不同的是,函数式组件接受数据是用传参,类组件是用this

3.1 函数式传参

使用第一个参数来接受

function People(props){
  return (
    <div>{props.name}</div>
  )
}

ReactDOM.render(
  <div>
    你好react
    <People name='cat'/>
  </div>,
  document.getElementById('root')
)

还可以传其他值

 <People name='cat' number={666}  boo={true}  fn={fn => 1}/>

传递子组件


function People(props){
  return (
  <div>
    {props.name}
    {props.children}
  </div>
  )
}

ReactDOM.render(
  <div>
    你好react
    <People name='cat' children={<div>我是children</div>}/>
  </div>,
  document.getElementById('root')
)

或者这样也可以

ReactDOM.render(
  <div>
    你好react
    <People name='cat' >
      <div>我是children</div>
    </People>
    <Man name="said"/>
  </div>,
  document.getElementById('root')
)

3.2 类组件传参

类组件使用实例接受,也就是用this

class Man extends React.Component {
  render() {
    return (
    <div>{this.props.name}</div>
    )
  }
}

ReactDOM.render(
  <div>
    你好react
    <People name='cat'/>
    <Man name="said"/>
  </div>,
  document.getElementById('root')
)

方便起见,我们改成这样

class Man extends React.Component {
  render() {
    let {name} = this.props;
    return (
    <div>{name}</div>
    )
  }
}

3.3 设置默认值

设置组件的默认值使用defaultProps

function People(props){
  console.log(props)
  return (
  <div>
    {props.name} age:{props.age}
    {props.children}
  </div>
  )
}

People.defaultProps = {
  age: 20
}

3.4 对传参进行类型检查

检查组件参数的类型使用prop-types

import PT from 'prop-types'
function People(props){
  return (
  <div>
    {props.name} age:{props.age}
    {props.children}
  </div>
  )

People.defaultProps = {
  age: 20
}
People.propTypes = {
  name: PT.string
}
ReactDOM.render(
  <div>
    你好react
    <People name={20} >
      <div>我是children</div>
    </People>
    <Man name="said"/>
  </div>,
  document.getElementById('root')
)

这样就成功的报警告了:Warning: Failed prop type: Invalid prop name of type number supplied to People, expected stringin People`

prop-types的值有下面几种

  1. PT.array
  2. bool
  3. number
  4. object
  5. string
  6. symbol
  7. node
  8. element

如果需要设置必填,可以设置isRequired

PT.string.isRequired

4、组件的内部状态 state

只有类组件才有内部状态,其实这个和vue的data类似。vue是数据驱动,react更多的是状态驱动,每一次修改state对于react都是状态改变,然后去修改值,每次状态改变都会比较新旧的virtual DOM 的结果,对变化的部分进行更新。

修改state的值, 这个和微信小程序的写法相似

this.setState({
  name: 'new name'
})

之所以说state是内部状态是因为state完全受控于当前组件,其他组件的state修改也不会影响到当前组件的state。

在一次事件执行期间的所有setState操作会合并成一次操作,然后更新render函数。所以在当前事件里获取到的state的值都是改变之前的值。

// state =  {a : 1};

onClock(()=>{
  this.setSatet({
    a: 2
  })
  console.log(this.state.a) // 1
})

当你想要获取你当前修改的值,可以使用函数式写法,给setState传入一个函数,它接收两个参数,第一个是你传入的参数,第二个是props,需要有一个返回值,返回你要修改的对象。

// 当前组件叫 Man
<div onClick={()=>{
  this.setState({
    name: ''
  }})

  this.setState((prev, props)=>{
    // 这里获取的是上面设置的值
    console.log(prev, props)  // {name: "ccccat"}  {name: "said"}
    return {
      name: '又修改了一次'
    }
  })
}}></div>


// 使用的地方
 <Man name="said"/>

5、事件

关于事件的写法上面也已经有了,DOM原生事件会接收一个event参数,在react里面也有,只不过这个是一个合成对象,react对这个进行处理了。

<div onClick={(event)=>{
  console.log(event);
}}>{name}</div>

可以使用event进行阻止冒泡event.stopPropagation()或者阻止默认行为event.preventDefault()

react支持的事件及事件对象更多看文档。

react事件方面最大的问题可能就是this了,this其实是JavaScript老生常谈的话题了,在行内绑定事件的时候如果没有用箭头函数,会找不到this,需要使用bind绑定以下this到当前实例,最好用箭头函数就好了。

在行内绑定this会有一个问题,就是由于组件每次会重新渲染,然后事件都会被重新注册,本来是没必要重新替换的,由于绑定了this,会重新返回一个事件,react会以为需要重新渲染,多多少少会有一些浪费性能,我们可以在constructor里面进行绑定

this.handleEvent = this.handleEvent.bind(this);

最好的方案是使用箭头函数或者官网推荐的类属性语法。

this.handleEvent = () => {
  // sth
}

6、生命周期函数

装载阶段:

  1. constructor()
  2. static getDerivedStateFromProps():在render之前可修改state的函数,不改变就返回null
  3. render():渲染虚拟DOM
  4. componentDidMount():DOM挂载完成

更新阶段:

  1. static getDerivedStateFromProps():父组件更新,子组件可以使用这个进行监控更新
  2. shouldComponentUpdate(nextProp, nextState):用于性能优化,返回一个true组件正常更新,返回false,视图不会更新。
  3. render()
  4. getSnapshptBeforeUpdate(prevProp, prveState):需要配合componentDidUpdate一起使用,获取一个更新之前的快照。它执行的时候已经计算好新的virtual DOM,但是浏览器还未更新DOM。
  5. componentDidUpdate(p, s, shot):更新已完成的回调,第三个参数是getSnapshptBeforeUpdate的返回值。

卸载阶段:

  1. componentWillUnmount():

错误处理:

  1. componentDidCatch(error, info): 捕获子组件的生命周期函数内抛出的错误。

7、获取DOM

想要获取真实DOM节点,需要在组件render之后,然后才可以在componentDidMount函数中拿到。

方式一:ref

<div ref= {(el) => {console.log(el)}}></div>

方式二:createRef()

import React, {createRef} from 'react';
constructor(props){
  this.div1 = createRef();
}
render(){
  return (
    <div ref= {this.div1}></div>
  )
}

8、受控组件

当插入一个表单组件,例如input,渲染完成后输入会正常输入,由于在react中,组件的变化由状态变化导致的,而现在状态没有变,这个input不受react控制,这种组件叫做非受控组件,也就是说它的状态变化不受react控制的,如果想控制它,就需要给input加一个value值,一旦加了value值,只要react不改变value,input永远不会改变。

<input value=""></input>

监控了input之后的处理

constructor(props){
  super(props)
  this.state =  {
    name: 'ccccat',
    val: ''
  }
}

changeInput = (e) => {
  let val = e.target.value;
  console.log(val)
  this.setState({
    val: val
  })
}

render() {
  let {name, val} = this.state;
  return (
    <input value={val} onChange={this.changeInput}></input>
  )
}

9、路由

使用路由需要安装react-router-dom

入口文件的代码:

import React from 'react';
import ReactDOM from 'react-dom';
import { HashRouter as Router, Route } from 'react-router-dom'

import Home from './components/Home'
import User from './components/User'

let root = document.getElementById('root');

ReactDOM.render(
  <Router>
    <>
      <Route path="/" component={Home}  />
      <Route path="/User" component={User}  />
    </>
  </Router>,
  root
)

User.tsx Home和User内部代码基本上一致:

import React from 'react';
// RouteComponentProps 是定义好的类型,是props的值对应的类型
import { RouteComponentProps } from 'react-router-dom';

type Props = RouteComponentProps & {
 // 这里可以写我们自定义的类型
}

// <Props>: 如果上面不引用RouteComponentProps然后也不定义这个的话,在里面是拿不到this.props.location的,只能拿到this.props,因为默认只有一个props,然后props有一个children,类型是一个reactElement,所以需要定义好,然后才可以使用

// this.props返回了三个对象
// 1. history: 历史对象,不是window.history,是内部的一个实现
// 2. location:当前路径的一些信息,与history.location一致
// 3. match:匹配对象
export default class extends React.Component<Props> {
  render() {
    console.log(this.props.location)
    return (
      <div>User</div>
    )
  }
}

10、hooks

让函数组件具有类组件功能的一个东西。

先实现一个class组件,然后用函数组件+hooks改写。

import React from 'react';

class myCount extends React.Component {
  state: {
    count: 0
  }
  componentDidMount(){
    this.interval = setInterval(()=>{
      this.setState({count: this.state.count+1})
    }, 1000)
  }

  componentWillUnmount(){
    if(this.interval){
      clearInterval(this.interval)
    }
  }

  render(){
    return <span>{this.state.count}</span>
  }
}

接下来我们改写:

import React, {useEffect, useState} from 'react';

function myCount(){
  // 声明一个存储的变量count和修改变量的方法setCount,初始值为0
  const [count, setCount] = useState(0);

  // 在组件更新完成之后执行回调,在组件销毁的时候执行返回的回调
  useEffect(()=>{
    const interval = setInterval(()=>{
      // setCount(1)
      setCount(count => count+1)
    }, 1000)
    return () => clearInterval(interval)
  }, [])
}

10.1 state Hook

这个hook有两个api:useState, useReducer

  • useState:传入一个初始值,返回一个数组,需要结构,如上面的例子[count, setCount] = useState(0);,这个时候count的值就是0,setCount就是更新count的方法,有两种使用方式
  1. setCount(1):重新给count赋值
  2. setCount(count => count+1):提供一个回调函数,参数是最新的count值。
  • useReducer:多接受一个回调,返回值和useState一致,回调函数内部的逻辑要自己写,这个和redux用法类似
import React, {useEffect, useState, useReducer} from 'react';
function countCallBack(count, action){
  switch (action.type){
    case 'add':
      return count + 1;
    case 'minus':
      return count - 1;
    default: 
      return count;
  }
}

function myCount(){
  const [count, dispactch] = useReducer(countCallBack, 0);

  useEffect(()=>{
    const interval = setInterval(()=>{
      dispactch({type: 'add'});
    }, 1000)
    return () => clearInterval(interval)
  }, [])
}

10.2 effect Hook

接受的参数回调的执行是在组件渲染包括重新渲染的时候执行的,回调返回的函数是在组件卸载的时候执行的, 第二个参数是依赖,类似于白名单,里面传入的字段的值修改后才会重新渲染。

useEffect(()=>{
  console.log('执行的方法')
  return () => console.log('卸载后的回调')
}, [count])

useLayoutEffect:在渲染前执行的方法,比effect要早。

11、redux

redux是一个单项数据流的管理工具,它的作用:存取公用性数据;

安装:

yarn add redux

使用:

  1. 创建一个store/store.js
  2. 输入以下代码
import { createStore } from 'redux';

// 默认的state
const initialState = {
  count: 0
}

/**
 * @param {*} state  {}
 * @param {*} action   {type: xx, data: {}};
 */

function reducer(state, action) {
  switch (action.type){
    case 'add':
      // sth
      // 这里必须return出一个新的对象
      return {count: state.count +1 };
    default:
      // sth...
      return state;
  }
}

const store = createStore(reducer, initialState);

console.log(store);
console.log(store.getState());

// 创建一个store
export default store;
  1. 获取数据:使用getState()获取数据
    store.getState()
    
  2. 更新数据: 使用dispatch传入一个action。
    store.dispatch({type: add, data: {count: 2}})
    
  3. 引入store:在需要使用store的页面引入,如下方法使用
import store from './store/store'
console.log(store.getState());
store.dispatch({ type: 'add' })
console.log(store.getState());
// {count: 0}
// {count: 1}

Search

    Table of Contents