React起步

一、安装

1.使用 create-react-app 快速构建 React 开发环境,并启动

npx create-react-app hello-react
cd hello-react
npm start

2.注意:创建项目反应慢,是因为在寻找相关配置registry时失败了,默认使用了npm安装

解决方法如下:

将npm的register设置为cnpm:

npm config set registry https://registry.npm.taobao.org

执行之后检查是否设置成功

npm config get registry

若返回 https://registry.npm.taobao.org ,则设置成功,再创建react项目。

 

二、元素渲染(两种方式)

1.ReactDOM.render()函数渲染

const element = <h1>Hello, world!</h1>;
ReactDOM.render(
    element,
    document.getElementById('example')
);

2.React.Component渲染

class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
      </div>
    );
  }
}

 

三、jsx

特征:有<>就是xml,有{}就是js表达式

ReactDOM.render(
    <h1>Hello, world!</h1>,
    document.getElementById('example')
);

 

四、组件(两种)

 注意:组件只包含一个顶层标签,命名首字母大写

组件分为有状态组件和无状态组件:

1.函数组件(无状态组件):不可以拥有状态,不能拥有生命周期,通过属性实现数据传递(props.X)

//ES5写法
function HelloMessage(props) {
    return <h1>Hello World!</h1>;
}
//ES6写法
const HelloMessage = (props) => {
    return <h1>Hello World!</h1>;
}
 
const element = <HelloMessage />;
 
ReactDOM.render(
    element,
    document.getElementById('example')
);

2.class组件(有状态组件):可以拥有状态,可以拥有生命周期,通过this接收状态和属性(this.state.X、 this.props.X)

class Welcome extends React.Component {
  render() {
    return <h1>Hello World!</h1>;
  }
}

 

五、props

props用于传递组件间传递数据

注意:props是不可变的,不能被修改的,只能读取。

1.函数组件中通过props属性传递数据:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

const element = <Welcome name="Sara" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

2.class组件中通过this.props传递数据(可用defaultProps设置默认props)

import React, { Component } from 'react'

class DefaultProps extends Component {
    render() {
        return (
            <div>
                <p>hello, {this.props.name}</p>
            </div>
        )
    }
}

DefaultProps.defaultProps = {
    name: 'dsd'
}

export default DefaultProps

 

使用propTypes属性对props进行类型检查:

验证器:prop-types 库

详细验证器:https://react.docschina.org/docs/typechecking-with-proptypes.html?

import React, { Component } from 'react'
import PropTypes from 'prop-types'

class DefaultProps extends Component {
    render() {
        return (
            <div>
                <p>hello, {this.props.name}</p>
            </div>
        )
    }
}

DefaultProps.defaultProps = {
    name: 'dsd'
}

DefaultProps.propTypes = {
    name: PropTypes.string
}

export default DefaultProps

 

六、state

 state用于改变组件内部数据。

注意:函数组件是无状态组件,没有state,只有class组件才有state

通过this.setState()修改state的值:

import React, { Component } from 'react'

class StateDemo extends Component {
    constructor(props) {
        super(props);
        this.state = {
            name: '小红'
        }
    }
    changState = () => {
        this.setState({
            name: '小明'
        })
    }
    render() {
        return (
            <div>
                <p>{this.state.name}</p>
                <button onClick={this.changState}>修改state</button>
            </div>
        )
    }
}

export default StateDemo

 

七、生命周期

注意:只有class组件才有生命周期,函数组件没有。

  • componentWillMount 在渲染前调用,在客户端也在服务端。

  • componentDidMount : 在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异步操作阻塞UI)。

  • componentWillReceiveProps 在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。

  • shouldComponentUpdate 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。 
    可以在你确认不需要更新组件时使用。

  • componentWillUpdate在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。

  • componentDidUpdate 在组件完成更新后立即调用。在初始化时不会被调用。

  • componentWillUnmount在组件从 DOM 中移除之前立刻被调用。

 

 

八、事件处理

1.命名:采用小驼峰命名法onClick、onChange

2.调用时注意this指向问题,事件调用方法,方法中的this指向调用事件的对象(2种写法)

(1)用bind()绑定this:

changState是ClickDemo类的方法,按钮点击事件调用changState,changState中的this默认指向button,要绑定this指向ClickDemo类才能正确执行代码中的this.setState

import React, { Component } from 'react'

class ClickDemo extends Component {
    constructor(props) {
        super(props);
        this.state = {
            name: '小红'
        }
        
        // 绑定this指向ClickDemo
        this.changState = this.changState.bind(this);
    }
    
