React組件的二三事

一、起步

創(chuàng)建一個React項(xiàng)目

1.創(chuàng)建項(xiàng)目:npx create-react-app my-app

2.打開項(xiàng)目:cd? my-app

3.啟動項(xiàng)目:npm? start?

4.暴露配置項(xiàng):npm? run? eject

二、組件

組件,從概念上類似于 JavaScript 函數(shù)。它接受任意的入?yún)?即 “props”),并返回?于描述?面展示內(nèi)容的 React 元素。

組件有兩種形式:class組件和function組件。

class組件

class組件通常擁有狀態(tài)生命周期繼承于Component,實(shí)現(xiàn)render?法。

用class組件創(chuàng)建一個 Clock:

import React, { Component } from "react"

export default class ClassComponent extends Component {

? ? constructor(props) {

? ? ? ? super(props);

? ? ? ? // 存儲狀態(tài),在構(gòu)造函數(shù)中初始化狀態(tài)

? ? ? ? this.state = {

? ? ? ? ? ? date: new Date()

? ? ? ? };

? ? }

? ? // 組件掛載完成后執(zhí)行:啟動定時器每秒更新狀態(tài)

? ? componentDidMount() {

? ? ? ? this.timer = setInterval(() => {

? ? ? ? ? ? // 更新state,不能用this.state

? ? ? ? ? ? this.setState({

? ? ? ? ? ? ? ? date: new Date()

? ? ? ? ? ? })

? ? ? ? }, 1000);

? ? }

? ? // 組件卸載之前執(zhí)行:停止定時器

? ? componentWillUnmount() {

? ? ? ? clearInterval(this.timer);

? ? }

? ? componentDidUpdate() { // 每次有值更新都會執(zhí)行

? ? ? ? console.log("componentDidUpdate")

? ? }

? ? render() {

? ? ? ? const { date } = this.state;

? ? ? ? return (

? ? ? ? ? ? <div>

? ? ? ? ? ? ? ? <h3>ClassComponent</h3>

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

? ? ? ? ? ? </div>

? ? ? ? )

? ? }

}

function組件

函數(shù)組件通常無狀態(tài),僅關(guān)注內(nèi)容展示,返回渲染結(jié)果即可。

從React16.8開始引入了hooks,函數(shù)組件也能夠擁有狀態(tài)

用function組件創(chuàng)建一個 Clock:?

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

export function FunctionComponent(props) {

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

? ? useEffect(() => { // 副作用

? ? ? ? // 相當(dāng)于componentDidMount、componentDidUpdate、componentWillUnmount的集合

? ? ? ? const timer = setInterval(() => {

? ? ? ? ? ? setDate(new Date())

? ? ? ? }, 1000)

? ? ? ? return () => clearInterval(timer) // 組件卸載的時候執(zhí)行

? ? },[])

? ? return (

? ? ? ? <div>

? ? ? ? ? ? <h3>FunctionComponent</h3>

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

? ? ? ? </div>

? ? )

}

三、正確使用setState

setState(partialState, callback)

1. partialState:object|function

?于產(chǎn)生與當(dāng)前state合并的子集。

2. callback:function

state更新之后被調(diào)?。?

關(guān)于 setState() 你應(yīng)該了解三件事:

1.不要直接修改 State

例如,此代碼不會重新渲染組件:

// 錯誤示范

this.state.comment = 'Hello';

而是應(yīng)該使用 setState() :

// 正確使?

this.setState({

? comment: 'Hello'

});

2.State 的更新可能是異步的

出于性能考慮,React 可能會把多個 setState() 調(diào)?合并成?個調(diào)用。

觀察以下例子中l(wèi)og的值和button顯示的counter。

setState在合成事件和生命周期中是異步的:

import React, { Component } from "react"

export default class SetStatePage extends Component {

? ? constructor(props) {

? ? ? ? super(props);

? ? ? ? this.state = {

? ? ? ? ? ? counter: 0

? ? ? ? }

? ? }

? ? componentDidMount() {

? ? ? ? this.changeValue(1);

? ? }

? ? changeValue = (v) => {

? ? ? ? // setState在合成事件和生命周期中是異步的,這里說的異步其實(shí)是批量更新,達(dá)到優(yōu)化性能的目的

? ? ? ? this.setState({

? ? ? ? ? ? counter: this.state.counter + v

? ? ? ? })

? ? ? ? console.log("counter", this.state.counter);

? ? }

? ? setCounter = () => {

? ? ? ? this.changeValue(1);

? ? }

? ? render() {

? ? ? ? const { counter } = this.state;

? ? ? ? return (

? ? ? ? ? ? <div>

? ? ? ? ? ? ? ? <h3>SetStatePage</h3>

? ? ? ? ? ? ? ? <button onClick={this.setCounter}>{counter}</button>

? ? ? ? ? ? </div>

? ? ? ? )

? ? }

}

使用定時器:setState在setTimeout中是同步的:

import React, { Component } from "react"

