在現代前端開發中,React已經成為最流行的JavaScript庫之一。它以其組件化、聲明式編程和高效的虛擬DOM渲染機制,贏得了廣大開發者的青睞。本文將詳細介紹如何使用React實現一個簡單的TodoList應用。通過這個項目,你將掌握React的基礎知識、組件化開發、狀態管理以及一些常見的優化技巧。
React是由Facebook開發并開源的一個用于構建用戶界面的JavaScript庫。它主要用于構建單頁應用(SPA),通過組件化的方式將UI拆分為獨立的、可復用的部分。React的核心思想是聲明式編程,開發者只需描述UI應該是什么樣子,而不需要關心具體的DOM操作。
React應用由多個組件構成,每個組件負責渲染一部分UI。組件可以是函數組件或類組件。函數組件是一個純函數,接收props
作為參數并返回一個React元素。類組件則是一個ES6類,繼承自React.Component
,并且可以包含狀態和生命周期方法。
// 函數組件
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// 類組件
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
JSX是JavaScript的語法擴展,允許在JavaScript代碼中編寫類似HTML的標記。JSX最終會被Babel編譯為React.createElement
調用。
const element = <h1>Hello, world!</h1>;
React組件可以通過state
來管理內部狀態。狀態是組件私有的,并且可以通過setState
方法來更新。狀態的變化會觸發組件的重新渲染。
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>
);
}
}
React事件處理與DOM事件處理類似,但有一些語法上的差異。React事件使用駝峰命名法,并且需要傳遞一個函數作為事件處理程序,而不是字符串。
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = { isToggleOn: true };
// 為了在回調中使用 `this`,這個綁定是必不可少的
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
要創建一個新的React項目,可以使用create-react-app
工具。這個工具會自動配置開發環境,包括Webpack、Babel、ESLint等。
npx create-react-app todo-list
cd todo-list
npm start
create-react-app
生成的項目結構如下:
todo-list/
├── node_modules/
├── public/
│ ├── index.html
│ └── ...
├── src/
│ ├── App.css
│ ├── App.js
│ ├── App.test.js
│ ├── index.css
│ ├── index.js
│ ├── logo.svg
│ └── serviceWorker.js
├── package.json
├── README.md
└── ...
一個典型的TodoList應用需要具備以下功能:
根據需求分析,我們可以將TodoList應用拆分為以下幾個組件:
首先,我們創建一個Todo
組件,作為整個應用的入口。Todo
組件將包含TodoForm
、TodoList
和Filter
組件。
import React, { useState } from 'react';
import TodoForm from './TodoForm';
import TodoList from './TodoList';
import Filter from './Filter';
function Todo() {
const [todos, setTodos] = useState([]);
const [filter, setFilter] = useState('all');
const addTodo = (text) => {
const newTodo = { id: Date.now(), text, completed: false };
setTodos([...todos, newTodo]);
};
const toggleTodo = (id) => {
setTodos(
todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
);
};
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
const editTodo = (id, newText) => {
setTodos(
todos.map(todo =>
todo.id === id ? { ...todo, text: newText } : todo
)
);
};
const filteredTodos = todos.filter(todo => {
if (filter === 'completed') {
return todo.completed;
} else if (filter === 'active') {
return !todo.completed;
} else {
return true;
}
});
return (
<div>
<h1>Todo List</h1>
<TodoForm addTodo={addTodo} />
<Filter setFilter={setFilter} />
<TodoList
todos={filteredTodos}
toggleTodo={toggleTodo}
deleteTodo={deleteTodo}
editTodo={editTodo}
/>
</div>
);
}
export default Todo;
接下來,我們創建TodoForm
組件,用于添加新任務。
import React, { useState } from 'react';
function TodoForm({ addTodo }) {
const [text, setText] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (text.trim()) {
addTodo(text);
setText('');
}
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="Add a new task"
/>
<button type="submit">Add</button>
</form>
);
}
export default TodoForm;
然后,我們創建TodoList
組件,用于顯示任務列表。
import React from 'react';
import TodoItem from './TodoItem';
function TodoList({ todos, toggleTodo, deleteTodo, editTodo }) {
return (
<ul>
{todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
toggleTodo={toggleTodo}
deleteTodo={deleteTodo}
editTodo={editTodo}
/>
))}
</ul>
);
}
export default TodoList;
接下來,我們創建TodoItem
組件,用于顯示單個任務,并處理任務的完成、刪除和編輯操作。
import React, { useState } from 'react';
function TodoItem({ todo, toggleTodo, deleteTodo, editTodo }) {
const [isEditing, setIsEditing] = useState(false);
const [editText, setEditText] = useState(todo.text);
const handleEdit = () => {
if (isEditing) {
editTodo(todo.id, editText);
}
setIsEditing(!isEditing);
};
return (
<li>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
{isEditing ? (
<input
type="text"
value={editText}
onChange={(e) => setEditText(e.target.value)}
/>
) : (
<span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
{todo.text}
</span>
)}
<button onClick={handleEdit}>{isEditing ? 'Save' : 'Edit'}</button>
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</li>
);
}
export default TodoItem;
刪除任務的功能已經在TodoItem
組件中實現,通過調用deleteTodo
函數來刪除指定任務。
編輯任務的功能也在TodoItem
組件中實現。通過isEditing
狀態來控制是否顯示編輯輸入框,并通過editTodo
函數來保存編輯后的任務內容。
最后,我們創建Filter
組件,用于過濾任務列表。
import React from 'react';
function Filter({ setFilter }) {
return (
<div>
<button onClick={() => setFilter('all')}>All</button>
<button onClick={() => setFilter('active')}>Active</button>
<button onClick={() => setFilter('completed')}>Completed</button>
</div>
);
}
export default Filter;
隨著應用規模的增大,組件之間的狀態傳遞可能會變得復雜。React提供了Context API,用于在組件樹中共享狀態,而不需要顯式地通過props逐層傳遞。
import React, { createContext, useState } from 'react';
export const TodoContext = createContext();
export function TodoProvider({ children }) {
const [todos, setTodos] = useState([]);
const [filter, setFilter] = useState('all');
const addTodo = (text) => {
const newTodo = { id: Date.now(), text, completed: false };
setTodos([...todos, newTodo]);
};
const toggleTodo = (id) => {
setTodos(
todos.map(todo =>
todo.id === id ? { ...todo, completed: !todo.completed } : todo
)
);
};
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
const editTodo = (id, newText) => {
setTodos(
todos.map(todo =>
todo.id === id ? { ...todo, text: newText } : todo
)
);
};
const filteredTodos = todos.filter(todo => {
if (filter === 'completed') {
return todo.completed;
} else if (filter === 'active') {
return !todo.completed;
} else {
return true;
}
});
return (
<TodoContext.Provider
value={{
todos: filteredTodos,
addTodo,
toggleTodo,
deleteTodo,
editTodo,
setFilter
}}
>
{children}
</TodoContext.Provider>
);
}
然后,在Todo
組件中使用TodoProvider
包裹子組件。
import React from 'react';
import TodoForm from './TodoForm';
import TodoList from './TodoList';
import Filter from './Filter';
import { TodoProvider } from './TodoContext';
function Todo() {
return (
<TodoProvider>
<div>
<h1>Todo List</h1>
<TodoForm />
<Filter />
<TodoList />
</div>
</TodoProvider>
);
}
export default Todo;
對于更復雜的應用,可以使用Redux來管理全局狀態。Redux是一個可預測的狀態容器,適用于大型應用。
首先,安裝Redux和React-Redux:
npm install redux react-redux
然后,創建Redux store、actions和reducers。
// store.js
import { createStore } from 'redux';
import rootReducer from './reducers';
const store = createStore(rootReducer);
export default store;
// actions.js
export const addTodo = (text) => ({
type: 'ADD_TODO',
payload: { id: Date.now(), text, completed: false }
});
export const toggleTodo = (id) => ({
type: 'TOGGLE_TODO',
payload: id
});
export const deleteTodo = (id) => ({
type: 'DELETE_TODO',
payload: id
});
export const editTodo = (id, newText) => ({
type: 'EDIT_TODO',
payload: { id, newText }
});
export const setFilter = (filter) => ({
type: 'SET_FILTER',
payload: filter
});
// reducers.js
const initialState = {
todos: [],
filter: 'all'
};
function todoReducer(state = initialState, action) {
switch (action.type) {
case 'ADD_TODO':
return {
...state,
todos: [...state.todos, action.payload]
};
case 'TOGGLE_TODO':
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.payload ? { ...todo, completed: !todo.completed } : todo
)
};
case 'DELETE_TODO':
return {
...state,
todos: state.todos.filter(todo => todo.id !== action.payload)
};
case 'EDIT_TODO':
return {
...state,
todos: state.todos.map(todo =>
todo.id === action.payload.id ? { ...todo, text: action.payload.newText } : todo
)
};
case 'SET_FILTER':
return {
...state,
filter: action.payload
};
default:
return state;
}
}
export default todoReducer;
最后,在Todo
組件中使用Provider
包裹子組件,并使用useSelector
和useDispatch
來訪問和更新狀態。
import React from 'react';
import { Provider, useSelector, useDispatch } from 'react-redux';
import store from './store';
import TodoForm from './TodoForm';
import TodoList from './TodoList';
import Filter from './Filter';
import { addTodo, toggleTodo, deleteTodo, editTodo, setFilter } from './actions';
function Todo() {
const todos = useSelector(state => {
if (state.filter === 'completed') {
return state.todos.filter(todo => todo.completed);
} else if (state.filter === 'active') {
return state.todos.filter(todo => !todo.completed);
} else {
return state.todos;
}
});
const dispatch = useDispatch();
return (
<div>
<h1>Todo List</h1>
<TodoForm addTodo={(text) => dispatch(addTodo(text))} />
<Filter setFilter={(filter) => dispatch(setFilter(filter))} />
<TodoList
todos={todos}
toggleTodo={(id) => dispatch(toggleTodo(id))}
deleteTodo={(id) => dispatch(deleteTodo(id))}
editTodo={(id, newText) => dispatch(editTodo(id, newText))}
/>
</div>
);
}
function App() {
return (
<Provider store={store}>
<Todo />
</Provider>
);
}
export default App;
React應用性能優化的常見方法包括:
React.memo
:React.memo
是一個高階組件,用于緩存函數組件的渲染結果,避免不必要的重新渲染。import React from 'react';
const TodoItem = React.memo(({ todo, toggleTodo, deleteTodo, editTodo }) => {
// ...
});
export default TodoItem;
useCallback
和useMemo
:useCallback
用于緩存回調函數,useMemo
用于緩存計算結果。”`jsx import React, { useCallback, useMemo } from ‘react’;
function Todo() { const addTodo = useCallback((text) => { const newTodo = { id: Date.now(), text, completed: false }; setTodos([…todos, newTodo]); }, [todos]);
const filteredTodos = useMemo(() => { return todos.filter(todo => { if (filter === ‘completed’) { return
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。