一、起步
創(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>
? ? ? ? )
? ? }
}