    changState(){
        this.setState({
            name: '小明'
        })
    }
    render() {
        return (
            <div>
                <p>{this.state.name}</p>
                <button onClick={this.changState}>修改state</button>
            </div>
        )
    }
}

export default ClickDemo

 

(2)class fields 方法:箭头函数中的this在定义时就确定为ClickDemo

import React, { Component } from 'react'

class ClickDemo extends Component {
    constructor(props) {
        super(props);
        this.state = {
            name: '小红'
        }
    }
    
    // 箭头函数中的this在定义时就确定为ClickDemo
    changState = () => {
        this.setState({
            name: '小明'
        })
    }
    render() {
        return (
            <div>
                <p>{this.state.name}</p>
                <button onClick={this.changState}>修改state</button>
            </div>
        )
    }
}

export default ClickDemo

 

九、条件渲染

1.if语句

import React, { Component } from 'react'

class IfDemo extends Component {
    constructor(props) {
      super(props);
      this.state = {
        isLoggin: false
      }
    }
    render() {
      let Tip;
      if(this.state.isLoggin) {
        Tip = <p>您已经登陆</p>
      } else {
        Tip = <p>您还未登陆</p>
      }
      return (
          <div>
              {Tip}
          </div>
      )
    }
}

export default IfDemo

2.&&运算符

import React, { Component } from 'react'

class AndDemo extends Component {
    constructor(props) {
      super(props);
      this.state = {
        list: [1,2,3]
      }
    }
    render() {
      let Tip;
      return (
        <div>
          {this.state.list.length > 0 && 
            <h2>你有{this.state.list.length}个元素</h2>
          }
        </div>
      )
    }
}

export default AndDemo

3.三目运算符

import React, { Component } from 'react'

class AndDemo extends Component {
    constructor(props) {
      super(props);
      this.state = {
        list: []
      }
    }
    render() {
      let Tip;
      return (
        <div>
          {this.state.list.length > 0 ? 
            <h2>你有{this.state.list.length}个元素</h2> :
            <h2>你没有元素</h2>
          }
        </div>
      )
    }
}

export default AndDemo

 

十、列表渲染

通常使用map()遍历数组,每项设置key来区分

import React, { Component } from 'react'

class List extends Component {
    constructor(props) {
        super(props);
        this.state = {
            list: [
                {name: '小红',age: 11},
                {name: '小明',age: 51},
                {name: '小黑',age: 71}
            ]
        }
    }
    render() {
        return (
            <div>
                {this.state.list.map((item, index) => {
                    return <p key={index}>{item.name}今年{item.age}岁</p>
                })}
            </div>
        )
    }
}

export default List

 

十一、受控组件

1.input(textarea写法相同)

import React, { Component } from 'react'

class List extends Component {
    constructor(props) {
        super(props);
        this.state = {
            name: ''
        }
    }
    changeName = (e) => {
        console.log(e.target.value)
        this.setState({
            name: e.target.value
        })
    }
    render() {
        return (
            <form>
                <div>
                    <label>name:</label>
                    <input type="text" value={this.state.name} onChange={this.changeName} />
                </div>
            </form>
        )
    }
}

export default List

 

2.多个input

import React, { Component } from 'react'

class List extends Component {
    constructor(props) {
        super(props);
        this.state = {
            name: '',
            age: ''
        }
    }
    changeInput = (e) => {
        console.log(e.target.value)
        this.setState({
            [e.target.name]: e.target.value
        })
    }
    render() {
        return (
            <form>
                <div>
                    <label>name:</label>
                    <input name="name" type="text" value={this.state.name} onChange={this.changeInput} />
                </div>
                <div>
                    <label>age:</label>
                    <input name="age" type="text" value={this.state.age} onChange={this.changeInput} />
                </div>
            </form>
        )
    }
}

export default List

 

3.select

import React, { Component } from 'react'

class SelectDemo extends Component {
    constructor(props) {
        super(props);
        this.state = {
            age: '12'
        }
    }
    changeSelected = (e) => {
        console.log(e.target.value)
        this.setState({
            age: e.target.value
        })
    }
    render() {
        return (
            <form>
                <label>年龄</label>
                <select value={this.state.age} onChange={this.changeSelected}>
                    <option value="11">11</option>
                    <option value="12">12</option>
                    <option value="13">13</option>
                </select>
            </form>
        )
    }
}

export default SelectDemo

 

