useRef 不动时,引用它
比如一个表单获取文本框的值,可以用useState,可以用useRef。如果不涉及赋值,也就是不涉及量的变化操作,用useRef更好。
import { useState } from 'react';
import './App.css';
const App = () => {
const [username, setUsername] = useState('');
return (
<>
<form>
<div>
<input
name='username'
onChange={(event) => {
setUsername(event.target.value);
}}
/>
</div>
<button
onClick={(event) => {
event.preventDefault();
console.log(username); //不建议的方式,因为每打入一个字母,就要setUserName一次
}}
>
GET USERNAME
</button>
</form>
</>
);
};
export default App;
import { useRef } from 'react';
import './App.css';
const App = () => {
const inputRef = useRef<HTMLInputElement>(null);
return (
<>
<form>
<div>
<input name='username' ref={inputRef} />
</div>
<button
onClick={(event) => {
event.preventDefault();
console.log(inputRef.current?.value); //更理想的获取方式,不管过程和变化,只要结果
}}
>
GET USERNAME
</button>
</form>
</>
);
};
export default App;
2. React.Memo 组件别乱动
我有一个页面App.tsx,页面上就俩东西,一个是按钮自增,一个是子组件叫大列表BigList.tsx

根据react默认的渲染规则,我App渲染时,子组件也重新渲染。也就是,我自增按钮每次点击,大列表也渲染了,大浪费了,完全没必要。代码大概是这样:
import { useState } from 'react';
import BigList from './BigList';
import './App.css';
const App = () => {
const [count, setCount] = useState(0);
return (
<>
<div className='countBlock'>{count}</div>
<button
onClick={() => {
setCount((count) => count + 1); //每次点击按钮,我庞大的列表也会重新渲染
}}
>
自增按钮
</button>
<div>
<BigList />
</div>
</>
);
};
export default App;
const BigList = () => {
const listData = ['庞大的列表', '庞大的列表'];
console.log('庞大的列表重新渲染了');
return (
<>
<ul style={{ border: '1px solid red' }}>
{listData.map((value) => (
<li>{value}</li>
))}
</ul>
</>
);
};
export default BigList;
现在我要做的就是,点击自增按钮时,大列表不渲染。这事特别简单,把BigList用React.Memo括一下。
import React from 'react';
const BigList = () => {
const listData = ['庞大的列表', '庞大的列表'];
console.log('庞大的列表重新渲染了');
return (
<>
<ul style={{ border: '1px solid red' }}>
{listData.map((value) => (
<li>{value}</li>
))}
</ul>
</>
);
};
export default React.memo(BigList); //如此简单就搞定了
React.memo告诉react,我这个组件如果没有内部变化,你不要重新渲染我。
但是,现实一般没有这么简单。上面这个BigList是个连props都没有的货,它里面的列表完全自给自足,这个情况在现实中不多。万一,万一它的列表数据是从props过来的,那就完犊子了。我们把listData移动到app里面,通过props传给它。
import { useState } from 'react';
import BigList from './BigList';
import './App.css';
const App = () => {
const [count, setCount] = useState(0);
const listData = ['庞大的列表', '庞大的列表']; //移动到这了
return(
// 其它没变省略了
<div>
<BigList listData={listData} /> //通过props传过去
</div>
);
// 其它没变省略了
};
export default App;
import React from 'react';
type BigListProps = {
listData: string[];
};
const BigList = (props: BigListProps) => {
console.log('庞大的列表重新渲染了');
return (
<>
<ul style={{ border: '1px solid red' }}>
{props.listData.map((value) => ( //通过props传过来的值进行渲染
<li>{value}</li>
))}
</ul>
</>
);
};
export default React.memo(BigList);
这时,我们会发现,React.memo无效了。原因的话,因为props毕竟是母组件传过来的,app.tsx重新渲染时,那个listData变量也重新生成了(虽然值没有变化,但数组或对象在内存中存储的地址不同),所以导致我们大列表组件内部变了,也就再次重新渲染了。
3. useMemo 变量别乱动
useMemo这个hook专门处理变量乱动的问题,简单把app.tsx里的listData改造下
const listData = useMemo(() => {
return ['庞大的列表', '庞大的列表'];
}, []);
成功!
但是还有一个闹心的场景,useMemo只能用在变量上,那如果母组件和子组件还有函数传递的话。。。
比如,我在它俩之间传递一个名叫ok的函数
const App = () => {
.....
const ok = () => {
console.log('ok');
};
return (
<>
.....
<div>
<BigList listData={listData} ok={ok} /> // 多传了一个ok
</div>
</>
);
};
import React from 'react';
type BigListProps = {
listData: string[];
ok: () => void;
};
const BigList = (props: BigListProps) => {
console.log('庞大的列表重新渲染了');
return (
<>
<ul style={{ border: '1px solid red' }}>
{props.listData.map((value) => (
<li>{value}</li>
))}
<div>
<button
onClick={() => {
props.ok(); //加了个按钮,专门执行传过来的ok
}}
>
OK BUTTON
</button>
</div>
</ul>
</>
);
};
export default React.memo(BigList);
再执行的话,又完了。
4. useCallback 函数别乱动
通过useCallback把app.tsx中ok函数改造下,就可以解决。
const ok = useCallback(() => {
console.log('ok');
}, []);
5. useCallback vs useMemo
如果你突发其想,非要用useMemo试一下,返回一个函数,也成功
const ok = useMemo(() => {
return () => {
console.log('ok');
};
}, []);
这时候,不懂事的你就要问了,它俩有啥区别呢?
经过一翻查询,我觉得这个问题,只能这么说:
useMemo更关注值,或者说结果。
useCallback更关注过程,或者说函数。
非要弄个例子的话,太难了,我也在迷茫中,我觉得不要过分追求,咱们还是保持平常心,爱咋咋地。

6. 这么好的东西,使劲用?
毕竟这些也是消耗资源的,肯定不是越多越好。
如果是,消耗资源的子列表,附属的弹窗、抽屉组件,真是没必要做多余渲染的,那用下还是好的,毕竟能够提高运行效率。
如果只是简单的一段文字,还要加代码改造,不用也罢。
