一、React 視圖層
項(xiàng)目創(chuàng)建
Create React App
npx create-react-app my-app
cd my-app
npm start / yarn start
JSX
基于 JS 的擴(kuò)展語法

如何將 React 元素渲染到DOM中?
const element = <h1>Hello,world</h1>;
ReactDOM.render(element,document.getElementById('root'))
組件
React 應(yīng)用組成和復(fù)用的基本單元
component = (props) => element
React組件必須像純函數(shù)一樣運(yùn)行!

函數(shù)組件
import React from 'react';
function Welcome(props) {
return <h1>Hello, {props.name} </h1>
}
export default Welcome;
類組件
必須有一個(gè) render() 方法
import React from 'react';
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name} </h1>
}
}
export default Welcome;
列表中的 key
只需要保證,在同一個(gè)數(shù)組中的兄弟元素之間的 key 是唯一的。而不需要在整個(gè)應(yīng)用程序甚至單個(gè)組件中保持唯一。
理想情況下,key 應(yīng)該從數(shù)據(jù)中獲取,對(duì)應(yīng)著唯一且固定的標(biāo)識(shí)符,例如 post.id。
state
只能在類組件中使用,組件內(nèi)部的可變狀態(tài)

創(chuàng)建 Clock 時(shí)鐘組件
import React, { Component } from 'react';
class Clock extends Component {
constructor(props) {
super(props);
this.state = { date: new Date() };
}
// 組件被掛載到 DOM 節(jié)點(diǎn)時(shí)會(huì)調(diào)用
componentDidMount() {
// setInterval 循環(huán)任務(wù) API
this.timerID = setInterval(() => this.tick(), 1000);
}
// 組件從 DOM 卸載時(shí)會(huì)調(diào)用
componentWillUnmount() {
// 清除 setInterval 創(chuàng)建的定時(shí)任務(wù)
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>Current Time:{this.state.date.toLocaleTimeString()} </div>
);
}
}
export default Clock;
setState 注意事項(xiàng)
不能通過 this.state 直接賦值
// Wrong
this.state.comment = 'Hello';
使用 this.setState 方法賦值
// Correct
this.setState({comment: 'Hello'});
如果新的狀態(tài)需要依賴當(dāng)前狀態(tài)得到
// Wrong
this.setState({
counter: this.state.counter + this.props.increment
})
// Correct
this.setState((state, props) => ({
counter:satae.counter + props.increment
}))
setState 對(duì) state 的修改是部分修改,而不是對(duì)整個(gè) state 全量替換

state 總結(jié)
- Props:父組件傳遞給子組件的屬性,在子組件內(nèi)部只讀
- State:組件內(nèi)部自己維護(hù)的狀態(tài),可以被修改
生命周期方法
針對(duì)類組件是有意義的,而函數(shù)組件沒有這些生命周期方法

Form
受控組件
input 的值受 react 組件控制
import React, { Component } from 'react';
class NameForm extends Component {
constructor(props) {
super(props);
this.state = { value: '' };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ value: event.target.value })
}
handleSubmit(event) {
alert('A name was submitted:' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit} >
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
export default NameForm;
非受控組件
import React, { Component } from 'react';
class NameForm extends Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.input = React.createRef();
}
handleSubmit(event) {
alert('A name was submitted:' + this.input.current.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit} >
<label>
Name:
<input type="text" ref={this.input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
export default NameForm;
組件的組合用法
一般用法
import React from 'react';
function FanctBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
)
}
function WelcomeDialog() {
return (
<FanctBorder color="blue">
<h1 className="Dialog-title"> Welcome </h1>
<p> Thank you for visiting our spacecraft! </p>
</FanctBorder>
)
}
export default WelcomeDialog;
更加靈活的組合方式
import React from 'react';
function FanctBorder(props) {
return (
<div>
<div>{props.left}</div>
<div><h3> this is line! </h3></div>
<div>{props.right}</div>
</div>
)
}
function Left(props) {
return (
<div>
<p> this is a Left! </p>
</div>
)
}
function Right(props) {
return (
<div>
<p> this is a Right! </p>
</div>
)
}
function WelcomeDialog() {
return (
<FanctBorder left={<Left />} right={<Right />} />
)
}
export default WelcomeDialog;