十二、ref

注意:

  • 当 ref 属性用于 HTML 元素时, ref 接收底层 DOM 元素作为其 current 属性。
  • 当 ref 属性用于自定义 class 组件时,ref 对象接收组件的挂载实例作为其 current 属性。
  • 你不能在函数组件上使用 ref 属性,因为他们没有实例。

使用:Refs 是使用 React.createRef() 创建的,并通过 ref 属性附加到 React 元素。在构造组件时,通常将 Refs 分配给实例属性,以便可以在整个组件中引用它们。当 ref 被传递给 render 中的元素时,对该节点的引用可以在 ref 的 current 属性中被访问。

import React, { Component } from 'react'

class RefDemo extends Component {
    constructor(props) {
        super(props);
        this.myref = React.createRef()
    }
    getRef = () => {
        console.log(this.myref.current)
    }
    render() {
        return (
            <div>
                <p ref={this.myref}>小红</p>
                <button onClick={this.getRef}>获取ref</button>
            </div>
        )
    }
}

export default RefDemo

 

十三、路由(react-router-dom)

 1.安装

cnpm i --save react-router-dom 

2.基本实现

import React from 'react';
import SelectDemo from './components/SelectDemo'
import { Switch, BrowserRouter as Router, Route, NavLink, Link } from 'react-router-dom'

function App() {
  return (
    <div className="App">
      {/* 将路由的组件用<Router>包裹起来 */}
      <Router>
        <nav>
          <ul>
            <li>
              {/* <Link to="">链接的使用 */}
              <Link to="/select">select</Link>
            </li>
            <li>
              <Link to="/refdemo">refdemo</Link>
            </li>
            <li>
              <NavLink exact to="/demo">demo</NavLink>
            </li>
          </ul>
        </nav>
        {/* <Switch>是指页面只出现下面路由中的一个路由 */}
        <Switch>
          {/* <Route path="">链接的设置(载入页面有3种写法) */}
          <Route path="/select" component={SelectDemo}></Route>
          <Route path="/refdemo"><RefDemo /></Route>
          <Route path="/demo" render={()=><p>demo</p>}></Route>
        </Switch>
      </Router>
    </div>
  );
}

export default App;

 

3.导航链接 高亮

 导航链接是使用链接时将<Link>改为<NavLink>,选中时会添加默认类active,自行在App.css中加入.active的样式即可

import React from 'react';
import SelectDemo from './components/SelectDemo'
import { Switch, BrowserRouter as Router, Route, NavLink, Link } from 'react-router-dom'
import './App.css'

function App() {
  return (
    <div className="App">
      {/* 将路由的组件用<Router>包裹起来 */}
      <Router>
        <nav>
          <ul>
            <li>
              {/* <NavLink to="">链接的使用 */}
              <NavLink to="/select">select</NavLink>
            </li>
            <li>
              <NavLink to="/refdemo">refdemo</NavLink>
            </li>
          </ul>
        </nav>
        {/* <Switch>是指页面只出现下面路由中的一个路由 */}
        <Switch>
          {/* <Route path="">链接的设置(载入组件有两种写法) */}
          <Route path="/select" component={SelectDemo}></Route>
          <Route path="/refdemo"><RefDemo /></Route>
        </Switch>
      </Router>
    </div>
  );
}

export default App;

 

4、exact属性:匹配规则,当有链接是/,而其他链接是/select之类时,为了不匹配到/,则在链接设置和使用时都加入exact

import React from 'react';
import './App.css';
import List from './components/List'
import SelectDemo from './components/SelectDemo'
import RefDemo from './components/RefDemo'
import { Switch, BrowserRouter as Router, Route, NavLink, Link } from 'react-router-dom'

function App() {
  return (
    <div className="App">
      <Router>
        <nav>
          <ul>
            <li>
              {/* 为了在选中/select时不匹配到/链接,使用exact属性 */}
              <NavLink exact to="/select">select</NavLink>
            </li>
            <li>
              <NavLink exact to="/refdemo">refdemo</NavLink>
            </li>
            <li>
              <NavLink exact to="/">List</NavLink>
            </li>
          </ul>
        </nav>
        <Switch>
          {/* 使用exact属性 */}
          <Route exact path="/select" component={SelectDemo}></Route>
          <Route exact path="/refdemo"><RefDemo /></Route>
          <Route exact path="/" component={List}></Route>
        </Switch>
      </Router>
    </div>
  );
}

export default App;

