適合初學者的綜合 React 備忘清單
React 是一個用于構建用戶界面的 JavaScript 庫
import {createRoot} from 'react-dom/client'
import App from './App'
const elm = document.getElementById('app')
const root = createRoot(elm);
root.render(<App />);
npx create-react-app my-app
import React, {Component} from 'react'
import ReactDOM from 'react-dom'
export class Hello extends Component {
...
}
export default function World() {
/* ... */
}
使用 export
導出 Hello
,export default
導出 World
組件
import World, { Hello } from './hello.js';
使用 import
導入 Hello
組件,在示例中使用。
import React from "react";
import "./Student.css";
export const Student = (
<div className="Student"></div>
);
注意:類屬性 className
const divStyle = {
backgroundImage: 'url(' + imgUrl + ')',
};
export const Student = (
<div style={divStyle}></div>
);
<Student name="Julie" age={23}
pro={true} />
函數組件 Student
中訪問屬性
function Student(props) {
return <h1>Hello, {props.name}</h1>;
}
Class 組件 Student
中訪問屬性
class Student extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
class
組件使用 this.props
訪問傳遞給組件的屬性。
function Example() {
return (
<AlertBox>
<h1>您有待處理的通知</h1>
</AlertBox>
)
}
函數 AlertBox
組件
function AlertBox(props) {
return (
<div className="alert-box">
{props.children}
</div>
);
}
{props.children}
Class AlertBox
組件,與函數組件 AlertBox
組件相同
class AlertBox extends React.Component {
render () {
return (
<div className="alert-box">
{this.props.children}
</div>
);
}
}
{this.props.children}
children
作為子組件的的屬性傳遞。
函數中的 State,Hook 是 React 16.8 的新增特性
import { useState } from 'react';
function Student() {
const [count, setCount] = useState(0);
const click = () => setCount(count + 1);
return (
<div>
<p>您點擊了 {count} 次</p>
<button onClick={click}>
點擊我
</button>
</div>
);
}
使用 setState
更新狀態,下面是函數組件讀取狀態
<p>您點擊了 {count} 次</p>
import React from 'react';
class Student extends React.Component {
constructor(props) {
super(props);
this.state = {count: 1};
// 確保函數可以訪問組件屬性(ES2015)
this.click = this.click.bind(this);
}
click() {
const count = this.state.count;
this.setState({ count: count + 1})
}
render() {
return (
<div>
<button onClick={this.click}>
點擊我
</button>
<p>您點擊了{this.state.count}次</p>
</div>
);
}
}
使用 setState
更新狀態,class
組件中不能使用 hooksclass
組件讀取狀態
<p>您點擊了{this.state.count}次</p>
const elm = ['one', 'two', 'three'];
function Student() {
return (
<ul>
{elm.map((value, index) => (
<li key={index}>{value}</li>
))}
</ul>
);
}
key
值在兄弟節點之間必須唯一
export default function Hello() {
function handleClick(event) {
event.preventDefault();
alert("Hello World");
}
return (
<a href="/" onClick={handleClick}>
Say Hi
</a>
);
}
function addNumbers(x1, x2) {
return x1 + x2;
}
const element = (
<div>
{addNumbers(2, 5)}
</div>
);
import { useState } from 'react'
import Avatar from './Avatar';
import Profile from './Profile';
function Student() {
const [count, setCount] = useState(0);
return (
<div>
<Avatar src={count} />
<Profile username={count} />
</div>
);
}
React 并_沒有_創建一個新的 div
。它只是把子元素渲染到 domNode
中。domNode
是一個可以在任何位置的有效 DOM 節點。
render() {
return ReactDOM.createPortal(
this.props.children,
domNode
);
}
提供了一種將子節點渲染到存在于父組件以外的 DOM 節點的優秀的方案
import { Fragment } from 'react'
import Avatar from './Avatar';
import Profile from './Profile';
const Student = () => (
<Fragment>
<Avatar src="./demo.jpg" />
<Profile username="name" />
</Fragment>
);
從 v16.2.0
開始 Fragment
可用于返回多個子節點,而無需向 DOM 添加額外的包裝節點。或者使用 <></>
效果是一樣的。
const Student = () => (
<>
<Avatar src="./demo.jpg" />
<Profile username="name" />
</>
);
查看:
render() {
return 'Look ma, no spans!';
}
您可以只返回一個字符串。查看:
const Student = () => [
<li key="A">First item</li>,
<li key="B">Second item</li>
];
不要忘記 key
!查看:
const FancyButton = React.forwardRef(
(props, ref) => (
<button ref={ref} className="btn">
{props.children}
</button>
)
);
// 你可以直接獲取 DOM button 的 ref:
const ref = React.createRef();
<FancyButton ref={ref}>
點擊我
</FancyButton>;
import {Component,createRef} from 'react'
class MyComponent extends Component {
constructor(props) {
super(props);
this.myRef = createRef();
}
render() {
return <div ref={this.myRef} />;
}
}
提示:Refs 適用于類組件,但不適用于函數組件(除非您使用 useRef hook,請參閱hooks)
function CustomTextInput(props) {
// 這里必須聲明 $input,這樣 ref 才可以引用它
const $input = useRef(null);
function handleClick() {
$input.current.focus();
}
return (
<div>
<input type="text" ref={$input} />
<input
type="button" value="聚焦文本輸入"
onClick={handleClick}
/>
</div>
);
}
<div>
<Header />
<React.StrictMode>
<div>
<ComponentOne />
<ComponentTwo />
</div>
</React.StrictMode>
<Footer />
</div>
突出顯示應用程序中潛在問題的工具。請參閱:
測量一個 React 應用多久渲染一次以及渲染一次的 代價
<Profiler id="Navigation" onRender={callback}>
<Navigation {...props} />
</Profiler>
為了分析 Navigation
組件和它的子代。應該在需要時才去使用它。
:- | :- |
---|---|
id(string) | 發生提交的 Profiler 樹的 id |
onRender(function) | 組件樹任何組件 “提交” 一個更新的時候調用這個函數 |
:- | :- |
---|---|
phase: "mount" | "update" | 判斷是由 props /state /hooks 改變 或 “第一次裝載” 引起的重渲染 |
actualDuration: number | 本次更新在渲染 Profiler 和它的子代上花費的時間 |
baseDuration: number | 在 Profiler 樹中最近一次每一個組件 render 的持續時間 |
startTime: number | 本次更新中 React 開始渲染的時間戳 |
commitTime: number | 本次更新中 React commit 階段結束的時間戳 |
interactions: Set | 當更新被制定時,“” 的集合會被追蹤 |
class CustomButton extends React.Component {
// ...
}
CustomButton.defaultProps = {
color: 'blue'
};
<CustomButton /> ;
不傳值 props.color
將自動設置為 blue
class Hello extends Component {
constructor (props) {
super(props)
this.state = { visible: true }
}
}
在構造 constructor()
中設置默認狀態。
class Hello extends Component {
state = { visible: true }
}
function CustomButton(props) {
const { color = 'blue' } = props;
return <div>{color}</div>
}
function CustomButton() {
const [color, setColor]=useState('blue')
return <div>{color}</div>
}
JSX
僅僅只是 React.createElement(component, props, ...children)
函數的語法糖
<MyButton color="blue" shadowSize={2}>
點擊我
</MyButton>
會編譯為
React.createElement(
MyButton,
{color: 'blue', shadowSize: 2},
'點擊我'
);
沒有子節點
<div className="sidebar" />
會編譯為
React.createElement(
'div',
{className: 'sidebar'}
)
const Menu = ({ children }) => (
<div className="menu">{children}<div>
);
Menu.Item = ({ children }) => (
<div>{children}<div>
);
<Menu>
<Menu.Item>菜單一</Menu.Item>
<Menu.Item>菜單二</Menu.Item>
<Menu>
let element = <h1>Hello, world!</h1>;
let emptyHeading = <h1 />;
const root = ReactDOM.createRoot(
document.getElementById('root')
);
const element = <h1>Hello, world</h1>;
root.render(element);
參考:
const avatarUrl = "img/picture.jpg"
const element = <img src={avatarUrl} />;
const element = (
<button className="btn">
點擊我
</button>
);
注意:類屬性 className
let name = '張三';
let element = <h1>Hello, {name}</h1>;
function fullName(firstName, lastName) {
return firstName + ' ' + lastName;
}
let element = (
<h1>
Hello, {fullName('三', '張')}
</h1>
);
const divStyle = {
color: 'blue',
backgroundImage: 'url(' + imgUrl + ')',
};
function MyComponent() {
return <div style={divStyle}>組件</div>;
}
const markup = {__html: '我 · 你' };
const MyComponent = () => (
<div dangerouslySetInnerHTML={markup} />
);
dangerouslySetInnerHTML
是 React 為瀏覽器 DOM 提供 innerHTML
的替換方案。
const MyComponent = () => (
<div>
<input type="radio" id="ab" name="v">
<label for="ab">HTML</label>
</div>
);
for
在 JS
中是保留字,JSX 元素使用了 htmlFor
代替
非受控組件的屬性,設置組件第一次掛載時的 value
<textarea defaultValue="Hello" />
<input>
、<select>
和 <textarea>
支持 value 屬性
非受控組件的屬性,設置組件是否被選中
<input type="radio" defaultChecked />
類型為 checkbox
或 radio
時,組件支持 checked 屬性
屬性用于指定 CSS
的 class
<div className="warp">...</div>
React 中使用 使用 class
屬性代替
import React from "react";
function formatName(user) {
return user.firstName
+ ' '
+ user.lastName;
}
export function Greeting(user) {
if (user) {
return (
<h1>你好, {formatName(user)}!</h1>
);
}
return (
<h1>你好, 先生。</h1>
);
}
注意:組件必須總是返回一些東西。
<Greeting firstName="三" lastName="張" />
export default function Weather(props) {
const isLoggedIn = props.isLoggedIn;
return (
<div>
<b>{isLoggedIn ? '已' : '未'}</b>登錄。
</div>
);
}
{isShow && <div>內容</div>}
<Dropdown>
下拉列表
<Menu>
<Menu.Item>菜單一</Menu.Item>
<Menu.Item>菜單二</Menu.Item>
<Menu.Item>菜單三</Menu.Item>
</Menu>
</Dropdown>
組件名稱以大駝峰式命名。
function Greeting(props) {
let button;
if (props.isLoggedIn) {
button = <UserGreeting />;
} else {
button = <GuestGreeting />;
}
return <div>{button}</div>;
}
function Student() {
const [count, setCount] = useState(0);
return (
<Fragment>
{/* 這里寫注釋 */}
</Fragment>
);
}
import React from 'react';
const UserName = () => <h1>Kenny</h1>;
export default function UserProfile() {
return (
<div className="UserProfile">
<div>Hello</div>
<UserName />
</div>
);
}
注意:每個組件都需要一個根元素,。
class Welcome extends React.Component {
render() {
return <h1>{this.props.name}</h1>;
}
}
:- | - |
---|---|
this.forceUpdate() | 強制重新渲染 |
this.setState({ ... }) | 更新狀態 |
this.setState(state =>{ ... }) | 更新狀態 |
:- | - |
---|---|
defaultProps | 默認 props |
displayName | 顯示組件名稱(用于調試) |
:- | - |
---|---|
this.props | 組件接受參數 |
this.state | 組件內狀態 |
import React, {PureComponent} from 'react'
class MessageBox extends PureComponent {
···
}
import React, { Component } from 'react';
// 高階組件 with
const with = data => WrappedComponent => {
return class extends Component {
constructor(props) {
super(props);
}
render() {
return (
<WrappedComponent data={data} />
)
}
}
}
使用高階組件
const LowComponent = (props) => (
<div>{props.data}</div>
);
const MyComp = with('Hello')(LowComponent)
function FancyBorder(props) {
return (
<div className={'Fancy'+props.color}>
{props.children}
</div>
);
}
組件可以通過 JSX 嵌套
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="title">歡迎</h1>
<p className="message">
感謝您訪問我們的宇宙飛船
</p>
</FancyBorder>
);
}
function SplitPane(props) {
return (
<div className="SplitPane">
<div className="left">
{props.left}
</div>
<div className="right">
{props.right}
</div>
</div>
);
}
function App() {
return (
<SplitPane
left={<Contacts />}
right={<Chat />}
/>
);
}
給組件 SplitPane
傳遞 left
和 right
兩個組件參數
import React from 'react';
import UserAvatar from "./UserAvatar";
export default function UserProfile() {
return (
<div className="UserProfile">
<UserAvatar />
<UserAvatar />
</div>
);
}
注意:假設 UserAvatar
在 UserAvatar.js
中聲明
import React from 'react';
import {Button} from 'uiw';
export default function UserProfile() {
return (
<div className="UserProfile">
<Button type="primary">
主要按鈕
</Button>
</div>
);
}
注意: 組件在 上找到,需要先安裝導入
const Menu = ({ children }) => (
<div className="menu">{children}<div>
);
Menu.Item = ({ children }) => (
<div>{children}<div>
);
<Menu>
<Menu.Item>菜單一</Menu.Item>
<Menu.Item>菜單二</Menu.Item>
<Menu>
方法 | 描述 |
---|---|
useState | 返回一個 state ,更新 state 的函數 |
useEffect | 可能有副作用代碼的函數 |
useContext | 接收并返回該 context 的當前值 |
方法 | 描述 |
---|---|
useReducer | useState 的替代方案 |
useCallback | 返回一個回調函數 |
useMemo | 返回一個 值 |
useRef | 返回一個可變的 ref 對象 |
useImperativeHandle | 暴露給父組件的實例值 |
useLayoutEffect | DOM 變更后同步調用函數 |
useDebugValue | 開發者工具中顯示標簽 |
useDeferredValue | 接受并返回該值的新副本 |
useTransition | 過渡任務的等待狀態 |
useId | 用于生成唯一 ID |
方法 | 描述 |
---|---|
useSyncExternalStore | 讀取和訂閱外部數據源 |
useInsertionEffect | DOM 突變之前 同步觸發 |
function Counter({ initialCount }) {
const [count, setCount] = useState(initialCount);
return (
<>
Count: {count} <button onClick={() => setCount(initialCount)}>Reset</button>
<button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
<button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
</>
);
}
function TextInputWithFocusButton() {
const $input = useRef(null);
const onButtonClick = () => {
$input.current.focus();
};
return (
<>
<input ref={$input} type="text" />
<button onClick={onButtonClick}>
聚焦輸入
</button>
</>
);
}
current
指向已掛載到 DOM 上的文本輸入元素
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} />;
}
FancyInput = forwardRef(FancyInput);
父組件使用
<FancyInput ref={inputRef} />
inputRef.current.focus()
useEffect(() => {
const subs = props.source.subscribe();
return () => {
subs.unsubscribe();
};
}, [props.source]);
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
const memoizedValue = useMemo(
() => {
return computeExpensiveValue(a, b)
},
[a, b]
);
function Checkbox() {
const id = useId();
return (
<>
<label htmlFor={id}>
你喜歡React嗎?
</label>
<input id={id} type="checkbox" />
</>
);
};
用于生成跨服務端和客戶端穩定的唯一 ID
的同時避免 hydration
不匹配
function useFriendStatus(friendID) {
const [
isOnline, setIsOnline
] = useState(null);
// ...
// 在開發者工具中的這個 Hook 旁邊顯示標簽
// e.g. "FriendStatus: Online"
useDebugValue(
isOnline ? 'Online' : 'Offline'
);
return isOnline;
}
不推薦你向每個自定義 Hook
添加 debug
值
useEffect(
() => {
// componentDidMount
// 組件掛載時,可以在這里完成你的任務
return () => {
// componentWillUnmount
// 卸載時執行,清除 effect
};
},
[ ]
);
這是一個類似 class
組件中 componentDidMount
& componentWillUnmount
兩個生命周期函數的寫法。
方法 | 描述 |
---|---|
constructor (props) | 渲染前 |
static getDerivedStateFromProps() | 調用 render 方法之前調用 |
render() | class 組件中唯一必須實現的方法 |
componentDidMount() | 在組件掛載后(插入 DOM 樹中)立即調用 |
UNSAFE_componentWillMount() | 在掛載之前被調用,建議使用 constructor() |
在 constructor()
上設置初始狀態。在 componentDidMount()
上添加 DOM 事件處理程序、計時器(等),然后在 componentWillUnmount()
上刪除它們。
方法 | 描述 |
---|---|
componentWillUnmount() | 在組件卸載及銷毀之前直接調用 |
過時方法 | 新方法 |
---|---|
componentWillMount() | UNSAFE_componentWillMount() |
componentWillReceiveProps() | UNSAFE_componentWillReceiveProps() |
componentWillUpdate() | UNSAFE_componentWillUpdate() |
17+ 之后不再支持,在 17
版本之后,只有新的 UNSAFE_
生命周期名稱可以使用。
方法 | 描述 |
---|---|
static getDerivedStateFromProps(props, state) | 調用 render 之前調用,在初始掛載及后續更新時都會被調用 |
shouldComponentUpdate(nextProps, nextState) | 如果返回 false ,則跳過 render() |
render() | 在不修改組件 state 的情況下,每次調用時都返回相同的結果 |
getSnapshotBeforeUpdate() | 在發生更改之前從 DOM 中捕獲一些信息(例如,滾動位置) |
componentDidUpdate() | 這里使用 setState() ,但記得比較 props 。首次渲染不會執行此方法 |
方法 | 描述 |
---|---|
static getDerivedStateFromError(error) | 后代組件拋出錯誤后被調用,它將拋出的錯誤作為參數,并返回一個值以更新 state |
componentDidCatch(error, info) | 在后代組件拋出錯誤后被調用,會在“提交”階段被調用,因此允許執行副作用 |
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
constructor(props) {
super(props);
// 不要在這里調用 this.setState()
this.state = { counter: 0 };
this.handleClick = this.handleClick.bind(this);
}
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// 更新 state 使下一次渲染可以顯降級 UI
return { hasError: true };
}
render() {
if (this.state.hasError) {
// 你可以渲染任何自定義的降級 UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
componentDidUpdate(prevProps) {
// 典型用法(不要忘記比較 props):
if (this.props.uid !== prevProps.uid) {
this.fetchData(this.props.uid);
}
}
getSnapshotBeforeUpdate(prevProps, prevState) {
// 我們是否在 list 中添加新的 items ?
// 捕獲滾動??位置以便我們稍后調整滾動位置。
if (prevProps.list.length < this.props.list.length) {
const list = this.listRef.current;
return list.scrollHeight - list.scrollTop;
}
return null;
}
import PropTypes from 'prop-types'
:- | - |
---|---|
any | 任意類型 |
(props, propName, 組件名稱)=>{} | 自定義驗證器 |
:- | - |
---|---|
string | 字符串 |
number | 數組 |
func | 函數 |
bool | 布爾值 |
symbol | - |
:- | - |
---|---|
oneOf(any) | 枚舉類型 |
oneOfType([type]) | 幾種類型中的任意一個類型 |
:- | - |
---|---|
array | 數組 |
arrayOf | 數組由某一類型的元素組成 |
:- | - |
---|---|
object | 對象 |
objectOf | 對象由某一類型的值組成 |
instanceOf(...) | 類的實例 |
shape | 對象由特定的類型值組成 |
exact | 有額外屬性警告 |
:- | - |
---|---|
element | React 元素 |
elementType | React 元素類型(即 MyComponent ) |
node | DOM 節點 |
:- | - |
---|---|
(···).isRequired | 必需的 |
請參閱:
MyComponent.propTypes = {
email: PropTypes.string,
seats: PropTypes.number,
callback: PropTypes.func,
isClosed: PropTypes.bool,
any: PropTypes.any
symbol: PropTypes.symbol,
}
你可以將屬性聲明為 JS 原生類型,默認都是可選的。
MyComponent.propTypes = {
// 確保這個 prop 沒有被提供時,會打印警告信息
requiredFunc: PropTypes.func.isRequired,
// 任意類型的必需數據
requiredAny: PropTypes.any.isRequired,
}
你可以在任何 PropTypes
屬性后面加上 isRequired
。
MyComponent.propTypes = {
// 只能是特定的值,枚舉類型。
optionalEnum: PropTypes.oneOf([
'News', 'Photos'
]),
// 一個對象可以是幾種類型中的任意一個類型
optionalUnion: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
]),
}
MyComponent.propTypes = {
// 任何可被渲染的元素
// (包括數字、字符串、元素或數組)
// (或 Fragment) 也包含這些類型。
node: PropTypes.node,
// 一個 React 元素。
element: PropTypes.element,
// 一個 React 元素類型(即,MyComponent)
elementType: PropTypes.elementType,
}
MyComponent.propTypes = {
// 可以指定一個對象由某一類型的值組成
objectOf: PropTypes.objectOf(
PropTypes.number
),
// 可以指定一個對象由特定的類型值組成
objectWithShape: PropTypes.shape({
color: PropTypes.string,
fontSize: PropTypes.number
}),
// 帶有額外屬性警告的對象
objectWithStrictShape: PropTypes.exact({
name: PropTypes.string,
quantity: PropTypes.number
}),
}
MyComponent.propTypes = {
custom: (props, propName, compName) => {
if (!/matchm/.test(props[propName])) {
// 它在驗證失敗時應返回一個 Error 對象
return new Error(
'無效的prop `'
` \`${propName}\` 提供給` +
` \`${compName}\`。驗證失敗。`
);
}
},
}
請不要使用 console.warn
或拋出異常,因為這在 oneOfType
中不會起作用。
arrayOf
或 objectOf
驗證器MyComponent.propTypes = {
arrayProp: PropTypes.arrayOf((propValue, key, componentName, location, propFullName) => {
if (!/matchme/.test(propValue[key])) {
// 它應該在驗證失敗時返回一個 Error 對象。
return new Error(
'Invalid prop `' + propFullName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
})
}
propValue
是數組或對象本身,key
是他們當前的鍵。
MyComponent.propTypes = {
arr: PropTypes.arrayOf(PropTypes.number),
};
可以指定一個數組由某一類型的元素組成
MyComponent.propTypes = {
message: PropTypes.instanceOf(Message),
};
聲明 message
為類的實例