useState
使用状态响应输入
import { useState } from 'react';
export default function Form() {
const [answer, setAnswer] = useState('');
const [error, setError] = useState(null);
const [status, setStatus] = useState('typing');
if (status === 'success') {
return <h1>答对了!</h1>
}
async function handleSubmit(e) {
e.preventDefault();
setStatus('submitting');
try {
await submitForm(answer);
setStatus('success');
} catch (err) {
setStatus('typing');
setError(err);
}
}
function handleTextareaChange(e) {
setAnswer(e.target.value);
}
return (
<>
<h2>城市测验</h2>
<p>
哪个城市有把空气变成饮用水的广告牌?
</p>
<form onSubmit={handleSubmit}>
<textarea
value={answer}
onChange={handleTextareaChange}
disabled={status === 'submitting'}
/>
<br />
<button disabled={
answer.length === 0 ||
status === 'submitting'
}>
提交
</button>
{error !== null &&
<p className="Error">
{error.message}
</p>
}
</form>
</>
);
}
function submitForm(answer) {
// 模拟接口请求
return new Promise((resolve, reject) => {
setTimeout(() => {
let shouldError = answer.toLowerCase() !== 'lima'
if (shouldError) {
reject(new Error('猜的不错,但答案不对。再试试看吧!'));
} else {
resolve();
}
}, 1500);
});
}
有时候,你希望两个组件的状态始终同步更改。要实现这一点,可以将相关 state 从这两个组件上移除,并把 state 放到它们的公共父级,再通过 props 将 state 传递给这两个组件。这被称为“状态提升”,这是编写 React 代码时常做的事。
useReducer
对于拥有许多状态更新逻辑的组件来说,过于分散的事件处理程序可能会令人不知所措。对于这种情况,你可以将组件的所有状态更新逻辑整合到一个外部函数中,这个函数叫作 reducer
**编写一个 reducer 函数 **
function tasksReducer(tasks, action) {
switch (action.type) {
case 'added': {
return [
...tasks,
{
id: action.id,
text: action.text,
done: false,
},
];
}
case 'changed': {
return tasks.map((t) => {
if (t.id === action.task.id) {
return action.task;
} else {
return t;
}
});
}
case 'deleted': {
return tasks.filter((t) => t.id !== action.id);
}
default: {
throw Error('未知 action: ' + action.type);
}
}
}
**在组件中使用 reducer **
从 React 中导入 useReducer Hook
替换掉之前的 useState,使用 useReducer
const [tasks, setTasks] = useState(initialTasks);
import { useReducer } from 'react';
import AddTask from './AddTask.js';
import TaskList from './TaskList.js';
export default function TaskApp() {
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);
function handleAddTask(text) {
dispatch({
type: 'added',
id: nextId++,
text: text,
});
}
function handleChangeTask(task) {
dispatch({
type: 'changed',
task: task,
});
}
function handleDeleteTask(taskId) {
dispatch({
type: 'deleted',
id: taskId,
});
useContext
使用 Context 深层传递参数
通常来说,你会通过 props 将信息从父组件传递到子组件。但是,如果你必须通过许多中间组件向下传递 props,或是在你应用中的许多组件需要相同的信息,传递 props 会变的十分冗长和不便。Context 允许父组件向其下层无论多深的任何组件提供信息,而无需通过 props 显式传递
1、创建context
LevelContext.js
import {createContext} from 'react'
export const LevelContext = createContext(0);
2、使用context
Heading.js
import {useContext} from 'react'
import {LevelContext} from './LevelContext.js';
export default function Heading({children}) {
const level = useContext(LevelContext);
switch(level) {
case 0:
throw Error('Heading 必须在Section内部')
case 1:
return <h1>{children}</h1>;
case 2:
return <h2>{children}</h2>;
case 3:
return <h3>{children}</h3>;
case 4:
return <h4>{children}</h4>;
case 5:
return <h5>{children}</h5>;
case 6:
return <h6>{children}</h6>;
default:
throw Error('未知的 level:' + level);
}
}
Section.js
import {useContext} from 'react'
import {LevelContect} from './LevelContext.js'
export default function Section({children}) {
const level = useContext(LevelContext);
return (
<section className="section">
<LevelContext.Provider value={level}>
{children}
</LevelContext.Provider>
</section>
)
}
App.js
import Heading from './Heading.js';
import Section from './Section.js';
export default function Page() {
return (
<Section>
<Heading>主标题</Heading>
<Section>
<Heading>副标题</Heading>
<Heading>副标题</Heading>
<Heading>副标题</Heading>
<Section>
<Heading>子标题</Heading>
<Heading>子标题</Heading>
<Heading>子标题</Heading>
<Section>
<Heading>子子标题</Heading>
<Heading>子子标题</Heading>
<Heading>子子标题</Heading>
</Section>
</Section>
</Section>
</Section>
);
}
useEffect
可以让你在函数组件中执行副作用操作
useEffect(setup, dependencies?)
类似React Component的生命周期。看做componentDidMount, componentDidUpdate和componentWillUnmount这三个函数的组合
第二个参数dependencies:
1、没有传值时,组件的初始化和更新都会执行该方法;
2、一个空数组时,只在初始化调用一次之后不再执行,相当于componentDidMount;
3、传入第二个参数,只有一个值,该值有变化就执行,有多个值的数组,会比较每一个值,有一个不相等就执行。 数组里的[]元素变化,useEffect才会被再次调用,组件更新渲染,数组元素没变化,它并不会重新触发。
import { useEffect } from 'react';
import { createConnection } from './chat.js';
function ChatRoom({ roomId }) {
const [serverUrl, setServerUrl] = useState('https://localhost:1234');
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [serverUrl, roomId]);
// ...
}
useRef
useRef 是一个 React Hook,它能让你引用一个不需要渲染的值。
const ref = useRef(initialValue)
参数
initialValue:ref 对象的current属性的初始值。可以是任意类型的值。这个参数会首次渲染后被忽略。
返回值
useRef 返回一个只有一个属性的对象:
current:最初,它被设置为你传递的initialValue。之后你可以把它设置为其他值。如果你把 ref 对象作为一个 JSX 节点的ref属性传递给 React,React 将为它设置current属性。
import { useRef } from 'react';
function MyComponent() {
const intervalRef = useRef(0);
const inputRef = useRef(null);
// ...
useRef 返回一个具有单个 current 属性 的 ref 对象,并初始化为你提供的 initial value
在后续的渲染中,useRef 将返回相同的对象。你可以改变它的 current 属性来存储信息,并在之后读取它。这会让你联想起 state,但是有一个重要的区别。
改变 ref 不会触发重新渲染。 这意味着 ref 是存储一些不影响组件视图输出的信息的完美选择。例如,如果你需要存储一个 intervalID 并在以后检索它,你可以把它放在一个 ref 中。如果要更新 ref 里面的值,你需要手动改变它的 current 属性:
function handleStartClick() {
const intervalId = setInterval(() => {
// ...
}, 1000);
intervalRef.current = intervalId;
}