一、React基础

react

起步

1、安装官方脚手架: npm install -g create-react-app

2、创建项目:create-react-app react-project-name

3、启动项目:npm start

4、暴露配置项:npm run eject

入口文件:index.js

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

babel-loader 最终会把jsx转换成js能识别的内容

JSX:是一种JavaScript的语法扩展,其格式比较像模板语言,但事实上完全是在JavaScript内部实现的

JSX实质就是 React.createElement 的调用,最终的结果是React元素(JavaScript对象)

const jsx = <div>hello react</div>

ReactDOM.render(jsx, document.getElementById('root'));

组件

组件是抽象的独立功能模块,react应用程序由组件构建而成

组件的两种表现形式:class组件  function组件

class组件通常拥有状态(state)和生命周期(lifeCycle),继承于Component,实现render方法

import React, { Component } from 'react'

export default class Home extends Component {

render() {

return (

<div>

我是home页面

</div>

)

}

}

function组件

函数组件通常无状态,仅关注内容展示,返回渲染结果即可。

---从React16.8开始引入了hooks,函数组件也能够拥有状态。

写一个定时器显示当前组件

import React, { Component } from 'react'

export default class Home extends Component {

constructor(props){

super(props)

this.state = {

date: new Date()

}

}

// 组件挂载后---生命周期函数

componentDidMount(){

this.timerId = setInterval(()=>{

this.setState({

date: new Date()

})

}, 1000)

}

// 组件卸载之前

componentWillUnmount(){

clearInterval(this.timerId)

}

render() {

const str = '我是home页面'

const {date} = this.state

return (

<div>

<h1>{str}</h1>

<p>{date.toLocaleTimeString()}</p>

</div>

)

}

}

setState方法

setCounter(){  // 这样写找不到this

this.setState({

counter: this.state.counter + 1

})

}

// 解决方法一

constructor(props){

super(props)

this.state = {

date: new Date(),

counter: 0

}

this.setCounter = this.setCounter.bind(this)

}

setCounter(){

this.setState({

counter: this.state.counter + 1

})

}

// 解决方法二

setCounter = () => {

this.setState({

counter: this.state.counter + 1

})

}

setState为异步,如何将其实现同步

setCounter = () => {

this.setState({

counter: this.state.counter + 1

})

console.log(this.state.counter) // 这里打印0,页面显示1

}

// 打印0时,页面显示2,并且每次只加2

setCounter = () => {

this.setState({

counter: this.state.counter + 1

})

this.setState({

counter: this.state.counter + 2

})

console.log(this.state.counter)

}

// 实现同步

// 方式一

setCounter = () => {

this.setState(nextState => {

return {

counter: nextState.counter + 1

}

})

this.setState(nextState => {

return {

counter: nextState.counter + 2

}

})

}

// 方式二

setCounter = () => {

setTimeout(() => {

this.setState({

counter: this.state.counter + 1

})

this.setState({

counter: this.state.counter + 2

})

console.log(this.state.counter)

}, 0)

}

// 方式三:原生事件上去绑定

componentDidMount(){

document.getElementsByTagName('button')[0].addEventListener('click', ()=>{

this.setState({

counter: this.state.counter + 1

})

this.setState({

counter: this.state.counter + 2

})

})

}

 总结:setState只有在合成事件和钩子函数中是异步的,在原生事件和setTimeout、setInterval中都是同步的

function组件

function组件是没有状态和生命周期的

如果一定要使用state,可以进行引入 useState、useEffect

下列代码将上面的class组件转成funciton组件

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

export default function User() {

const [date, setDate] = useState(new Date())

useEffect(() => {

const timerId = setInterval(() => {

setDate(new Date())

}, 1000)

return () => clearInterval(timerId)

})

return (

<div>

<h1>我是user页面</h1>

<p>{date.toLocaleTimeString()}</p>

</div>

)

}

事件处理

onclick事件和onchange事件

import React, { Component } from 'react'