export default class SetStatePage extends Component {

? ? constructor(props) {

? ? ? ? super(props);

? ? ? ? this.state = {

? ? ? ? ? ? counter: 0

? ? ? ? }

? ? }

? ? changeValue = (v) => {

? ? ? ? this.setState({

? ? ? ? ? ? counter: this.state.counter + v

? ? ? ? })

? ? ? ? console.log("counter", this.state.counter);

? ? }

? ? setCounter = () => {

? ? ? ? // setState在setTimeout中是同步的

? ? ? ? setTimeout(() => {

? ? ? ? ? ? this.changeValue(1);

? ? ? ? }, 0);

? ? }

? ? render() {

? ? ? ? const { counter } = this.state;

? ? ? ? return (

? ? ? ? ? ? <div>

? ? ? ? ? ? ? ? <h3>SetStatePage</h3>

? ? ? ? ? ? ? ? <button onClick={this.setCounter}>{counter}</button>

? ? ? ? ? ? </div>

? ? ? ? )

? ? }

}

原生事件中修改狀態(tài):setState在原生事件中是同步的:

import React, { Component } from "react"

export default class SetStatePage extends Component {

? ? constructor(props) {

? ? ? ? super(props);

? ? ? ? this.state = {

? ? ? ? ? ? counter: 0

? ? ? ? }

? ? }

? ? componentDidMount() {

? ? ? ? document.getElementById('test').addEventListener('click', this.changeValue);

? ? }

? ? changeValue = () => {

? ? ? ? // setState在原生事件中是同步的

? ? ? ? this.setState({

? ? ? ? ? ? counter: this.state.counter + 1

? ? ? ? })

? ? ? ? console.log("counter", this.state.counter);

? ? }

? ? render() {

? ? ? ? const { counter } = this.state;

? ? ? ? return (

? ? ? ? ? ? <div>

? ? ? ? ? ? ? ? <h3>SetStatePage</h3>

? ? ? ? ? ? ? ? <button id="test">原生事件*{counter}</button>

? ? ? ? ? ? </div>

? ? ? ? )

? ? }

}

在回調(diào)中獲取狀態(tài)值:setState在回調(diào)中是同步的:

import React, { Component } from "react"

export default class SetStatePage extends Component {

? ? constructor(props) {

? ? ? ? super(props);

? ? ? ? this.state = {

? ? ? ? ? ? counter: 0

? ? ? ? }

? ? }

? ? changeValue = (v) => {

? ? ? ? this.setState({

? ? ? ? ? ? counter: this.state.counter + v

? ? ? ? },() => {

? ? ? ? ? ? // callback就是在state更新完成之后執(zhí)行

? ? ? ? ? ? console.log("counter", this.state.counter);

? ? ? ? })

? ? }

? ? setCounter = () => {

? ? ? ? this.changeValue(1)

? ? }

? ? render() {

? ? ? ? const { counter } = this.state;

? ? ? ? return (

? ? ? ? ? ? <div>

? ? ? ? ? ? ? ? <h3>SetStatePage</h3>

? ? ? ? ? ? ? ? <button onClick={this.setCounter}>{counter}</button>

? ? ? ? ? ? </div>

? ? ? ? )

? ? }

}

總結(jié): setState只有在合成事件和生命周期函數(shù)中是異步的,在原?事件和setTimeout中都是同步的,這?的異步其實(shí)是批量更新。

3.State 的更新會被合并

import React, { Component } from "react"

export default class SetStatePage extends Component {

? ? constructor(props) {

? ? ? ? super(props);

? ? ? ? this.state = {

? ? ? ? ? ? counter: 0

? ? ? ? }

? ? }

? ? changeValue = (v) => {

? ? ? this.setState({

? ? ? ? ? ? counter: this.state.counter + v

? ? ? ? },() => {

? ? ? ? ? ? console.log("counter", this.state.counter);

? ? ? ? })

? ? }

? ? setCounter = () => {

? ? ? ? this.changeValue(1); // 不會執(zhí)行

? ? ? ? this.changeValue(2); // 不會執(zhí)行

? ? ? ? this.changeValue(3); // 會執(zhí)行

? ? }

? ? render() {

? ? ? ? const { counter } = this.state;

? ? ? ? return (

? ? ? ? ? ? <div>

? ? ? ? ? ? ? ? <h3>SetStatePage</h3>

? ? ? ? ? ? ? ? <button onClick={this.setCounter}>{counter}</button>

? ? ? ? ? ? </div>

? ? ? ? )

? ? }

}

如果想要鏈?zhǔn)礁聅tate:

import React, { Component } from "react"