React Hooks
React 16.8 以后,新的組件開發(fā)方法
React Hooks 編寫形式對(duì)比
先來寫一個(gè)最簡(jiǎn)單的組件,點(diǎn)我們點(diǎn)擊按鈕時(shí),點(diǎn)擊數(shù)量不斷增加。
有狀態(tài)組件寫法:
import React, { Component } from 'react';
class Example extends Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
this.addCount = this.addCount.bind(this)
}
render() {
return (
<div>
<button type="button" onClick={this.addCount}>
count is: {this.state.count}
</button>
</div>
);
}
addCount() {
this.setState({
count: this.state.count + 1
})
}
}
export default Example;
React Hooks 寫法
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0)
useEffect(() => {
console.log(`useEffect=> You clicked ${count}`);
return () => {
console.log('解綁生命周期函數(shù)!')
}
}, [count]) // count 發(fā)生變化,就會(huì)執(zhí)行解綁
return (
<div>
<button type="button" onClick={() => setCount((count) => count + 1)}>
count is: {count}
</button>
</div>
)
}
export default Example;
State Hook

const [state, setState] = useState(initialState);
Effect Hook

useEffect(() =>{ effectFn(); return clearFn},[...dependencies])
一般用法
useEffect(() => {
const getUserInfo = async () => {
try {
const userInfo = await fetch('https://api.github.com/users/hzjsj');
console.log(userInfo)
} catch (err) {
console.error(err)
}
}
getUserInfo();
}, [])
其它 Hooks API

二、React Router 路由層
路由分類
1.服務(wù)端路由
請(qǐng)求發(fā)送到服務(wù)端,服務(wù)端返回對(duì)應(yīng)的頁面內(nèi)容
2.客戶端路由
請(qǐng)求不發(fā)送到服務(wù)端,有客戶端代碼更新頁面內(nèi)容
MPA 和 SPA

React Router是什么
React 應(yīng)用中的客戶端路由解決方案
基礎(chǔ)示例
import React from "react";
import {
BrowserRouter as Router,
// HashRouter as Router,
Switch,
Route,
Link,
useHistory,
useParams
} from "react-router-dom";
export default function BasicExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/dashboard">Dashboard</Link>
</li>
</ul>
<hr />
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/dashboard">
<Dashboard />
</Route>
<Route path="/user/:username">
<User />
</Route>
</Switch>
</div>
</Router>
);
}
// You can think of these components as "pages"
// in your app.
function Home() {
const history = useHistory();
return (
<div>
<h2>Home</h2>
<button
onClick={() => {
history.push("/user/jason");
}}
>
Go User{" "}
</button>
</div>
);
}
function About() {
return (
<div>
<h2>About</h2>
</div>
);
}
function Dashboard() {
return (
<div>
<h2>Dashboard</h2>
</div>
);
}
function User() {
const params = useParams();
const username = params.username;
return <div>Welcome: {username}</div>;
}
Router
browserrouter:根據(jù) URL 中的 path 做路由跳轉(zhuǎn)
HashRouter:根據(jù) URL 中的 hash 部分做路由
Route
當(dāng) url 和 Route 中定義的 path 匹配時(shí),渲染對(duì)應(yīng)的組件
重要 props:path、exact
Switch
當(dāng)找到Switch組件內(nèi)的第一個(gè)路由規(guī)則匹配的Route組件后,立即停止后續(xù)的查找
路由跳轉(zhuǎn)
聲明式的組件方式:Link
命令式的 API 調(diào)用方式:history.push
Hooks
useHistory:獲取 history 對(duì)象
useParams:獲取路由中的參數(shù)