一、起步
创建一个React项目
1.创建项目:npx create-react-app my-app
2.打开项目:cd my-app
3.启动项目:npm start
4.暴露配置项:npm run eject
二、组件
组件,从概念上类似于 JavaScript 函数。它接受任意的入参(即 “props”),并返回⽤于描述⻚面展示内容的 React 元素。
组件有两种形式:class组件和function组件。
class组件
class组件通常拥有状态和生命周期,继承于Component,实现render⽅法。
用class组件创建一个 Clock:
import React, { Component } from "react"
export default class ClassComponent extends Component {
constructor(props) {
super(props);
// 存储状态,在构造函数中初始化状态
this.state = {
date: new Date()
};
}
// 组件挂载完成后执行:启动定时器每秒更新状态
componentDidMount() {
this.timer = setInterval(() => {
// 更新state,不能用this.state
this.setState({
date: new Date()
})
}, 1000);
}
// 组件卸载之前执行:停止定时器
componentWillUnmount() {
clearInterval(this.timer);
}
componentDidUpdate() { // 每次有值更新都会执行
console.log("componentDidUpdate")
}
render() {
const { date } = this.state;
return (
<div>
<h3>ClassComponent</h3>
<p>{date.toLocaleTimeString()}</p>
</div>
)
}
}
function组件
函数组件通常无状态,仅关注内容展示,返回渲染结果即可。
从React16.8开始引入了hooks,函数组件也能够拥有状态
用function组件创建一个 Clock:
import React, { useState, useEffect} from "react";
export function FunctionComponent(props) {
const [date, setDate] = useState(new Date())
useEffect(() => { // 副作用
// 相当于componentDidMount、componentDidUpdate、componentWillUnmount的集合
const timer = setInterval(() => {
setDate(new Date())
}, 1000)
return () => clearInterval(timer) // 组件卸载的时候执行
},[])
return (
<div>
<h3>FunctionComponent</h3>
<p>{date.toLocaleTimeString()}</p>
</div>
)
}
三、正确使用setState
setState(partialState, callback)
1. partialState:object|function
⽤于产生与当前state合并的子集。
2. callback:function
state更新之后被调⽤。
关于 setState() 你应该了解三件事:
1.不要直接修改 State
例如,此代码不会重新渲染组件:
// 错误示范
this.state.comment = 'Hello';
而是应该使用 setState() :
// 正确使⽤
this.setState({
comment: 'Hello'
});
2.State 的更新可能是异步的
出于性能考虑,React 可能会把多个 setState() 调⽤合并成⼀个调用。
观察以下例子中log的值和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在合成事件和生命周期中是异步的,这里说的异步其实是批量更新,达到优化性能的目的
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>
)
}
}
原生事件中修改状态: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>
)
}
}
在回调中获取状态值:setState在回调中是同步的:
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更新完成之后执行
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中都是同步的,这⾥的异步其实是批量更新。
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); // 不会执行
this.changeValue(2); // 不会执行
this.changeValue(3); // 会执行
}
render() {
const { counter } = this.state;
return (
<div>
<h3>SetStatePage</h3>
<button onClick={this.setCounter}>{counter}</button>
</div>
)
}
}
如果想要链式更新state:
import React, { Component } from "react"
export default class SetStatePage extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0
}
}
changeValue = (v) => {
// setState函数
this.setState(state => {
return {
counter: state.counter + v
}
})
}
setCounter = () => {
this.changeValue(1); // 会执行
this.changeValue(2); // 会执行
this.changeValue(3); // 会执行
}
render() {
const { counter } = this.state;
return (
<div>
<h3>SetStatePage</h3>
<button onClick={this.setCounter}>{counter}</button>
</div>
)
}
}
四、组件复合
复合组件给与你足够的敏捷去定义自定义组件的外观和行为,这种方式更明确和安全。如果组件间有公⽤用的⾮UI逻辑,将它们抽取为JS模块导入使⽤而不是继承它。
不具名:
HomePage.js文件内容如下:
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文件内容如下:
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文件内容如下:
import React, { Component } from "react";
export default class TopBar extends Component {
render() {
return (
<div>
<h3>TopBar</h3>
</div>
)
}
}
BottomBar.js文件内容如下:
import React, { Component } from "react";
export default class BottomBar extends Component {
render() {
return (
<div>
<h3>BottomBar</h3>
</div>
)
}
}
具名:传个对象进去
userPage.js文件内容如下:
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文件内容如下:
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>
)
}
}
网友评论