export default class SetStatePage extends Component {

? ? constructor(props) {

? ? ? ? super(props);

? ? ? ? this.state = {

? ? ? ? ? ? counter: 0

? ? ? ? }

? ? }

? ? changeValue = (v) => {

? ? ? ? // setState函數(shù)

? ? ? ? this.setState(state => {

? ? ? ? ? ? return {

? ? ? ? ? ? ? ? counter: state.counter + v

? ? ? ? ? ? }

? ? ? ? })

? ? }

? ? setCounter = () => {

? ? ? ? this.changeValue(1); // 會執(zhí)行

? ? ? ? this.changeValue(2); // 會執(zhí)行

? ? ? ? this.changeValue(3); // 會執(zhí)行

? ? }

? ? render() {

? ? ? ? const { counter } = this.state;

? ? ? ? return (

? ? ? ? ? ? <div>

? ? ? ? ? ? ? ? <h3>SetStatePage</h3>

? ? ? ? ? ? ? ? <button onClick={this.setCounter}>{counter}</button>

? ? ? ? ? ? </div>

? ? ? ? )

? ? }

}

四、組件復(fù)合

復(fù)合組件給與你足夠的敏捷去定義自定義組件的外觀和行為,這種方式更明確和安全。如果組件間有公?用的?UI邏輯,將它們抽取為JS模塊導(dǎo)入使?而不是繼承它。

不具名:

HomePage.js文件內(nèi)容如下:

import React, { Component } from "react";

import Layout from './Layout'

export default class HomePage extends Component {

? ? render() {

? ? ? ? return (

? ? ? ? ? ? <Layout showTopBar={false} showBottomBar={true} title="首頁">

? ? ? ? ? ? ? ? <div>

? ? ? ? ? ? ? ? ? ? <h3>HomePage</h3>

? ? ? ? ? ? ? ? </div>

? ? ? ? ? ? </Layout>

? ? ? ? )

? ? }

}

Layout.js文件內(nèi)容如下:

import React, { Component } from "react";

import TopBar from '../components/TopBar'

import BottomBar from '../components/BottomBar'

export default class Layout extends Component {

? ? componentDidMount() {

? ? ? ? const { title = "商城" } = this.props;

? ? ? ? document.title = title;

? ? }

? ? render() {

? ? ? ? const { children, showTopBar, showBottomBar } = this.props;

? ? ? ? console.log('children', children)

? ? ? ? return (

? ? ? ? ? ? <div>

? ? ? ? ? ? ? ? {showTopBar && <TopBar />}

? ? ? ? ? ? ? ? {children}

? ? ? ? ? ? ? ? <h3>Layout</h3>

? ? ? ? ? ? ? ? {showBottomBar && <BottomBar />}

? ? ? ? ? ? </div>

? ? ? ? )

? ? }

}

TopBar.js文件內(nèi)容如下:

import React, { Component } from "react";

export default class TopBar extends Component {

? ? render() {

? ? ? ? return (

? ? ? ? ? ? <div>

? ? ? ? ? ? ? ? <h3>TopBar</h3>

? ? ? ? ? ? </div>

? ? ? ? )

? ? }

}

BottomBar.js文件內(nèi)容如下:

import React, { Component } from "react";

export default class BottomBar extends Component {

? ? render() {

? ? ? ? return (

? ? ? ? ? ? <div>

? ? ? ? ? ? ? ? <h3>BottomBar</h3>

? ? ? ? ? ? </div>

? ? ? ? )

? ? }

}

具名:傳個對象進(jìn)去

userPage.js文件內(nèi)容如下:

import React, { Component } from "react";

import Layout from './Layout'

export default class UserPage extends Component {

? ? render() {

? ? ? ? return (

? ? ? ? ? ? <Layout showTopBar={true} showBottomBar={true} title="用戶中心">

? ? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? ? ? {

? ? ? ? ? ? ? ? ? ? ? ? content: (

? ? ? ? ? ? ? ? ? ? ? ? ? ? <div>

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? <h3>UserPage</h3>

? ? ? ? ? ? ? ? ? ? ? ? ? ? </div>

? ? ? ? ? ? ? ? ? ? ? ? ),

? ? ? ? ? ? ? ? ? ? ? ? text: "這是一個文本",

? ? ? ? ? ? ? ? ? ? ? ? btnClick: () => {

? ? ? ? ? ? ? ? ? ? ? ? ? ? console.log("btnClick")

? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? </Layout>

? ? ? ? )

? ? }

}

Layout.js文件內(nèi)容如下:

import React, { Component } from "react";

import TopBar from '../components/TopBar'

import BottomBar from '../components/BottomBar'

export default class Layout extends Component {

? ? componentDidMount() {

? ? ? ? const { title = "商城" } = this.props;

? ? ? ? document.title = title;

? ? }

? ? render() {

? ? ? ? const { children, showTopBar, showBottomBar } = this.props;

? ? ? ? console.log('children', children)

? ? ? ? return (

? ? ? ? ? ? <div>

? ? ? ? ? ? ? ? {showTopBar && <TopBar />}

? ? ? ? ? ? ? ? {children.content}

? ? ? ? ? ? ? ? {children.text}

? ? ? ? ? ? ? ? <button onClick={children.btnClick}>button</button>

? ? ? ? ? ? ? ? <h3>Layout</h3>

? ? ? ? ? ? ? ? {showBottomBar && <BottomBar />}

? ? ? ? ? ? </div>

? ? ? ? )

? ? }

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容