5、strict属性:精准匹配,当链接完全匹配时才能跳转

6、404页面设置:在所有设置链接后添加一个没有path属性的<Route>,表示匹配所有路由,当链接没有被设置时就自动匹配这个路由

import React from 'react';
import './App.css';
import List from './components/List'
import SelectDemo from './components/SelectDemo'
import RefDemo from './components/RefDemo'
import { Switch, BrowserRouter as Router, Route, NavLink, Link } from 'react-router-dom'
import NotFound from './components/NotFound';

function App() {
  return (
    <div className="App">
      <Router>
        <nav>
          <ul>
            <li>
              <NavLink exact to="/select">select</NavLink>
            </li>
            <li>
              <NavLink exact to="/refdemo">refdemo</NavLink>
            </li>
            <li>
              <NavLink exact to="/">List</NavLink>
            </li>
          </ul>
        </nav>
        <Switch>
          <Route exact path="/select" component={SelectDemo}></Route>
          <Route exact path="/refdemo"><RefDemo /></Route>
          <Route exact path="/" component={List}></Route>
          {/* 在设置路由最后加入没有path的路由,匹配前方没有设置的路由(404) */}
          <Route component={NotFound}></Route>
        </Switch>
      </Router>
    </div>
  );
}

export default App;

 

7.路由模式:history模式和hash模式

切换路由的模式只需引入BrowserRouter或HashRouter

import { BrowserRouter as Router } from 'react-router-dom'

或者

import { HashRouter as Router } from 'react-router-dom'

 

8、获取路由参数

(1)params参数

App.js:设置和使用路由

import React from 'react';
import './App.css';
import Param from './components/Param'
import { Switch, BrowserRouter as Router, Route, NavLink, Link } from 'react-router-dom'
import NotFound from './components/NotFound';

function App() {
  return (
    <div className="App">
      <Router>
        <nav>
          <ul>
            <li>
              {/* 使用路由 */}
              <NavLink exact to="/param/user/1">param</NavLink>
            </li>
          </ul>
        </nav>
        <Switch>
          {/* 设置路由 */}
          <Route path="/param/user/:id" component={Param}></Route>
          <Route component={NotFound}></Route>
        </Switch>
      </Router>
    </div>
  );
}

export default App;

Param.js:通过props.match获取路由参数:

import React, { Component } from 'react'

class Param extends Component {
    componentDidMount() {
        console.log(this.props.match)
    }
    render() {
        return (
            <div> param </div>
        )
    }
}

export default Param

 

(2)query参数(两种方法)

App.js:

import React from 'react';
import './App.css';
import Query from './components/Query'
import { Switch, BrowserRouter as Router, Route, NavLink, Link } from 'react-router-dom'
import NotFound from './components/NotFound';

function App() {
  return (
    <div className="App">
      <Router>
        <nav>
          <ul>
            <li>
              {/* 使用路由 */}
              <NavLink exact to="/query/user?id=1">param</NavLink>
            </li>
          </ul>
        </nav>
        <Switch>
          {/* 设置路由 */}
          <Route path="/query/user" component={Query}></Route>
          <Route component={NotFound}></Route>
        </Switch>
      </Router>
    </div>
  );
}

export default App;

 

方法一、使用URLSearchParams()

import React, { Component } from 'react'

class Query extends Component {
    render() {
        const query = new URLSearchParams(this.props.location.search)
        console.log(query.get("id")); // 1
        return (
            <div> query </div>
        )
    }
}

export default Query

 

方法二、使用Node中的querystring.parse()解析路由

注意:获取到的第一个数据有带着?

import React, { Component } from 'react'
import querystring from 'querystring'

class Query extends Component {
    render() {
        const query = querystring.parse(this.props.location.search)
        console.log(query); // {?id:1}
        return (
            <div> query </div>
        )
    }
}

export default Query

 

 

9.重定向

1.使用<Redirect from="" to="">进行重定向

import React from 'react';
import './App.css';
import List from './components/List'
import { Switch, BrowserRouter as Router, Route, NavLink, Link, Redirect } from 'react-router-dom'
import NotFound from './components/NotFound';

function App() {
  return (
    <div className="App">
      <Router>
        <nav>
          <ul>
            <li>
              {/* 使用路由 */}
              <NavLink exact to="/mine">重定向</NavLink>
            </li>
          </ul>
        </nav>
        <Switch>
          <Redirect from="/mine" to="/list" />
          <Route path="/list" component={List}></Route>
          <Route component={NotFound}></Route>
        </Switch>
      </Router>
    </div>
  );
}

