封面 pid: 83647245

基础

React使用JSX来描述组件(相当于Vue的template),在JSX中大括号中的内容将会被当做JS表达式来解析,下面这个例子style属性接收到了一个{color:'#5abebc'}的对象,{content}被解析为2233(这类解析会自动转义防止xss攻击)。因为JSX的特性,HTML中跟JS关键字冲突的属性被重新定义如class=>className、for=>htmlFor。如果需要嵌入解析html代码,使用dangerouslySetInnerHTML属性

let content = '2233'
let jsx1 = <div style={{color:'#5abebc'}}>{content}</div>
// jsx本身也是表达式,可以作为一个变量,
let jsx2 = (
  <p>
    <span className="hint">{content}</span>
  </p>
)
// 跨行写jsx需要括号包裹

let jsx3 = <div dangerouslySetInnerHTML={{__html:'<p>2333</p>'}}></div>

使用函数或者class来定义一个组件。若使用函数定义,接收一个props,返回JSX;若使用class定义,需要继承React.Component、实现render方法并返回JSX。props包含了父组件传给子组件的属性,并且传递是单向的

import {Component} from 'react' 

function MyCom1(props){
  return (
    <p>{props.content}</p>
  )
}

class MyCom2 extends Component{
  render(){
    return <p>{this.props.content}</p>
  }
}

函数定义的组件是无状态的,通常只能接收props并作展示。class定义的组件是有状态的(state对象),并且可以基于各种钩子方法和自定方法实现复杂逻辑。在组件中,不能直接操作state,只能使用this.setState()来合并更新state

class MyCom3 extends Component{
  constructor(props){
    super(props)
    this.state = {content:'2333'} //状态!
  }

  myfun(){// 假如要在该方法修改state
    this.setState({content:'6666'})
  }
  render(){
    return <p>{this.state.content}</p>
  }
}

组件的stateprops可能会异步更新,如果某个状态的要依赖他们确定,setState()应该接收一个函数来进行更新。setState()本身也是异步的,如果需要在状态更新后执行某些操作,可以在第二个参数指定回调函数

this.setState(
  (state,props)=>{content: state.v1 +props.v2},
  ()=>console.log('state updated')
)

在JSX中使用JS中的条件判断(if/else、&&、三目)来实现条件渲染(相当于v-if),如果需要阻止渲染,可以返回null

render(){
  const sex = this.props.sex
  const isLogin = this.props.isLogin
  if(!this.props.show)return null

  let hn
  if(this.props.level===1)
    hn = <h1>666</h1>
  else
    hn = <h2>666</h2>

  return(
    <div>
      {hn}
      <p>{sex?'先生':'女士'}</p>
      {isLogin && <button>注销</button>}
    </div>
  )
}

在JSX中使用JS中的map()根据数组遍历生成组件(相当于v-for),遍历生成的组件应该带上key唯一属性

render(){
  const data = [1,2,3]
  return(
    <ul>
      {data.map(e=><li key={e}>{e}</li>)}
    </ul>
  )
}

React中数据流是单向的,表单数据的双向绑定要自己实现,通过props传入要绑定的数据,通过事件传入回调函数实现数据更新

class NameForm extends Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    //回调函数需要进行this绑定
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  /*使用箭头函数实现的this绑定
  handleChange = (event)=>{
    this.setState({value: event.target.value});
  }*/

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('提交的名字: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          名字:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="提交" />
      </form>
    );
  }
}

如果多个input元素使用同一个事件回调函数,可以为其添加name属性,在回调函数中可以通过event.target.name来判别是哪个元素。

如果多个子组件需要响应相同的变化数据,可以将他们的state提升到父组件中,父组件通过props给子组件分发数据和事件回调。

对于自定义组件,props上有一个children属性(即props.children)用于表示传入的子组件(标签间的内容,相当于slot)

生命周期