export default class Search extends Component {

constructor(props){

super(props)

this.state = {

name: '12'

}

}

handler = () => {

console.log('handler');

}

change = (event) => {

let value = event.target.value

this.setState({

name: value

})

console.log('change', this.state.name);

}

render() {

const {name} = this.state

console.log(this);return (

<div>

<h1>我是Search页面</h1>

<button onClick={this.handler}>click</button>

<input onChange={this.change} value={name}/>

</div>

)

}

}

事件回调函数注意绑定this指向,常见三种方法:

1、构造函数中绑定并覆盖:this.change = this.change.bind(this)

2、方法定义为箭头函数:change = ()=>{}

3、事件中定义为箭头函数:onChange = { () => this.change() } 

--- react里遵循单项数据流,没有双向绑定,输入框要设置value和onChange,成为受控组件。

组件通信

props属性传递

// App.js中

const store = {

userInfo: {

userName: 'Dylan'

}

}

function tellme(msg){

console.log('tellme:', msg);

}

function App() {

return (

<div className="App">

<Search store={store} tellme={tellme} />

</div>

);

}

export default App;

// Search.js中

handler = () => {

console.log('handler');

const {tellme} = this.props

tellme('im dylanLv')

}

render() {

const {name} = this.state

console.log(this);

const {userInfo} = this.props.store

return (

<div>

<h1>我是Search页面,{userInfo.userName}</h1>

<button onClick={this.handler}>click</button>

<input onChange={this.change} value={name}/>

</div>

)

}

如果父组件传递的是函数,则可以把子组件信息传入父组件,这个常称为状态提示 

react16的生命周期

import React, {Component} from 'react'

export default class LifeCycle extends Component {

constructor(props) {

super(props)

this.state = {

counter: 0

}

console.log('constructor', this.state.counter);

}

// 挂载之前

UNSAFE_componentWillMount() {

console.log('componentWillMount', this.state.counter);

}

// 挂载之后

componentDidMount() {

console.log('componentDidMount', this.state.counter);

}

// 更新之前

UNSAFE_componentWillUpdate() {

console.log('componentWillUpdate', this.state.counter);

}

// 更新之后

componentDidUpdate() {

console.log('componentDidUpdate', this.state.counter);

}

// 卸载之前

componentWillUnmount() {

console.log('componentWillUnmount', this.state.counter);

}

// 是否更新--执行render--优化使用

shouldComponentUpdate(nextProps, nextState) {

console.log('shouldComponentUpdate', this.state.counter, nextState.counter, nextProps);

return this.state.counter != 5

}

setCounter = () => {

this.setState({

counter: this.state.counter + 1

})

}

render() {

console.log('render');

const {counter} = this.state

return (

<div>

<h1>我是一个lifeCycle, Hylan</h1>

<p>{counter}</p>

<button onClick={this.setCounter}>click</button>

{!!(counter % 2) && <Foo/>}

</div>

)

}

}

class Foo extends Component {

componentWillUnmount() {

console.log('componentWillUnmount foo');

}

render() {

return <div>我是FOO组件</div>

}

}

V17可能会废弃的三个生命周期函数用 getDerivedStateFromProps 替代,目前使用的话加上 UNSAFE_

  • componentWillMount
  • componentWillReceiveProps
  • conponentWillUpdate

引入两个新的生命周期

  • static getDerivedStateFromProps
  • getSnapshotBeforeUpdate

static getDerivedStateFromProps(props, state){

// getDerivedStateFromProps 会在调用render方法之前调用

// 并且在初始挂载以及后续更新时都会被调用

// 它应返回一个对象来更新 state ,如果返回 null 则不更新任何内容

const { counter } = state

console.log('getDerivedStateFromProps', counter)

return counter < 8 ? null : { counter: 0 }

}

// 上一次(更新前)的影像

// 返回值会作为 componentDidUpdate 的第三个参数

getSnapshotBeforeUpdate(prevProps, prevState){

const { counter } = prevState

console.log('getSnapshotBeforeUpdate', counter)

return null

}

// 更新之后

componentDidUpdate(prevProps, prevState, snapshot) {

console.log('componentDidUpdate', this.state.counter);

}

使用Context 上下文传值---可以跨层级传值

// App.js

import React from 'react';

import './App.css';

import Home from './pages/Home';

