react-hooks重构pwa火车票

282 阅读3分钟

使用creat-react-app创建项目

  • which npx //查看npx是否已安装
  • npx creat-react-app myproject //创建项目

用eject结构编译脚本

  • npm run eject

react最新特性简介及context的使用

  • Context
  • ContextType
  • lazy
  • Suspense
  • memo
  1. context提供了一种数据传递方式,可以在组件树之间传递,而不必一级一级手动传递 context工作原理:有一个context实例对象,派生出ProviderConsumer两个组件。

image.png

image.png

  • 单个context的使用 app.js文件中引入context
import { Component, createContext } from 'react' 
const BatteryContext = createContext()

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

class Leaf extends Component {
  render(){
    return (<BatteryContext.Consumer>
      {
        battery => <h1>Battery: {battery}</h1>
      }
    </BatteryContext.Consumer>)
  }
}

class App extends Component {
  state = {
    battery: 60
  }
  minusBattery = () => {
    this.setState({
      battery: this.state.battery-1
    })
  }
  render(){
    const { battery } = this.state
    return (
      <BatteryContext.Provider value={battery}>
        <button type="button" onClick={this.minusBattery}>--</button>
        <Middle></Middle>
      </BatteryContext.Provider>
    );
  }
}

export default App;
  • 多个context的使用 app.js文件中引入context
import logo from './logo.svg';
import './App.css';
import { Component, createContext } from 'react'
const BatteryContext = createContext()
const OnlineContext = createContext()
class Middle extends Component {
  render() {
    return <Leaf />
  }
}

class Leaf extends Component {
  render() {
    return (<BatteryContext.Consumer>
      {
        battery => (
          <OnlineContext.Consumer>
            {
              online => <h1>battery: {battery} online: {String(online)}</h1>
            }
          </OnlineContext.Consumer>
        )
      }
    </BatteryContext.Consumer>)
  }
}

class App extends Component {
  state = {
    battery: 60,
    online: false
  }
  minusBattery = () => {
    this.setState({
      battery: this.state.battery - 1
    })
  }
  reverseState = () => {
    this.setState({
      online: !this.state.online
    })
  }
  render() {
    const { battery, online } = this.state
    return (
      <BatteryContext.Provider value={battery}>
        <OnlineContext.Provider value={online}>
          <button type="button" onClick={this.minusBattery}>press --</button>
          <button type="button" onClick={this.reverseState}>reverse state</button>
          <Middle></Middle>
        </OnlineContext.Provider>
      </BatteryContext.Provider>
    );
  }
}

export default App;

  • 假设我们想要在jsx结构之前获得battery的值呢?这时候就需要使用contextype了 这时候cousumer也可以去掉了,更方便
class Leaf extends Component {
  static contextType = BatteryContext
  render() {
    const battery = this.context
    return (
      <h1>battery: {battery}</h1>
    )
  }
}

lazy和Suspense的使用

image.png

webpack的import语法,把文件打包成一个独立的模块,只有用到的时候才去加载,那么react组件是如何加载的呢 lazy函数:封装的是组件的导入行为,而不是组件

import React, { Component, lazy, Suspense } from 'react'

const About = lazy(() => import('./about.jsx')) //lazy返回就是一个react组件
class MyLazy extends Component {
  render() {
    return (
        <div>
            <Suspense fallback={<div>loading</div>}>
                <About></About>
            </Suspense>
        </div>
    )
  }
}

export default MyLazy;

memo的使用

  1. 第一种情况:数据只有一个层级 点击按钮,组件mymemo也会跟着不断的重新render,可以通过shouldComponentUpdate 父组件app中的count变化,重新渲染子组件mymemo,这种情况可以通过shouldComponentUpdate,判断父组件传给子组件的数据有没有发生变化来确定是否重新渲染

mymemo.jsx文件:

import React, { Component } from 'react'

class MyMemo extends Component {
    shouldComponentUpdate(nextProps, nextState){
        if (nextProps.name === this.props.name) {
                return false
        }
        return true
    }
    render() {
        console.log('memo');
        return (
                <div>memo</div>
        )
    }
}

export default MyMemo;

app.jsx文件:

import './App.css';
import React, { Component } from 'react'
import MyMemo from './components/myMemo';

class App extends Component {
    state = {
       count: 0
    }
    addCount = () => {
        this.setState({
            count: this.state.count++
        })
    }
    render() {
        return (
            <div>
                <button onClick={this.addCount} >加1</button>
                <MyMemo name="should memo reRender"></MyMemo>
            </div>
        );
    }
}

export default App;


  1. 第二种情况:数据有多个层级 使用PureComponent,组件继承PureComponent,不需要使用shouldComponentUpdate, shouldComponentUpdate已经帮我们实现了shouldComponentUpdate的判断逻辑,但是也只是第一层级的数据 app.jsx文件:
import './App.css';
import React, { Component } from 'react'
import MyMemo from './components/myMemo';

class App extends Component {
	state = {
		count: 0
	}
	addCount = () => {
		this.setState({
			count: this.state.count++
		})
	}
	render() {
		return (
			<div>
				<button onClick={this.addCount} >加1</button>
				<MyMemo name="should memo reRender"></MyMemo>
			</div>
		);
	}
}

export default App;

mymemo.jsx文件:

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

class MyMemo extends  PureComponent {
	render() {
		console.log('memo');
		return (
			<div>memo</div>
		)
	}
}

export default MyMemo;

注意:以下代码展示了使用PureComponent需要注意的事项,它只关心父组件传给子组件的第一层级的数据是否发生变化,如果传多层级数据,比如对象,哪怕对象里面的某个属性发生变化,子组件也不会更新。 mymemo.jsx文件:

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

class MyMemo extends  PureComponent {
	render() {
		console.log('子组件是否重新渲染');
		return (
			<div>{this.props.person.age}</div>
		)
	}
}

export default MyMemo;

app.jsx文件:

import './App.css';
import React, { Component } from 'react'
import MyMemo from './components/myMemo';

class App extends Component {
	state = {
		count: 0,
		person: {
			age: 18
		}
	}
	render() {
		const person = this.state.person
		console.log('父组件是否重新渲染');
		return (
			<div>
				<button onClick={() => {
					person.age++
					this.setState({
						person
					})
				}} >Add</button>
				<MyMemo person={person}></MyMemo>
			</div>
		);
	}
}

export default App;

3、第三种情况:使用PureComponent,虽然没给子组件传任何数据,传了一个内联的回调函数,也会引起子组件重新渲染 mymemo.jsx文件:

import './App.css';
import React, { Component } from 'react'
import MyMemo from './components/myMemo';

class App extends Component {
	state = {
		count: 0,
		person: {
			age: 18
		}
	}
	render() {
		const person = this.state.person
		console.log('父组件是否重新渲染');
		return (
			<div>
				<button onClick={() => {
					person.age++
					this.setState({
						count: this.state.count+1
					})
				}} >Add</button>
				<MyMemo person={person} cb={() => {}}></MyMemo>
			</div>
		);
	}
}

export default App;

app.jsx文件:

import './App.css';
import React, { Component } from 'react'
import MyMemo from './components/myMemo';

class App extends Component {
	state = {
		count: 0,
		person: {
			age: 18
		}
	}
	render() {
		const person = this.state.person
		console.log('父组件是否重新渲染');
		return (
			<div>
				<button onClick={() => {
					person.age++
					this.setState({
						count: this.state.count+1
					})
				}} >Add</button>
				<MyMemo person={person} cb={() => {}}></MyMemo>
			</div>
		);
	}
}

export default App;

优化上述代码,使得传入内联函数,却不会引起子组件重新渲染。把cb改写成箭头函数。mymemo.jsx文件的代码跟上面一样 app.jsx文件:

class App extends Component {
	state = {
		count: 0,
		person: {
			age: 18
		}
	}
	callback = () => {
		
	}
	render() {
		const person = this.state.person
		console.log('父组件是否重新渲染');
		return (
			<div>
				<button onClick={() => {
					person.age++
					this.setState({
						count: this.state.count+1
					})
				}} >Add</button>
				<MyMemo person={person} cb={this.callback}></MyMemo>
			</div>
		);
	}
}

export default App;