export default App;

 

2.使用props.history.push()进行重定向

App.js:

import React from 'react';
import './App.css';
import { Switch, BrowserRouter as Router, Route, NavLink, Link, Redirect } from 'react-router-dom'
import NotFound from './components/NotFound';
import Push from './components/Push';
import List from './components/List'

function App() {
  return (
    <div className="App">
      <Router>
        <Switch>
          {/* 重定向的两个路由需定义才有路由对象(即props.history) */}
          <Route path="/list" component={List}></Route>
          <Route path="/push" component={Push}></Route>
          <Route component={NotFound}></Route>
        </Switch>
      </Router>
    </div>
  );
}

export default App;

Push.js:

import React, {Component} from 'react'

const PushDemo = (props) => {
    const toOther = () => {
        console.log(props)
        props.history.push('/list')
    }
    return (
        <div>
            <button onClick={toOther}>重定向</button>
        </div>
    )
}

export default PushDemo

 

3.使用props.history.replace()进行重定向(写法与props.history.push()一样)

 

10.withRouter:为未设为路由的组件添加路由对象

App.js:

import React from 'react';
import './App.css';
import { Switch, BrowserRouter as Router, Route, NavLink, Link, Redirect } from 'react-router-dom'
import Push from './components/Push';

function App() {
  return (
    <div className="App">
      {/* 被添加路由对象的组件需放在<Router>中 */}
      <Router>
        <Push />
      </Router>
    </div>
  );
}

export default App;

push.js:

import React, {Component} from 'react'
import {withRouter} from 'react-router-dom'

const PushDemo = (props) => {
    const toOther = () => {
        console.log(props)
        props.history.push('/list')
    }
    return (
        <div>
            <button onClick={toOther}>重定向</button>
        </div>
    )
}

// withRouter()为组件添加路由对象
export default withRouter(PushDemo)

 

11.Prompt:离开页面之前提示用户

App.js: 

import React from 'react';
import './App.css';
import { Switch, BrowserRouter as Router, Route, NavLink, Link, Redirect } from 'react-router-dom'
import List from './components/List';
import Prompt from './components/Prompt';

function App() {
  return (
    <div className="App">
      {/* 拥有的组件需放在<Router>中 */}
      <Router>
        <nav>
          <NavLink to="/list">list</NavLink>
        </nav>
        <Switch>
          <Route path="/list"><List></List></Route>
        </Switch>
        <Prompt / >
      </Router>
    </div>
  );
}

export default App;

Prompt.js:

import React, { Component } from 'react'
import {Prompt} from 'react-router-dom'

class PromptDemo extends Component {
    constructor(props) {
        super(props);
        this.state = {
            name: ''
        }
    }
    changeName = (e) => {
        this.setState({
            name: e.target.value
        })
    }
    render() {
        return (
            <div>
                <input value={this.state.name} type="text" onChange={this.changeName} />
                {/* 当name不为空时,离开页面会有prompt弹窗提示 */}
                <Prompt when={!!this.state.name} message={'确定要离开当前页面吗?'}></Prompt>
            </div>
        )
    }
}

export default PromptDemo

 

12.嵌套路由

App.js

import React from 'react';
import './App.css';
import { Switch, BrowserRouter as Router, Route, NavLink, Link, Redirect } from 'react-router-dom'
import Book from './components/Book/Book';
import Java from './components/Book/JavaBook';
import Web from './components/Book/WebBook';

function App() {
  return (
    <div className="App">
      {/* 拥有的组件需放在<Router>中 */}
      <Router>
        <nav>
          <NavLink to="/book/java">java</NavLink>
          <NavLink to="/book/web">web</NavLink>
        </nav>
        <Switch>
          <Book>
            <Route path="/book/java" component={Java}></Route>
            <Route path="/book/web" component={Web}></Route>
          </Book>
        </Switch>
      </Router>
    </div>
  );
}

export default App;

 

Book.js:

import React, { Component } from 'react'

class Book extends Component {
    render() {
        return (
            <div>
                <h2>book</h2>
                {/* 使用props.children放置子路由的内容 */}
                {this.props.children}
            </div>
        )
    }
}

export default Book

 JavaBook.js:

import React from 'react'

const Java = () => {
    return (
        <div>Java</div>
    )
}

export default Java

WebBook.js

import React from 'react'

const Web = () => {
    return (
        <div>Web</div>
    )
}

export default Web

 