const Context = React.createContext()

const Provider = Context.Provider

const Consumer = Context.Consumer

const store = {

home: {},

user: {

name: 'Dylan'

}

}

function App() {

return (

<div className="App">

<Provider value={store}>

<Consumer>

{ctx => <Home {...ctx}/>}

</Consumer>

</Provider>

</div>

);

}

export default App;

// Home.js

import React, { Component } from 'react'

export default class Home extends Component {

render() {

// home:{

// home: {}

// user: {name: "Dylan"}

// }

console.log("home", this.props);

return (

<div>

home

</div>

)

}

}

一般使用时会将Context暴露在外部,使用者将其引用就行了

 AppContext.js 文件

import React from 'react';

const Context = React.createContext()

export const Provider = Context.Provider

export const Consumer = Context.Consumer

App.js 文件

import React from 'react';

import './App.css';

import Home from './pages/Home';

import {Provider, Consumer} from './AppContext'

const store = {

home: {},

user: {

name: 'Dylan'

}

}

function App() {

return (

<div className="App">

<Provider value={store}>

<Consumer>

{ctx => <Home {...ctx}/>}

</Consumer>

</Provider>

</div>

);

}

export default App;

跨层级传值

App.js 文件

const store = {

home: {},

user: {

name: 'Dylan'

}

}

function App() {

return (

<div className="App">

<Provider value={store}>

<Home />

</Provider>

</div>

);

}

Home.js文件

import React, { Component } from 'react'

import {Consumer} from '../AppContext'

export default class Home extends Component {

render() {

console.log("home", this.props);

return <Consumer>

{ctx => <HomeHandle {...ctx}/>}

</Consumer>

}

}

function HomeHandle(props){

console.log('HomeHandle', props);

return <div>

<h1>HomeHandle</h1>

</div>

}

组件复合---Composition

TabBar.js 文件

import React, {Component} from 'react'

export default class TabBar extends Component {

render() {

return <div className="tabBar">TabBar</div>

}

}

Layout.js 文件

import React, {Component} from 'react'

import TabBar from '../components/TabBar'

export default class Layout extends Component {

render() {

const {children, showTabBar = true} = this.props

return (

<div>

{children}

{showTabBar && <TabBar />}

</div>

)

}

}

Home.js  文件 ,使用Layout模板

import React, {Component} from 'react'

import {Consumer} from '../AppContext'

import Layout from './Layout';

export default class Home extends Component {

render() {

return <Consumer>{ctx => <HomeHandle {...ctx}/>}</Consumer>

}

}

function HomeHandle(props) {

console.log('HomeHandle', props);

return <Layout showTabBar={true}>

<div>

<h1>Home</h1>

</div>

</Layout>

}

上述类似  vue  中的匿名插槽

下面代码展示具名插槽

Home.js 文件

import React, {Component} from 'react'

import {Consumer} from '../AppContext'

import Layout from './Layout';

export default class Home extends Component {

render() {

return <Consumer>{ctx => <HomeHandle {...ctx}/>}</Consumer>

}

}

function HomeHandle(props) {

console.log('HomeHandle', props);

return <Layout>

{{

btn: <button>按钮</button>,

content: "我是内容"

}}

</Layout>

}

Layout.js  文件

import React, {Component} from 'react'

import TabBar from '../components/TabBar'

export default class Layout extends Component {

componentDidMount(){

const { title='商场' } = this.props

document.title = title

}

render() {

const {children, showTabBar} = this.props

console.log('props:',this.props);

return (

<div>

{children.btn}

{children.content}

{showTabBar && <TabBar />}

</div>

)

}

}

如何让layout自行判断传进来的组件是具名还是匿名

Layout.js 文件

