React 是一个用于构建用户界面的 JavaScript 库。React 是用户界面(UI)的一部分,不是一个完整的框架。它只关注视图层,也就是 MVC 模式中的 V(视图 View)。React 的核心思想是组件化,将 UI 拆分成独立的、可复用的组件。
状态管理
React中最重要的概念之一是状态(state),它用于存储组件中的数据。以下是一个简单的状态示例:
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
function handleIncrement() {
setCount(count + 1);
}
function handleDecrement() {
setCount(count - 1);
}
return (
<div>
<p>Count: {count}</p>
<button onClick={handleIncrement}>Increment</button>
<button onClick={handleDecrement}>Decrement</button>
</div>
);
}
export default Counter;
条件渲染
React允许我们根据条件显示或隐藏组件。以下是一个简单的条件渲染示例:
import React from 'react';
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <h1>Welcome back!</h1>;
}
else {
return <h1>Please sign up.</h1>;
}
}
export default Greeting;
列表和Keys
在React中,可以通过使用.map()方法进行遍历,从而将数组渲染为一组组件。以下是一个简单的渲染列表示例:
import React from 'react';
function List(props) {
const items = props.items;
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.text}</li>
))}
</ul>
);
}
export default List;
表单
在React中,可以通过使用表单来收集用户输入。以下是一个简单的表单示例:
import React, { useState } from 'react';
function Form() {
const [name, setName] = useState('');
function handleSubmit(event) {
event.preventDefault();
alert(`Hello, ${name}!`);
}
return (
<form onSubmit={handleSubmit}>
<label>
Name:
<input type="text" value={name} onChange={event => setName(event.target.value)} />
</label>
<button type="submit">Submit</button>
</form>
);
}
export default Form;
React 生命周期
React 生命周期是控制 React 组件在不同状态下行为的函数集合。以下是 React 组件生命周期的各个阶段:
- Mounting - 组件被创建并放置在 DOM 中
- Updating - 组件更新并重新渲染
- Unmounting - 组件被从 DOM 中移除
Mounting 阶段
Mounting 阶段发生在组件第一次被渲染到 DOM 中,它包含以下生命周期函数:
class Example extends React.Component {
constructor(props) {
super(props);
console.log("constructor");
}
componentDidMount() {
console.log("componentDidMount");
}
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}
- constructor - 构造函数,通常用于初始化组件状态。
- componentDidMount - 在组件被渲染到 DOM 后,调用 componentDidMount 函数,可以进行一些操作,如调用API,设置计时器等。
Updating 阶段
Updating 阶段发生在组件更新后,它包含以下生命周期函数:
class Example extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
console.log("shouldComponentUpdate");
return true;
}
componentDidUpdate() {
console.log("componentDidUpdate");
}
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}
-
shouldComponentUpdate - 告诉 React 是否有必要重新渲染组件。默认情况下不会引起渲染,只有在下面的情况下才会引起更新:
- 组件的 props 或 state 发生变化
- 父组件重新渲染
-
componentDidUpdate - 在组件更新完成后,调用 componentDidUpdate 函数来更新 DOM,从而更新组件。
Unmounting 阶段
Unmounting 阶段发生在组件从 DOM 中被卸载之前,它只包含一个生命周期函数:
class Example extends React.Component {
componentWillUnmount() {
console.log("componentWillUnmount");
}
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}
- componentWillUnmount - 在组件从 DOM 中卸载之前调用,可以进行一些清理工作,如取消计时器、撤销 API 请求等。
高阶组件
高阶组件(Higher-Order Components,HOC)是一个用于重用组件逻辑的强大技术。以下是一个简单的示例:
import React from 'react';
function withTimestamp(WrappedComponent) {
return class extends React.Component {
render() {
return <WrappedComponent timestamp={new Date()} {...this.props} />;
}
};
}
function Greeting(props) {
return (
<div>
<h1>Hello, {props.name}!</h1>
<p>Current time: {props.timestamp.toString()}</p>
</div>
);
}
const EnhancedGreeting = withTimestamp(Greeting);
export default EnhancedGreeting;
withLoading 高阶组件
javascript复制代码
import React from 'react';
function withLoading(WrappedComponent) {
return class extends React.Component {
state = {
loading: true,
data: null,
error: null
};
async componentDidMount() {
try {
const response = await fetch(this.props.url);
const data = await response.json();
this.setState({ data, loading: false });
} catch (error) {
this.setState({ error, loading: false });
}
}
render() {
if (this.state.loading) {
return <p>Loading...</p>;
} else if (this.state.error) {
return <p>Sorry, an error occurred: {this.state.error.message}</p>;
} else {
return <WrappedComponent {...this.props} data={this.state.data} />;
}
}
};
}
function UserList({ data }) {
return (
<ul>
{data.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
const UserListWithLoading = withLoading(UserList);
function App() {
return (
<div>
<h1>List of users</h1>
<UserListWithLoading
url="https://jsonplaceholder.typicode.com/users"
/>
</div>
);
}
withRouter 高阶组件
javascript复制代码
import React from 'react';
import { withRouter } from 'react-router-dom';
function NavBar({ history }) {
function handleClick() {
history.push('/about');
}
return (
<nav>
<h1>My App</h1>
<button onClick={handleClick}>Go to About page</button>
</nav>
);
}
export default withRouter(NavBar);
这是使用了React Router中的withRouter高阶组件,它会将路由相关的属性(如history对象)传递给被包裹的组件(NavBar)。在上面的示例中,点击按钮时,我们调用history.push方法将页面导航到/about路径。
上下文
React上下文(Context)是一种跨组件层次传递数据的方法。以下是一个使用React上下文的示例:
import React, { createContext, useContext } from 'react';
const MyContext = createContext('default');
function Child() {
const value = useContext(MyContext);
return <p>Value: {value}</p>;
}
function Parent() {
return (
<MyContext.Provider value="hello">
<Child />
</MyContext.Provider>
);
}
export default Parent;
以下是 React 组件的一个示例代码,它展示了如何使用 class 组件、props 和 state 来创建一个简单的计数器:
import React from 'react';
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
this.handleIncrement = this.handleIncrement.bind(this);
this.handleDecrement = this.handleDecrement.bind(this);
}
handleIncrement() {
this.setState({ count: this.state.count + 1 });
}
handleDecrement() {
this.setState({ count: this.state.count - 1 });
}
render() {
return (
<div>
<h1>Count: {this.state.count}</h1>
<button onClick={this.handleIncrement}>+</button>
<button onClick={this.handleDecrement}>-</button>
</div>
);
}
}
export default Counter;
在上述代码中,我们首先定义了一个名为 Counter 的类组件,并在其中通过构造函数创建了一个名为 count 的 state 变量,初始值为 0。另外,为了处理用户输入的事件,在组件中也定义了两个方法:handleIncrement() 和 handleDecrement()。
Props 属性传递
在组件的 render 方法中,我们使用 JSX 模板语言编写了包含一个标题和两个按钮的 UI。其中,按钮分别调用了 handleIncrement() 和 handleDecrement() 方法来增加或减少计数器的值,并使用大括号来绑定当前 state 中的 count 变量到视图中。
以下是关于 React 组件传值和 Store 全局数据存储的示例代码:
import React from 'react';
import Button from './Button';
function App() {
return (
<div>
<h1>Welcome to my app</h1>
<Button text="Click me!" />
</div>
);
}
export default App;
在上述代码中,我们创建了一个名为 App 的组件,并在其中使用 Button 组件。通过将 text 属性赋值为 "Click me!",我们将统一这个值传递给了 Button 组件进行显示。
父子组件传参
import React, { useState } from 'react';
import ChildComponent from './ChildComponent';
function ParentComponent() {
const [message, setMessage] = useState("");
function handleChildButtonClick(msg) {
setMessage(msg);
}
return (
<div>
<ChildComponent onButtonClick={handleChildButtonClick} />
<p>{message}</p>
</div>
);
}
export default ParentComponent;
在上述代码中,我们创建了一个名为 ParentComponent 的组件,并使用 useState Hook 创建了名为 message 的 state 变量。接着,我们定义了一个名为 handleChildButtonClick() 的方法,用来接收子组件传递过来的参数并更新 message 变量的值。
在 render 方法中,我们对 <ChildComponent> 组件传递了一个名为 onButtonClick 的 props 属性,并将 handleChildButtonClick() 方法赋值给了它。这样,在子组件中调用 onButtonClick() 方法时,就能够触发父组件的方法并传递参数了。
Router路由
React Router DOM提供了三个不同的组件来帮助应用完成路由功能:
- BrowserRouter:使用HTML5 history API实现路由跳转,允许你使用类似于传统Web应用的URL,并支持前端路由取代传统后端路由的方式。
- Route:用于在应用中声明路由规则,可以指定一个路径和一个组件,当URL匹配到该路径时,Router将渲染指定组件。
- Link:用于在应用中跳转路由,允许用户与应用透明地交互,并进行页面跳转,它会产生一个类似于
<a>标签的效果,但是不会刷新整个页面,而是仅更新匹配到的组件部分。
React Router还提供了其他一些有用的组件,例如Switch、Redirect和NavLink等,用于路由切换、重定向和激活导航等操作。
当使用React Router时,通常需要在应用的顶级组件中包含BrowserRouter,并使用Route来声明路由规则,例如:
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About'
function App() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/about" component={About} />
</Switch>
</BrowserRouter>
);
}
export default App;
**
在上述代码中,我们先导入了BrowserRouter、Route和Switch等组件。其中BrowserRouter用于包裹整个应用程序,Route用于声明路由规则,Switch用于渲染与当前URL匹配的第一个Route。
我们通过exact属性来确保路由精确匹配,path属性指定了该路由对应的路径,component属性指定了该路径匹配时要渲染的组件。
另外,我们可以在组件中使用Link组件用于跳转路由,例如:
import { Link } from 'react-router-dom';
function Header() {
return (
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</nav>
);
}
export default Header;
**
to属性指定了跳转的路径,当该链接被点击时,React Router会根据指定的路径匹配对应的Route进行渲染。
Redux Store 全局数据存储
以下是一个完整的 React + Redux 应用程序示例代码,展示了如何将全局数据存储和管理在 Redux Store 中,并在组件中进行调用和更新:
创建 Actions
// types.js
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
// actions.js
import { INCREMENT, DECREMENT } from './types';
export function increment() {
return { type: INCREMENT };
}
export function decrement() {
return { type: DECREMENT };
}
在上述代码中,我们定义了两个名为 INCREMENT 和 DECREMENT 的 Action 类型常量,并在 actions.js 文件中通过 increment() 和 decrement() 方法来分别返回这两种类型的 Action。当组件需要进行状态更新时,只需要调用相应方法并将其返回值传递给 Redux Store 即可。
创建 Reducer
// reducer.js
import { INCREMENT, DECREMENT } from './types';
const initialState = {
count: 0,
};
function reducer(state = initialState, action) {
switch (action.type) {
case INCREMENT:
return { ...state, count: state.count + 1 };
case DECREMENT:
return { ...state, count: state.count - 1 };
default:
return state;
}
}
export default reducer;
在上述代码中,我们定义了一个名为 reducer 的纯函数,用于处理对 Store 的更新操作。在初始化状态时,我们使用了一个名为 initialState 的对象来进行默认赋值。当接收到 INCREMENT 或 DECREMENT ActionType 时,我们分别对计数器的值进行加一和减一的操作,并通过展开 state 对象来返回更新后的新状态。
创建 Store
// store.js
import { createStore } from 'redux';
import reducer from './reducer';
const store = createStore(reducer);
export default store;
在上述代码中,我们调用了 Redux 提供的 createStore() 方法,将 reducer 纯函数传递给它以创建一个名为 store 的 Store 实例。在 Store 实例被创建完成之后,我们导出这个 Store 以便于在组件中进行调用和使用。
在组件中调用并操作 Store 数据
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from './actions';
function Counter() {
const count = useSelector(state => state.count);
const dispatch = useDispatch();
function handleIncrement() {
dispatch(increment());
}
function handleDecrement() {
dispatch(decrement());
}
return (
<div>
<h1>Count: {count}</h1>
<button onClick={handleIncrement}>+</button>
<button onClick={handleDecrement}>-</button>
</div>
);
}
export default Counter;
在上述代码中,我们使用了 useSelector() Hook 来从 Store 中获取当前计数值,并使用 useDispatch() Hook 来将 Action Creators 转发到 Store 中实现状态更新。当用户点击 "+" 或 "-" 按钮时,组件会触发 handleIncrement() 或 handleDecrement() 方法,并通过调用相应的 Action Creators 来更新 Store 中的计数器值。
通过以上代码示例,我们可以看到如何使用 Redux 进行全局数据管理,在 React 应用程序中实现组件之间的状态共享和传递。
Hook函数
useState
javascript复制代码
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>
);
}
useEffect()
useEffect是React Hook中常用的函数之一,用于在组件渲染完成后进行一些副作用操作,例如:获取数据、订阅事件、更新DOM等。
useEffect在何时执行,取决于其第二个参数的情况。useEffect的第二个参数是一个数组,用于指定该副作用操作执行的条件。具体来说,useEffect的执行情况有以下几种:
当第二个参数为空数组时,只会在组件渲染完毕后执行一次,相当于类组件中的componentDidMount。
useEffect(() => {
// 副作用操作
}, []);
**
当第二个参数不为空数组时,仅在数组中的元素发生变化时执行,相当于类组件中的componentDidUpdate。
const [count, setCount] = useState(0);
useEffect(() => {
// 副作用操作
console.log(`count的值为${count}`);
}, [count]);
**
上述代码中,只有count的值发生变化时,useEffect才会被执行。
当第二个参数为undefined时,每次组件渲染完成后均会执行副作用操作,相当于类组件中的componentDidMount和componentDidUpdate的组合。
useEffect(() => {
// 副作用操作
});
**
需要注意的是,当useEffect返回一个函数时,该函数将在组件销毁时执行,相当于类组件中的componentWillUnmount。
useEffect(() => {
// 副作用操作
return () => {
// 清理操作
};
});
**
总而言之,useEffect可以用于在组件渲染完成后进行一些副作用操作,并根据第二个参数来控制它的执行情况。
useEffect() Hook 用于在组件渲染之后执行副作用操作(如数据请求、状态变更等)。它接收两个参数:一个回调函数和依赖项数组。当依赖项数组中的任何一个元素发生变化时, useEffect() 都会再次执行回调函数。另外,如果依赖项数组为空或未提供,则回调函数将在每次渲染组件时执行。
举例来说,以下代码演示了如何在 useEffect() 中进行数据请求:
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [data, setData] = useState([]);
useEffect(() => {
fetch('https://my.api.com/data')
.then(response => response.json())
.then(data => setData(data));
}, []);
return (
<ul>
{data.map(item => <li key={item.id}>{item.name}</li>)}
</ul>
);
}
export default MyComponent;
在上述代码中,我们使用 useState() Hook 创建了一个名为 data 的 state 变量,并在 useEffect() Hook 中通过 fetch() 方法进行了数据请求。由于要确保仅在组件第一次加载时执行一次数据请求并加载数据,我们将空数组作为第二个参数传递给 useEffect(),从而将其作为依赖项放入。最终,在成功获取和解析数据之后,我们将其存储在 data 变量中,并通过使用 map() 方法对其进行渲染。
import React, { useState, useEffect } from 'react';
export default function App() {
const [state, setState] = useState(0);
const timer = useEffect(() => {
setInterval(()=>{
setState((prev)=>prev+1)
},1000)
// 清除定时器
return () => clearInterval()
},[])
return (
<div>
<h2>{state}</h2>
</div>
);
}
在useEffect中进行清除定时器的操作。
useContext()
useContext() Hook 用于在组件间共享上下文数据,避免了通过 props 层层传递数据的麻烦。它接收一个 Context 对象作为参数,并返回与上下文相关的值。需注意,必须将该 Context Provider 装置在当前组件的组件树中。
举例来说,以下代码创建了一个名为 ThemeContext 的 Context 对象,定义了主题颜色,并在不同的组件中使用 useContext() 来获取该上下文数据:
import React, { useContext } from 'react';
const ThemeContext = React.createContext('light');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button style={{ background: theme.background, color: theme.color }}>I am styled by theme context!</button>
}
function MyApp() {
return (
<ThemeContext.Provider value={{ background: 'black', color: 'white' }}>
<ThemedButton />
</ThemeContext.Provider>
);
}
export default MyApp;
在以上代码中,我们使用 createContext() 方法创建了一个名为 ThemeContext 的上下文对象,并将默认值设为 "light"。在 ThemedButton 组件中,我们通过调用 useContext() Hook 来获取 ThemeContext 上下文值,并将其应用到按钮样式。最后,在 MyApp 根组件中,我们将主题数据作为 Prop 值传递给了 ThemeContext.Provider,并将 ThemedButton 组件装置在该 Provider 下面。
通过上述代码示例,我们可以看到如何使用 React Hook 来简化组件间状态共享、数据请求等任务。除了 useEffect() 和 useContext() 之外,React Hook 还提供了许多实用的 Hook,如 useState()、useMemo()、useReducer() 等。建议在项目中大量使用它们,以便优化代码和提高开发效率。