十四、错误处理

错误处理:组件报错时,关掉页面错误后显示组件发生错误字样

1.创建处理错误的组件components/ErrorCatch.js: 使用componentDidCatch()捕获错误

import React from 'react'

class ErrorCatch extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            hasError: false,
            error: null,
            errorInfo: null
        }
    }
    componentDidCatch(error, errorInfo) {
        console.log(error);
        this.setState({
            hasError: true,
            error: error,
            errorInfo: errorInfo
        })
    }
    render() {
        if(this.state.hasError) {
            return <div> { this.props.render(this.state.error, this.state.errorInfo) } </div>
        } 
        return this.props.children;
    }
}

export default ErrorCatch

2.创建发生错误的组件components/Error.js:

import React from 'react'
const Error = () => {
    return (
        <div>
            {null.map((item,index) => {
                console.log(item)
            })}
        </div>
    )
}

export default Error

3.App.js中引入:

import React from 'react';
import logo from './logo.svg';
import './App.css';
import Error from './components/Error'
import ErrorCatch from './components/ErrorCatch'

class App extends React.Component {
  render() {
    return (
      <div>
        <ErrorCatch render={(error, errorInfo) => <p>{'组件发生错误'}</p>}>
          <Error />
        </ErrorCatch>
      </div>
    )
  }
}

export default App;

 

十五、shouldComponentUpdate()优化组件

对于不必一直进行更新的组件,在shouldComponentUpdate(nextProps,nextState)判断其props或state是否更新,若更新,则进行组件更新,若无更新,则阻止组件更新。

Interval.js:

import React from 'react'
import Child from './Child'
class Interval extends React.Component {
    timer = null
    constructor(props) {
        super(props);
        this.state = {
            count: 0
        }
    }
    componentDidMount() {
        this.timer = setInterval(()=> {
            this.setState({
                count: this.state.count + 1
            })
        }, 1000)
    }
    // 要在离开组件之前关闭定时器,避免组件卸载后继续更新state而报错
    componentWillUnmount(){
        console.log(123)
        clearInterval(this.timer)
    }

    render() {
        console.log('interval')
        return (
            <div>
                {this.state.count}
                <Child num="2"></Child>
            </div>
        )
    }
 }

 export default Interval

Child.js:

import React, { Component } from 'react'

class Child extends Component {
    // 当传入Child组件的值没有改变时,Child组件不重新渲染
    shouldComponentUpdate(nextProps,nextState) {
        if(this.props.num == nextProps.num) {
            return false;
        }
        return true;
    }
    render() {
        console.log('child')
        return ( 
            <div>Child</div>
        )
    } 
}

export default Child

 

十六、PureComponent

PureComponent对数据进行浅比较,自动安排是否需要重新渲染

注意:用在class组件,如果对象中包含复杂的数据结构,则有可能因为无法检查深层的差别,产生错误的比对结果。

Demo:将上个例子中的Child.js进行改造,获得同样效果,即child组件的num没有变化时,child组件不重新渲染

import React, { PureComponent } from 'react'

class Child extends PureComponent {
    render() {
        console.log('child')
        return ( 
            <div>Child</div>
        )
    }
}

export default Child

 

十七、React.memo()

注意:与React.PureComponent非常相似,但它适用于函数组件,但不适用于 class 组件。 

import React from 'react'

const Memo = (props) => {
    console.log(props.num);
    return (
        <div>
            {props.num}
        </div>
    )
}

export default React.memo(Memo)

 

十八、Fragment:虚标签

<Fragment>编译后无任何显示:

import React, { Component, PureComponent, Fragment } from 'react'

class List extends PureComponent {
    render() {
        console.log('child')
        return ( 
            <ul>
                <Fragment>
                    <li>列表1</li>
                </Fragment>
                <Fragment>
                    <li>列表2</li>
                </Fragment>
            </ul>
        )
    }
    
}

export default List 

编译后的列表为

<ul>
  <li>列表1</li>
  <li>列表2</li>
</ul>

十九、Context

Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。 

import React, { createContext, Component } from 'react'
// Top组件经过Middle组件传值给Bottom组件,在Middle组件中不需要用到该值,可使用createContext(),在Bottom组件中接收
const MyContext = createContext();

// Top组件中设置MyContext的值
class Top extends Component {
    render() {
        return (
            <MyContext.Provider value="12222">
                <Middle />
            </MyContext.Provider>
        )
    }
}

class Middle extends Component {
    render() {
        return (
            <Bottom />
        )
    }
}