React生命周期总体上分为挂载、更新、卸载三个阶段。
挂载阶段(创建组件):

  • constructor(),state等数据初始化
  • componentWillMount(),首次渲染之前执行
  • render(),(首次)渲染挂载dom
  • componentDidMount(),首次渲染之后执行,通常发起请求获取数据

更新阶段(props或state更新):

  • componentWillReceiveProps(nextProps)props更新特有,可以在此根据props的变化执行相关逻辑(更新state、跳转等...)
  • shouldComponentUpdate(nextProps,nextState),返回布尔值指示是否终止后续更新阶段,主要用于性能优化(如父组件发生更新但传入的props无变化,可以阻止子组件的更新)
  • componentWillUpdate(nextProps,nextState),更新前执行
  • render(),diff比较新旧vdom,渲染更新dom
  • componentDidUpdate(nextProps,nextState),更新后执行

卸载阶段(销毁组件):

  • componentWillUnmount(),销毁前执行,通常清除事件监听等收尾

在16+版本中,引入了getDerivedStateFromProps(nextProps, prevState),意在替换componentWillReceiveProps (nextProps)中更新state的行为。该函数需声明为静态函数,不能访问this,返回一个对象以更新state,返回null表示不更新,必须返回两者之一。

还引入了getSnapshotBeforeUpdate(prevProps, prevState),这里暂略

进阶(待补充)

Hook

Hook,只用于函数组件的顶层,可以使无状态组件拥有有状态组件的特性。Hook主要有useStateuseEffect,他们都可以执行多次。
useState用于声明state变量,接收一个值作为变量的初始值,返回变量及更新变量的方法。
useEffect可以让你执行副作用操作(数据请求、dom操作等),第一个参数接收一个函数f1用于执行副作用;f1可以选择返回一个函数f2用于清除副作用,f2会在销毁组件前执行。第二个参数用于指定副作用何时执行,如果不传值副作用会在每次render后执行;如果传入[],副作用仅在组件挂载和卸载阶段执行一次;如果传入非空数组,副作用会在数组中的值变化时执行

import React, { useState, useEffect } from 'react'
function Mycom(){
  //得到了一个state变量count及修改他的方法
  const [count, setCount] = useState(0);

  let interval;
  useEffect(()=>{
    interval=setInterval(()=>console.log(233),1000)
    return ()=>clearInterval(interval)
  })

  //仅在count更新时执行副作用
  useEffect(()=>document.title=count,[count])
  return(
    <span onClick={setCount(count+1)}>{count}</span>
  )
}

高阶组件HOC

高阶组件是一个函数,它接收一个组件,返回一个新组件。代码复用是HOC的一个用法,如果多个组件存在相同或相似的逻辑处理,可以将这一部分抽离到高阶组件,让高阶组件去修饰被包裹组件。

HOC应当是纯函数,不应该修改传入的组件,也不应该继承传入的组件,而是采用组合。并且为了不阻碍diff的复用,不要在render函数中使用HOC。

Ref

与Vue相似,React提供ref来直接操作dom或组件,以解决常规方式无法解决的问题。ref使用React.createRef()创建,并通过ref属性附加到React元素上实现绑定。ref不能绑在函数组件,只能绑在class组件或者HTML元素上。通常在组件初始化时创建ref并在render()中绑定ref,之后通过ref.current访问被绑定的dom或组件

class MyCom1 extends Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }

  componentDidMount(){
    this.myRef.current.focus()
  }

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

如果函数组件要使用ref,使用useRefHook。

function MyCom2(){
  const myRef = useRef(null)
  return <input ref={this.myRef} />
}

另一种绑定ref的方法叫回调ref,该方案要求传入ref属性以一个函数,在绑定时执行,函数接收绑定的dom或组件

class MyCom3 extends Component {
  constructor(props) {
    super(props);
    this.myRef = null;
  }

