npm i use-immer immer -D
import { useState } from 'react';
import { useImmer } from 'use-immer';
let nextId = 3;
const initialList = [
{ id: 0, title: 'Big Bellies', seen: false },
{ id: 1, title: 'Lunar Landscape', seen: false },
{ id: 2, title: 'Terracotta Army', seen: true },
];
export default function BucketList() {
const [list, updateList] = useImmer(initialList);
function handleToggle(artworkId, nextSeen) {
updateList(draft => {
const artwork = draft.find(a =>
a.id === artworkId
);
artwork.seen = nextSeen;
});
}
return (
<>
<h1>Art Bucket List</h1>
<h2>My list of art to see:</h2>
<ItemList
artworks={list}
onToggle={handleToggle} />
</>
);
}
function ItemList({ artworks, onToggle }) {
return (
<ul>
{artworks.map(artwork => (
<li key={artwork.id}>
<label>
<input
type="checkbox"
checked={artwork.seen}
onChange={e => {
onToggle(
artwork.id,
e.target.checked
);
}}
/>
{artwork.title}
</label>
</li>
))}
</ul>
);
}
import { useState } from 'react';
export default function Form() {
const [person, setPerson] = useState({
name: 'Niki de Saint Phalle',
artwork: {
title: 'Blue Nana',
city: 'Hamburg',
image: 'https://i.imgur.com/Sd1AgUOm.jpg',
}
});
function handleNameChange(e) {
setPerson({
...person,
name: e.target.value
});
}
function handleTitleChange(e) {
setPerson({
...person,
artwork: {
...person.artwork,
title: e.target.value
}
});
}
function handleCityChange(e) {
setPerson({
...person,
artwork: {
...person.artwork,
city: e.target.value
}
});
}
function handleImageChange(e) {
setPerson({
...person,
artwork: {
...person.artwork,
image: e.target.value
}
});
}
return (
<>
<label>
Name:
<input
value={person.name}
onChange={handleNameChange}
/>
</label>
<label>
Title:
<input
value={person.artwork.title}
onChange={handleTitleChange}
/>
</label>
<label>
City:
<input
value={person.artwork.city}
onChange={handleCityChange}
/>
</label>
<label>
Image:
<input
value={person.artwork.image}
onChange={handleImageChange}
/>
</label>
<p>
<i>{person.artwork.title}</i>
{' by '}
{person.name}
<br />
(located in {person.artwork.city})
</p>
<img
src={person.artwork.image}
alt={person.artwork.title}
/>
</>
);
}
官网案例
react中使用useState
定义的状态变量,在单次更新循环中的更新其实是异步的,所以下面的setNum
两次更新后,num
还是等于1
。
代码如下:
const [num,setNum]=useState(0);
setNum(num+1);
setNum(num+1);
这时候我们可以使用更新函数的回调形式,来规避这个问题:
const [num,setNum]=useState(0);
setNum(prev=>prev+1);
setNum(prev=>prev+1);
上面 prev
是上次更新 num
后得到的值,每次拿到的都是最新的。
export default function Comp(){
const [dom,setDom]=useState(null);
return <span ref={(span) => setDom(span)}>div</span>
}
如果是html标签的ref,refDom.current
则直接返回的dom节点。
如果是组件,则返回的组件实例,内部含有forwardRef
、useImperativeHandle
暴露的对象或变量。
在jsx模版
文件中直接使用map
函数做渲染时,务必在map
下return
的最外层加上key
设置。
return {
arr.map((item) => {
return <div key={item["featureId"]}>["title"]}</div>;
})
}
数组和对象状态值的修改、新增、删除都不应该直接修改变量本身,而是应该在原来数据基础上重新生成一个对象然后使用赋值函数进行状态更新。
function Message({ messageColor }) {
const [color, setColor] = useState(messageColor);
这里,一个 color state
变量被初始化为 messageColor
的 prop
值。这段代码的问题在于,如果父组件稍后传递不同的 messageColor
值(例如,将其从 ‘blue’ 更改为 ‘red’),则 color state
变量将不会更新! state
仅在第一次渲染期间初始化。
互相引用的state变量,本质还是独立的,被引用的对象的更新不会导致引用对象的数据变量
const [items, setItems] = useState(initialItems);
const [selectedItem, setSelectedItem] = useState(
items[0]
);
setSelectedItem
例如:items[0]
的改变,不会导致selectedItem
的改变。
相同位置的相同组件,组件被销毁重新创建过程中,state会被保留。
对 React 来说重要的是组件在 UI 树中的位置,而不是在 JSX 中的位置!
https://react.docschina.org/learn/preserving-and-resetting-state
react之useRef和createRef
inputRef
ref={inputRef}
传递给子组件ref={ref}
子组件赋值import { forwardRef, useRef } from 'react';
const MyInput = forwardRef((props, ref) => {
return <input {...props} ref={ref} />;
});
export default function Form() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<>
<MyInput ref={inputRef} />
<button onClick={handleClick}>
聚焦输入框
</button>
</>
);
}
import {
forwardRef,
useRef,
useImperativeHandle
} from 'react';
const MyInput = forwardRef((props, ref) => {
const realInputRef = useRef(null);
useImperativeHandle(ref, () => ({
// 只暴露 focus,没有别的
focus() {
realInputRef.current.focus();
},
}));
return <input {...props} ref={realInputRef} />;
});
export default function Form() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<>
<MyInput ref={inputRef} />
<button onClick={handleClick}>
聚焦输入框
</button>
</>
);
}
默认情况下,我们在更改state之后,页面的更新是异步的。如果想要强制同步,我们可以使用react-dom
下的flushSync
。
const [text, setText] = useState('');
function handleAdd() {
flushSync(() => {
setText('');
});
console.log("text",text);//js戴拿,这里不会立即更新
}
const [count, setCount] = useState(0);
useEffect(() => {
setCount(count + 1);
});
每次渲染结束都会执行 Effect;而更新 state 会触发重新渲染。但是新一轮渲染时又会再次执行 Effect,然后 Effect 再次更新 state……如此周而复始,从而陷入死循环。
Effect 通常应该使组件与 外部 系统保持同步。如果没有外部系统,你只想根据其他状态调整一些状态,那么 你也许不需要 Effect。
useEffect(() => {
// 这里的代码会在每次渲染后执行
});
useEffect(() => {
// 这里的代码只会在组件挂载后执行
}, []);
useEffect(() => {
//这里的代码只会在每次渲染后,并且 a 或 b 的值与上次渲染不一致时执行
}, [a, b]);
useEffect(() => {
const node = ref.current;
node.style.opacity = 1; // 触发动画
return () => {
//这里一般放一些重置业务。组件销毁时,会先执行return中的业务,然后在挂在成功后,在执行useEffect中的业务。
//正常state更新导致的重新渲染,不会出发return中业务的执行。
node.style.opacity = 0; // 重置为初始值
};
}, []);
useEffectEvent
用于设置方法为非响应式,因此你不需要将它们指定为useEffect
依赖。
function ChatRoom({ roomId, onReceiveMessage }) {
const [messages, setMessages] = useState([]);
const onMessage = useEffectEvent(receivedMessage => {
onReceiveMessage(receivedMessage);
});
useEffect(() => {
const connection = createConnection();
connection.connect();
connection.on('message', (receivedMessage) => {
onMessage(receivedMessage);
});
return () => connection.disconnect();
}, [roomId]); // ✅ 所有依赖已声明
// ...
我们在书写自定义Hook时,需要注意一下几点:
use
开头官网案例:案例地址