// Bottom组件中将MyContext赋值给contextType,组件中可通过this.context获取MyContext
class Bottom extends Component {
    static contextType = MyContext;
    render() {
        return ( 
            <p>{this.context}</p>
        )
    }
}

export default Top

二十、HOOK

1.HOOK作用:在不编写 class 的情况下使用 state 以及其他的 React 特性。

2.基础Demo:

import React,{useState,useCallback} from 'react'

export default () => {
    // useState(0):设置count的默认值为0
    // setCount: 改变count的值
    const [count, setCount] = useState(0);

    return (
        <div>
            <p>count:{count}</p>
            // 使用setCount()改变count的值
            <button onClick={() => {setCount(count+ 1)}}>Add</button>          
        </div>
    ) 
}

3.useCallback():进行优化,若第二参数中的值没有改变,则不会执行第一个参数中的方法

import React,{useState,useCallback} from 'react'

export default () => {
    // useState(0):设置count的默认值为0
    // setCount: 改变count的值
    const [count, setCount] = useState(0);
    const [afterCount, setAfterCount] = useState(0);

    return (
        <div>
            <p>count:{count}</p>
            <p>afterCount:{afterCount}</p>
            <button onClick={() => {setCount(count+ 1)}}>Add</button>
            {/* useCallback(fun,[]):第二个参数值如果改变了,则执行第一个参数中的方法,若没有改变,则不执行 */}
            <button onClick={useCallback(() => {setAfterCount(afterCount+ 1)}, [count])}>Add</button>

        </div>
    ) 
}

4、userReducer(reducer,initialState):当state 逻辑较复杂且包含多个子值时,可使用userReducer(reducer,initialState),类似于redux

import React, { useReducer } from 'react'

const initialState = {
    count: 0
}

const reducer = (state, action) => {
    switch(action.type) {
        case "increment": 
            return {count: state.count + 1};
        case "decrement":
            return {count: state.count - 1};
        default: 
            throw new Error();
    }
}

function UseReducerDemo () {
    const [state, disptch] = useReducer(reducer, initialState) 
    return (
        <div>
            <p>{state.count}</p>
            <button onClick={() => disptch({type: 'increment'})}> + </button>
            <button onClick={() => disptch({type: 'decrement'})}> - </button>
        </div>
    )
}

export default UseReducerDemo

 

5.useContext(context):获取context

Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。

UseContextParentDemo.js:

import React from 'react'
import UseContextChildDemo from './UseContextChildDemo'

// React.createContext(默认值):创建context
export const MyContext = React.createContext('hello')
// 修改context的值
const UseContextParentDemo = () => {
    return(
        <MyContext.Provider value="hi">
            <UseContextChildDemo />
        </MyContext.Provider>
    )
}

export default UseContextParentDemo

UseContextChildDemo.js:

import React, { useContext } from 'react'
import {MyContext} from './UseContextParentDemo'

// 使用useContext(context)获取context
const UseContextChildDemo = () => {
    return(
        <div>
            <p>{useContext(MyContext)}</p>
        </div>
    )
}

export default UseContextChildDemo;

 

6.useEffect():

可以让你在函数组件中执行副作用操作。

副作用:数据获取,设置订阅以及手动更改react组件中的DOM等。

useEffect(): 代替三个生命周期:componentDidMount、componentDidUpdate、componentWillUnmount

第二个参数决定其代表哪个生命周期:

* 当没有第二个参数时, useEffect() 相当于componentDidMount、 componentDidUpdate

* 当第二个参数为[] 时, 相当于componentDidMount

*[count]: 只有count发生改变时, 才会触发componentDidUpdate

* return:相当于componentWillUnmount

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

export default () => {
    const [count, setCount] = useState(0);
    // useEffect(): 代替三个生命周期:componentDidMount、componentDidUpdate、componentWillUnmount
    useEffect(() => {
        document.title = `你点击了${count}下`;
    })

    return (
        <div>
            <p>你点击了{count}下</p>
            <button onClick={()=>{setCount(count + 1)}}>+1</button>
        </div>
    )
}

 

二十一、State

1.this.setState({})在生命周期中异步执行: 

import React, { Component } from 'react'

class StateDemo extends Component {
    constructor(props) {
        super(props);
        this.state = {
            count: 0
        }
    }

    // this.setState()在生命周期中异步操作,当执行第一个时,已经往下执行。来不及执行第二第三个
    componentDidMount() {
        this.setState({
            count: this.state.count + 1
        })
        this.setState({
            count: this.state.count + 1
        })
        this.setState({
            count: this.state.count + 1
        })
    }

