仪表板应用教程
1. 概述
在本教程中,我们将学习如何使用 React 构建一个简单的仪表板应用。仪表板应用通常用于显示和管理各种数据,如统计信息、图表、表格等。我们将从环境搭建开始,逐步深入到组件设计、状态管理、路由配置、样式处理等方面。
2. 环境搭建
2.1 Node.js 和 npm
首先,确保你已经安装了 Node.js 和 npm。你可以通过以下命令检查它们的版本:
node -v
npm -v
如果没有安装,请访问 Node.js 官网 下载并安装。
2.2 Create React App
使用 Create React App 快速搭建 React 项目:
npx create-react-app dashboard-app
cd dashboard-app
npm start
这将创建一个基本的 React 项目,并在浏览器中打开 http://localhost:3000。
3. JSX 语法
3.1 什么是 JSX?
JSX 是 JavaScript 的语法扩展,允许你在 JavaScript 中编写类似 HTML 的代码。例如:
const element = <h1>Hello, world!</h1>;
3.2 使用 JSX
在 src/App.js 中,你可以看到类似以下的代码:
import React from 'react';
import './App.css';
function App() {
return (
<div className="App">
<header className="App-header">
<h1>Welcome to the Dashboard</h1>
</header>
</div>
);
}
export default App;
4. 组件基础
4.1 函数组件
函数组件是一个简单的 JavaScript 函数,返回 JSX:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
4.2 类组件
类组件使用 ES6 类来定义:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
5. Props 和 State
5.1 Props
Props 是组件的输入参数,用于传递数据:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Alice" />
<Welcome name="Bob" />
</div>
);
}
5.2 State
State 是组件的内部数据,可以通过 useState Hook 或类组件的 this.state 来管理:
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
6. 生命周期方法
6.1 类组件生命周期
类组件有多个生命周期方法,如 componentDidMount、componentDidUpdate 和 componentWillUnmount:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = { date: new Date() };
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
6.2 Hooks 生命周期
使用 useEffect Hook 可以实现类似的生命周期功能:
import React, { useState, useEffect } from 'react';
function Clock() {
const [date, setDate] = useState(new Date());
useEffect(() => {
const timerID = setInterval(() => {
setDate(new Date());
}, 1000);
return () => clearInterval(timerID);
}, []);
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {date.toLocaleTimeString()}.</h2>
</div>
);
}
7. 事件处理
7.1 处理点击事件
function ActionButton() {
function handleClick() {
alert('Button clicked!');
}
return (
<button onClick={handleClick}>
Click me
</button>
);
}
7.2 传递参数
function ActionButton() {
function handleClick(message) {
alert(message);
}
return (
<button onClick={() => handleClick('Button clicked!')}>
Click me
</button>
);
}
8. 条件渲染
8.1 使用 if 语句
function Greeting(props) {
if (props.isLoggedIn) {
return <h1>Welcome back!</h1>;
}
return <h1>Please sign up.</h1>;
}
8.2 使用三元运算符
function Greeting(props) {
return (
<h1>{props.isLoggedIn ? 'Welcome back!' : 'Please sign up.'}</h1>
);
}
9. 列表和 Keys
9.1 渲染列表
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
function NumberList() {
return (
<ul>{listItems}</ul>
);
}
9.2 Keys
Keys 帮助 React 识别哪些项目已更改、添加或删除。它们应该是唯一的:
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
10. 表单处理
10.1 受控组件
function NameForm() {
const [value, setValue] = useState('');
const handleChange = (event) => {
setValue(event.target.value);
};
const handleSubmit = (event) => {
alert('A name was submitted: ' + value);
event.preventDefault();
};
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input type="text" value={value} onChange={handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
10.2 非受控组件
function NameForm() {
const inputRef = useRef(null);
const handleSubmit = (event) => {
alert('A name was submitted: ' + inputRef.current.value);
event.preventDefault();
};
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input type="text" ref={inputRef} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
11. 组合 vs 继承
11.1 组合
React 推荐使用组合而非继承来实现代码复用:
function Dialog(props) {
return (
<div className="Dialog">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
{props.children}
</div>
);
}
function WelcomeDialog() {
return (
<Dialog
title="Welcome"
message="Thank you for visiting our spacecraft!" />
);
}
11.2 继承
React 不推荐使用继承,因为组合已经足够灵活。
12. 高阶组件 (HOC)
12.1 什么是 HOC?
高阶组件是一个函数,接收一个组件并返回一个新的组件:
function withSubscription(WrappedComponent, selectData) {
return class extends React.Component {
constructor(props) {
super(props);
this.state = {
data: selectData(DataSource, props)
};
}
componentDidMount() {
DataSource.addChangeListener(this.handleChange);
}
componentWillUnmount() {
DataSource.removeChangeListener(this.handleChange);
}
handleChange = () => {
this.setState({
data: selectData(DataSource, this.props)
});
};
render() {
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
}
13. Render Props
13.1 什么是 Render Props?
Render Props 是一种在 React 组件之间共享代码的技术,通过一个函数 prop 来实现:
class MouseTracker extends React.Component {
constructor(props) {
super(props);
this.state = { x: 0, y: 0 };
}
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
});
};
render() {
return (
<div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
{this.props.render(this.state)}
</div>
);
}
}
function App() {
return (
<MouseTracker render={({ x, y }) => (
<h1>The mouse position is ({x}, {y})</h1>
)}/>
);
}
14. Context API
14.1 什么是 Context?
Context 提供了一种在组件树中传递数据的方式,而不需要手动通过 props 逐层传递:
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
15. Refs 和 DOM
15.1 使用 Refs
Refs 提供了一种访问 DOM 节点或 React 元素的方式:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
componentDidMount() {
this.myRef.current.focus();
}
render() {
return <input ref={this.myRef} type="text" />;
}
}
16. useState 和 useEffect
16.1 useState
useState 是 React 的一个 Hook,用于在函数组件中添加状态:
import React, { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
16.2 useEffect
useEffect 用于在函数组件中执行副作用操作:
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
17. useContext
17.1 使用 useContext
useContext 用于在函数组件中访问 Context:
import React, { useContext } from 'react';
const ThemeContext = React.createContext('light');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <Button theme={theme} />;
}
18. useReducer
18.1 使用 useReducer
useReducer 是 useState 的替代方案,适用于复杂的状态逻辑:
import React, { useReducer } from 'react';
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</>
);
}
19. 自定义 Hooks
19.1 创建自定义 Hook
自定义 Hook 是一个函数,名称以 use 开头,可以调用其他 Hook:
import { useState, useEffect } from 'react';
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
useEffect(() => {
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
20. Hooks 规则和最佳实践
20.1 规则
- 只在顶层调用 Hooks。
- 只在 React 函数组件或自定义 Hook 中调用 Hooks。
20.2 最佳实践
- 使用
useCallback和useMemo优化性能。 - 使用
useReducer管理复杂状态。
21. Redux 基础
21.1 什么是 Redux?
Redux 是一个状态管理库,用于管理应用的全局状态:
import { createStore } from 'redux';
function counterReducer(state = { value: 0 }, action) {
switch (action.type) {
case 'counter/incremented':
return { value: state.value + 1 };
case 'counter/decremented':
return { value: state.value - 1 };
default:
return state;
}
}
let store = createStore(counterReducer);
store.subscribe(() => console.log(store.getState()));
store.dispatch({ type: 'counter/incremented' });
store.