render() {

const {children, showTabBar = true} = this.props

const a = []

// 如果有这个值($$typeop) , 说明是匿名的

if(children.$$typeof){

a.push(children)

}else{

for(let item in children){

a.push(children[item])

}

}

return (

<div>

{

a.map((item, index) => {

return <div key={`child${index}`}>{item}</div>

})

}

{showTabBar && <TabBar />}

</div>

)

高阶组件

将Home.js 进行修改

import React, {Component} from 'react'

import {Consumer} from '../AppContext'

import Layout from './Layout';

const consumerHandler = Cmp => props => {

return <Consumer>

{ctx => <Cmp {...props} {...ctx}/>}

</Consumer>

}

function HomeHandle(props) {

console.log('HomeHandle', props);

return <Layout>

<div>

<h1>home</h1>

</div>

</Layout>

}

export default consumerHandler(HomeHandle)

可以将其放入 AppContext.js 文件(公共文件)

import React from 'react';

const Context = React.createContext()

export const Provider = Context.Provider

export const Consumer = Context.Consumer

export const consumerHandler = Cmp => props => {

return <Consumer>

{ctx => <Cmp {...props} {...ctx}/>}

</Consumer>

}

高阶组件---HOC

为了提高组件复用性,可测试性,就要保证组件功能单一性;但是若要满足复杂需求就要扩展功能单一的组件,在React里就有了HOC(Higher-Order Components)的概念。

定义:高阶组件就是一个工厂函数,它接收一个组件并返回另一个组件。

改写 App.js,展示高阶组件的用法

function Child(props){

return <div>child</div>

}

const foo = Cmp => props => {

return <div style={{border: '1px solid black'}}>

<Cmp {...props}/>

</div>

}

function App() {

const Foo = foo(Child)

return (

<div className="App">

<Child />

<Foo />

{/* <Home /> */}

{/* <User /> */}

</div>

);

}

export default App;

高阶组件实际上就是,你给我一个组件,我把这个组件封装一下,再返回给你一个封装后的组件

高阶组件可以链式调用

function Child(props){

return <div>child</div>

}

const foo = Cmp => props => {

return <div style={{border: '1px solid black'}}>

<Cmp {...props}/>

</div>

}

const foo2 = Cmp => props => {

return <div style={{border: '1px solid red', padding: '10px'}}>

<Cmp {...props}/>

</div>

}

function App() {

const Foo = foo2(foo(Child))

return (

<div className="App">

<Child />

<Foo />

</div>

);

}

export default App;

装饰器写法

高阶组件本身是对装饰器模式的应用,自然可以利用ES7中出现的装饰器语法来更优雅的书写代码。

CRA项目中默认不支持js代码使用装饰器语法,可修改后缀名为 tsx 则可以直接支持

Hooks

Hook是React16.8的一个新增项,它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

Hooks的特点:

  • 是你在无需修改组件结构的情况下复用状态逻辑
  • 可将组件中相互关联的部分拆分成更小的函数,复杂组件将变得更容易理解
  • 更简洁、更易理解的代码

hook中使用子组件,代码如下:

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

export default function HooksPage() {

const [date, setDate] = useState(new Date())

const [fruits, setFruits] = useState(['apple', 'banana'])

useEffect(() => {

const timerId = setInterval(() => {

setDate(new Date())

}, 1000)

return () => clearInterval(timerId)

})

return (

<div>

hooks

<p>{date.toLocaleTimeString()}</p>

{/* 使用子组件时,需要把 fruits 和 setFruits 传入后,子组件内方可使用 */}

<FruitList fruits={fruits} setFruits={setFruits}/>

</div>

)

}

function FruitList({fruits, setFruits}) {

// 点击后删除当前项

const delCur = index => {

const tem = [...fruits]

tem.splice(index, 1)

setFruits(tem)

}

return <div>

<ul>

{

fruits.map((item, index) => {

return <li key={`fruit${index}`} onClick={()=>delCur(index)}>{item}</li>

})

}

</ul>

</div>

}

添加一个新增 fruits 操作

export default function HooksPage() {

const [date, setDate] = useState(new Date())

const [fruits, setFruits] = useState(['apple', 'banana'])

useEffect(() => {

const timerId = setInterval(() => {

setDate(new Date())

}, 1000)

return () => clearInterval(timerId)

})

return (

<div>

hooks

<p>{date.toLocaleTimeString()}</p>

{/* 传入addFruit函数 */}

<AddFruit addFruit={item=>setFruits([...fruits, item])} />

{/* 使用子组件时,需要把 fruits 和 setFruits 传入后,子组件内方可使用 */}

<FruitList fruits={fruits} setFruits={setFruits}/>

</div>

)

}

function AddFruit({addFruit}) {

const [name, setName] = useState("")

return <div>

<input value={name} onChange={e => setName(e.target.value)} />

{/* 接收addFruit函数,点击按钮进行触发 */}

<button onClick={()=>addFruit(name)}>点击增加</button>

</div>

}

如果在 useEffect 中加入  console.log('useEffect'),可以发现不管是 setDate执行,还是点击新增按钮后的 setFruits 执行,console.log都会执行。

但实际上当前功能只需要 date的定时器不断执行而已,可以修改代码进行优化

useEffect(() => {

console.log('useEffect');

const timerId = setInterval(() => {

setDate(new Date())

}, 1000)

return () => clearInterval(timerId)

// useEffect的第二个参数,加入后就只对 date 生效了

}, [date])

接下来 点击 新增按钮 ,就不会打印 useEffect 了

副作用钩子

useEffect给函数组件增加了执行副作用操作的能力。

副作用(Side Effect)是指一个function 做了和本身运算返回值无关的事,比如:修改了全局变量、修改了传入的参数、甚至是 console.log ,所以ajax操作,修改dom都算做副作用。

  • 异步数据获取,更新HooksTest.js

useEffect(() => {

setTimeout(()=>{

setFruits(['香蕉','西瓜'])

}, 1000)

})

测试就会发现副作用操作会被频繁调用

  • 设置依赖

// 设置空数组意为没有依赖,则副作用操作仅执行一次

useEffect(()=>{...}, [])

  • 清除工作:有一些副作用是需要清除的,清除工作非常重要,可以防止引起内存泄漏

useEffect(() => {

const timerId = setInterval(() => {

console.log('mgs');

}, 1000)

return () => clearInterval(timerId)

}, [])

组件卸载后回执行返回的清理函数

useReducer

useReducer是useState的可选项,常用于组件有复杂状态逻辑时

将上面的代码改成使用 useReducer

Fruit.js 文件

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

export function AddFruit({addFruit}) {

const [name,

setName] = useState("")

return <div>

<input value={name} onChange={e => setName(e.target.value)}/>

<button onClick={() => addFruit(name)}>点击增加</button>

</div>

}

// FruitList 组件需要传入 fruits 和 setFruits

export function FruitList({fruits, setFruits}) {

// 点击后删除当前项

const delCur = index => {

const tem = [...fruits]

tem.splice(index, 1)

setFruits(tem)

}

return <div>

<ul>

{

fruits.map((item, index) => {

return <li key={`fruit${index}`} onClick={() => delCur(index)}>{item}</li>

})

}

</ul>

</div>

}

HookReducer.js 文件

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

import { FruitList, AddFruit } from '../components/Fruit'

function fruitsReducer(state, action){

switch(action.type){

case 'init':

case 'replace':

return action.paylod

default:

return state

}

}

export default function HooksReducer() {

const [fruits, dispatch] = useReducer(fruitsReducer, [])

useEffect(()=>{

setTimeout(()=>{

dispatch({type: 'init', paylod: ['apple', 'banana']})

}, 1000)

return ()=>{

// clearup

}

}, [])

return (

<div>

HooksReducer

<FruitList fruits={fruits} setFruits={(newFruitList)=>dispatch({type: 'replace', paylod: newFruitList})}/>

</div>

)

}

useContext

useContext 用于 快速在函数组件中导入上下文。---之前是用Consumer做的

import React, { useContext } from 'react'

const Context = React.createContext()

const Provider = Context.Provider

export default function HooksContext() {

const store = {

user: {

name: 'Dylan'

}

}

return (

<div>

<Provider value={store}>

<ContextChild></ContextChild>

</Provider>

</div>

)

}

function ContextChild(props){

console.log(useContext(Context));

const {user} = useContext(Context)

return <div>

{user.name}

</div>

}

 

 

以上是 一、React基础 的全部内容, 来源链接: utcz.com/z/381281.html

回到顶部