    // 这里打印出的this.state.count值为1
    render() {
        return (
            <div>{this.state.count}</div>
        )
    }
}

export default StateDemo

 

2. 在生命周期中使用this.setState((prevState,props) =>{}),可在生命周期结束前完成执行

import React, { Component } from 'react'

class StateDemo extends Component {
    constructor(props) {
        super(props);
        this.state = {
            count: 0
        }
    }

    componentDidMount() {
        this.setState((prevState,props) => ({
            count: prevState.count + 1
        }))
        this.setState((prevState, props) => ({
            count: prevState.count + 1
        }))
        this.setState((prevState, props) => ({
            count: prevState.count + 1
        }))
    }

    // 这里打印出的this.state.count值为3
    render() {
        return (
            <div>{this.state.count}</div>
        )
    }
}

export default StateDemo

 

 

3.使用this.setState({},()=>{})

import React, { Component } from 'react'

class StateDemo extends Component {
    constructor(props) {
        super(props);
        this.state = {
            count: 0
        }
    }

    componentDidMount() {
        // 合并所有的异步执行,异步执行完毕后才会执行回调
        this.setState({
            count: this.state.count
        }, () => {
            console.log(this.state.count) // 返回4
        })
        this.setState((prevState,props) => ({
            count: prevState.count + 1
        }))
        this.setState((prevState, props) => ({
            count: prevState.count + 1
        }))
        this.setState((prevState, props) => ({
            count: prevState.count + 1
        }))
    }

    // 这里打印出的this.state.count值为4
    render() {
        return (
            <div>{this.state.count}</div>
        )
    }
}

export default StateDemo

 

二十二、PubSubJS:消息订阅(subscribe)、发布(publish)机制

工具库:pubsub-js

无论父子组件、兄弟组件,都可以订阅、发布数据

例:子组件要修改父组件数据

子组件:发布消息

handlerDelete = () => {
    const { comment, index } = this.props;
    if (window.confirm(`确定删除${comment.username}的评论吗?`)) {
      PubSub.publish("deleteComment", index);
    }
};

父组件:订阅消息(componentDidMount)

componentDidMount() {
    PubSub.subscribe("deleteComment", (msg, data) => {
      this.deleteComment(data);
    });
}

 

二十三、生命周期

React V16.3生命周期:

 

React v16.4之后的生命周期:

 

二十四、createPortal:将组件中定义 的内容插入其他位置

语法: createPortal(child, content)

import React, { Component } from "react";
import { createPortal } from "react-dom";
class DialogPage extends Component {
  constructor(props) {
    super(props);
    this.state = {
      showDialog: false
    };
  }
  render() {
    const { showDialog } = this.state;
    return (
      <div>
        <button onClick={() => this.setState({ showDialog: !showDialog })}>
          toggle dialog
        </button>
        {showDialog && <Dialog />}
      </div>
    );
  }
}
{
  /* 此时Dialog组件不是添加在DialogPage组件中,而是在document.body中 */
}
class Dialog extends Component {
  render() {
    return createPortal(
      <div style={{ border: "1px solid #aaa" }}>dialog</div>,
      window.document.body
    );
  }
}
export default DialogPage;

 

二十五、高阶组件

高阶组件:参数为组件,返回值为组件。

import React, { Component } from "react";
// 高阶组件:参数为组件,返回值为组件
const foo = Cmp => props => {
  return (
    <div style={{ border: "1px solid red", padding: "10px" }}>
      <Cmp {...props} />
    </div>
  );
};
function Child(props) {
  return <div>Child</div>;
}
const Foo = foo(foo(Child));
class HocPage extends Component {
  render() {
    return <Foo />;
  }
}
export default HocPage;

多层链式嵌套,使用装饰器:(不能使用装饰器时进行配置 yarn add @babel/plugin-proposal-decorators)

import React, { Component } from "react";
// 高阶组件:参数为组件,返回值为组件
const foo = Cmp => props => {
  return (
    <div style={{ border: "1px solid red", padding: "10px" }}>
      <Cmp {...props} />
    </div>
  );
};
// 装饰器只能用在类组件上
@foo
@foo
class ClassChild extends Component {
  render() {
    return <div>ClassChild</div>;
  }
}

class HocPage extends Component {
  render() {
    return <ClassChild />;
  }
}
export default HocPage;

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值