背景
年底了,换了项目组,新的项目组使用react,从vue到react,我只花了一天的时间,看了官方简单的文章之后,就觉得这玩意很简单啊,比起vue的那么api来说,这根本没有学习成本好吧,十分迅速的就进入了react的项目开发,并且洋洋得意,根据我多年的经验来看,这波肯定会得到领导的赏识
很快,我就做完了我的需求,把代码提交上去,组长可能确实比较闲,还review了我的代码,并且指出了一系列的问题,并告诉我说学习react最难的部分,并不是知道怎么使用它,而是要知道怎么能够编写良好,干净的react代码
主要给我提了六点错误,我相信在座的各位,可能需要对号入座
在不需要使用state的时候使用state
涉及到项目中的代码逻辑,我们将一些内容通过demo的形式展示出来
提交表单在很多场景下都需要用到,对于一些表单的提交,大多数人的代码的实现可能是以下方式
export default function App() {
const [name, setName] = useState("");
const submit = (e) => {
e.preventDefault();
console.log(name);
};
const change = (e) => {
setName(e.target.value);
};
return (
<div className="container">
<form onSubmit={submit}>
<label htmlFor="name">姓名: </label>
<input value={name} onChange={change} type="text" id="name" />
</form>
</div>
);
}
页面上有一个姓名输入框,通过state的方式将数据绑定,提交的时候从state上再把数据取到,这一点确实很像vue的双向绑定,通过state的方式实现了,看着表面没有问题,并且页面也呈现了,submit的数据也取到了
但是实际上,我们并没有在别的地方使用这个name状态,除了在提交的时候,有人会说,value也用到了,但是实际上你是可以不需要value这个字段的,只有提交的时候才会用到这个数据,所以这里完全可以不使用state,防止组件刷新
只需要通过ref改一下即可
export default function App() {
const nameRef = useRef(null);
const submit = (e) => {
e.preventDefault();
console.log(nameRef.current.value);
};
return (
<div className="container">
<form>
<label htmlFor="name">姓名: </label>
<input ref={nameRef} type="text" id="name" />
</form>
<button onClick={submit}>提交</button>
</div>
);
}
和之前一样,我们点提交按钮的时候获取到了最新数据,并且页面没有多次刷新
useState的回调函数
那什么情况下使用useState呢?这种在页面上呈现的内容需要使用,比如一个计数器
export default function App() {
const [count, setCount] = useState(0);
const submit = (val) => {
setCount(count + val);
};
return (
<div className="container">
<button onClick={() => submit(-1)}>-1</button>
<span>{count}</span>
<button onClick={() => submit(1)}>+1</button>
</div>
);
}
表面看着没有问题,点击加减也都挺正常的,但是如果你是熟悉useState的话,你也会给setCount传递一个函数的形式,这两者表现形式似乎完全一样,感受不到区别
const submit = (val) => {
setCount((current) => current + val);
};
那具体的区别在哪呢?在不使用回调函数的时候,如果我们连续更新状态的话,像下面这样
setCount(count + val)
setCount(count + val)
实际上页面也只会加一次,因为在这一次的更新过程中,count的值是固定的,也就是我们常说的setState是异步的原因(当你更改状态的时候,它不会立刻更新,而是等到下一次render才会更新),并且react会将state进行批处理,但是如果是函数的形式
const submit = (val) => {
setCount((current) => current + val);
setCount((current) => current + val);
};
就能够得到想要的结果,所以如果你想使用之前的状态来进行state值的修改,最好使用函数形式
state异步更新,useEffect的使用
通过上一个count,我们知道我们立刻获取count值的时候获取到的不是最新值
const submit = (val) => {
setCount((current) => current + val);
console.log(count);
};
这时候有人就想到了useEffect,如此就获取到了新的值
useEffect(() => {
console.log(count)
}, [count])
然后对于这一点,很多同学就将useEffect当成了vue中的watch,实现了下面的逻辑
export default function App() {
const [user, setUser] = useState("");
const [name, setName] = useState("");
const [userName, setUserName] = useState("");
useEffect(() => {
setUserName(user + name);
}, [user, name]);
return (
<div className="container">
<input
type="text"
value={user}
onChange={(e) => setUser(e.target.value)}
/>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<span>{userName}</span>
</div>
);
}
咋一看好像没什么问题,并且实现了对应的功能,但是它不是最佳方式
实际完全可以不用userName这个state,直接在dom中<span>{user + name}</span>
就能实现对应的效果
有时候很多同学也会使用useEffect进行下面的操作
useEffect(() => {
fetch('#').then(d => setData(d))
}, [])
useEffect(() => {
console.log(d)
}, [d])
那为什么你不把console.log的逻辑放在.then里面呢?
常见useEffect错误
const [user, setUser] = useState("");
const person = {
user
}
useEffect(() => {
console.log(person)
}, [person])
这种情况,大多数会认为useEffect会在组件初始化的时候执行一次,但实际上useEffect中的回调会执行多次,因为person是个引用类型,每次的指针地址都是变化的
这个时候你可以使用useMemo来解决这个问题
const person = useMemo(() => ({
user
}), [user])
依赖问题
由于业务很复杂,所以在不断迭代的过程中,我们的effect通常就会变成这种情况
useEffect(() => {
......
}, [person, name, age, id, status, address ....])
依赖会越来越多,稍微改其中的一个点,就会执行effect大片的逻辑,这里最好能够拆分或者合并,确定要执行一个逻辑的,最好放在一个state中,比如可以将age,name,id,等基础信息放在一个state,然后这个effect依赖这一个state就可以了,进行state合并
总结
上述就是一些基础react使用者常出现的一些问题,hook确实能给我们带来很大的便利,但是有时候从vue到react,其中的一些思想还是需要做一些调整,才能更好的适应hooks的方式,我们可以多看看好的一些hooks的封装,加深一些hooks的理解,也欢迎大家关注公众号【FE情报局】,一起讨论一些技术文章
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。