React Router 路由
21.1 基础路由设置
javascript
import {
BrowserRouter,
Routes,
Route,
Link,
useNavigate,
useParams,
} from "react-router-dom";
function Home() {
return <h1>Home Page</h1>;
}
function About() {
return <h1>About Page</h1>;
}
function UserProfile() {
const { userId } = useParams();
return <h1>User {userId} Profile</h1>;
}
function NotFound() {
return <h1>404 - Page Not Found</h1>;
}
function App() {
return (
<BrowserRouter>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/user/123">User Profile</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/user/:userId" element={<UserProfile />} />
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
}21.2 导航和重定向
javascript
import { useNavigate, Navigate } from "react-router-dom";
function LoginForm() {
const navigate = useNavigate();
const [isLogged, setIsLogged] = useState(false);
const handleLogin = () => {
setIsLogged(true);
navigate("/dashboard");
};
return <button onClick={handleLogin}>Login</button>;
}
function ProtectedRoute({ isAuthenticated, children }) {
return isAuthenticated ? children : <Navigate to="/login" />;
}Redux Toolkit / Zustand
22.1 Redux Toolkit 基础
javascript
import { createSlice, configureStore } from "@reduxjs/toolkit";
import { useDispatch, useSelector } from "react-redux";
// 创建 slice
const counterSlice = createSlice({
name: "counter",
initialState: { value: 0 },
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action) => {
state.value += action.payload;
},
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
// 配置 store
export const store = configureStore({
reducer: {
counter: counterSlice.reducer,
},
});
// 在组件中使用
function Counter() {
const dispatch = useDispatch();
const count = useSelector((state) => state.counter.value);
return (
<div>
<p>{count}</p>
<button onClick={() => dispatch(increment())}>+</button>
<button onClick={() => dispatch(decrement())}>-</button>
<button onClick={() => dispatch(incrementByAmount(5))}>+5</button>
</div>
);
}22.2 Redux Toolkit 异步操作
javascript
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
// 创建异步 thunk
export const fetchUsers = createAsyncThunk("users/fetchUsers", async () => {
const response = await fetch("/api/users");
return response.json();
});
const usersSlice = createSlice({
name: "users",
initialState: { users: [], loading: false, error: null },
extraReducers: (builder) => {
builder
.addCase(fetchUsers.pending, (state) => {
state.loading = true;
})
.addCase(fetchUsers.fulfilled, (state, action) => {
state.loading = false;
state.users = action.payload;
})
.addCase(fetchUsers.rejected, (state, action) => {
state.loading = false;
state.error = action.error.message;
});
},
});
export default usersSlice.reducer;22.3 Zustand 简化状态管理
javascript
import create from "zustand";
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 }),
}));
function Counter() {
const { count, increment, decrement, reset } = useStore();
return (
<div>
<p>{count}</p>
<button onClick={increment}>+</button>
<button onClick={decrement}>-</button>
<button onClick={reset}>Reset</button>
</div>
);
}React Query 数据请求
23.1 基础查询
javascript
import { useQuery } from "@tanstack/react-query";
function Users() {
const { data, isLoading, error } = useQuery({
queryKey: ["users"],
queryFn: async () => {
const response = await fetch("/api/users");
return response.json();
},
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{data?.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}23.2 缓存和重新获取
javascript
function UserProfile({ userId }) {
const { data, refetch, isFetching } = useQuery({
queryKey: ["user", userId],
queryFn: async () => {
const response = await fetch(`/api/users/${userId}`);
return response.json();
},
staleTime: 1000 * 60 * 5, // 5分钟
cacheTime: 1000 * 60 * 10, // 10分钟
});
return (
<div>
<h1>{data?.name}</h1>
<button onClick={() => refetch()}>
{isFetching ? "Refetching..." : "Refresh"}
</button>
</div>
);
}23.3 变更(Mutations)
javascript
import { useMutation, useQueryClient } from "@tanstack/react-query";
function CreateUser() {
const queryClient = useQueryClient();
const { mutate, isLoading } = useMutation({
mutationFn: async (newUser) => {
const response = await fetch("/api/users", {
method: "POST",
body: JSON.stringify(newUser),
headers: { "Content-Type": "application/json" },
});
return response.json();
},
onSuccess: () => {
queryClient.invalidateQueries(["users"]);
},
});
const handleSubmit = (e) => {
e.preventDefault();
mutate({ name: "John", email: "john@example.com" });
};
return (
<form onSubmit={handleSubmit}>
<button type="submit" disabled={isLoading}>
{isLoading ? "Creating..." : "Create User"}
</button>
</form>
);
}