  setRef = (el) => this.myRef=el

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

路由

yarn add react-dom-router

在React中,路由的配置是像使用组件一样在JSX中进行描述,Router组件为容器(通常每个组件只有一个且在顶层),里面的Link组件用于跳转(相当于Vue中的router-link),Route组件用于描述路由匹配规则、渲染的组件、渲染区域(相当于Vue中带配置项的router-view,但是可以存在多个)。

import { BrowserRouter as Router, Route, Link, hashHistory } from 'react-router-dom'
import Homepage from './Homepage'
import Article from './Article'

render(){
  return(
    <Router>
      <ul>
        <li><Link to="/">首页</Link></li>
        <li><Link to="/post/1">文章1</Link></li>
      </ul> 
      <Route path="/" exact component={Homepage} />
      <Route path="/post/:id" component={Article} />
    </Router>
  )
}

上面的例子还用到了动态路由,这个用法和Vue是相似的。动态路由的参数在子组件中通过props.match.params获取。

React中重定向的方法有两种,一种是嵌入Redirect组件(一旦渲染就跳转),一种是编程式导航

import {Redirect} from 'react-router-dom'

render(){
  //编程式导航
  //this.props.history.push("/about")
  return(
    <div>
      <Redirect to="/about" />
    </div>
  )
}

有了上面路由相关组件的对应关系,React中路由嵌套和Vue是类似的(除了Route的表现有点不一样)

全局状态管理

yarn add react-redux redux

Redux中三个重要的概念,Action、Store、Reducer

  • Action:请求(通常由type和value组成),组件发起一个请求以修改某个状态
  • Store:所有状态的容器,并且联系着Action和Reducer
  • Reducer:处理请求的逻辑,根据请求的类型和携带值返回更新后的状态

只有Store有对状态的修改权限,通过Store提供的dispatch(action)方法发起请求,Store将请求转发给Reducer处理,Reducer处理后返回新的状态让Store进行合并更新

//store/index.js
//创建并导出一个store
import {createStore} from 'redux'
import reducer from './reducer.js'
const store = createStore(reducer)
export default store


//store/reducer.js
const defaultState = {count:1}
export default (state=defaultState, action)=>{
  switch(action.type){
    'changeCount': return {count:action.value}
    default: return state
  }
}

//某个组件中修改状态
import store from 'store/index.js'
//...
componentDidMount(){
  let state = store.getState()//获取store中的状态
  action = {type:'changeCount',value:233}
  store.dispatch(action)
}
//...

使用react-redux简化状态修改流程,使用Provider组件向子孙组件注入store,在需要跟store互动的子组件中使用connect方法进行状态和请求的映射。connect方法是一个高阶函数,第一个参数是store状态到组件props的映射,第二个参数是请求到组件props的映射,执行后返回函数,再接收组件导出即可

//父组件FaCom
import {connect} from 'react-redux'
import store from 'store/index.js'

const FaCom = (props)=>{
  return (
    <Provider store={store}>
      <SonCom />
    </Provider>
  )
}

export default FaCom
//子组件SonCom
import {connect} from 'react-redux'

const mapState2Props = (state)=>{
  return{
    //将store中的count映射到组件的props.count
    count:state.count
  }
}

const mapDispatch2Props = (dispatch)=>{
  return{
    //将发起请求的方法映射到组件的props.countChange
    countChange(e){
      dispatch({type:'changeCount',value:e.target.value})
    }
  }
}

const SonCom = (props) => {
  //直接使用映射的属性
  return (<input onChange={props.countChange} value={props.count}/>)
}

export default connect(mapState2Props,mapDispatch2Props)(SonCom)

    添加新评论 | #

    Markdown Supported
    简单数学题:NaN + NaN =
    表情

    Comments | ?? 条评论

    • 单身为狗 21 年

    • 朝循以始 夜继以终

    • Blog: Von by Bersder

    • Alive: 0 天 0 小时 0 分

    Support:

    frame ssr server DNS music player

    © 2019-2021 ᛟᛊᚺᛁᚾᛟ

    back2top