avatarGithub

React开发者注意 - 这些常见错误你一定要避免

2023-04-08

最近code review做的比较多,发现团队内同学(包括我自己)会犯一些常见的错误。这些错误可能会导致代码低效、难以维护,甚至会导致隐藏的BUG。本文将介绍React开发中的几个常见错误,以及如何避免它们。话不多说,让我们开始吧!

错误一:通过useEffect + useState实现派生状态


function Test(props) {
const [isOdd, setIsOdd] = useState(false)
useEffect(() => {
setIsOdd(props.number % 2 === 1)
}, [props.number])
return <div>{isOdd ? 'Yes' : 'No' }</div>
}

这里的问题在于 useEffect 是在组件渲染后再执行的,而 setCount 又会导致组件重新渲染。所以这里其实多了一次没有必要的渲染。

解法也非常简单,如下


function Home(props) {
const isOdd = props.number % 2 === 1
return <div>{isOdd ? 'Yes' : 'No' }</div>
}

isOdd 状态是完全和props.number挂钩的,我们只需要基于props根据我们的逻辑去计算一个新的值就可以了。如果这个计算比较耗时,那我们还可以通过 useMemo 做缓存


function Home(props) {
const isOdd = useMemo(() => {
return someExpensiveCalculate(props.number)
}, [props.number])
return <div>{isOdd ? 'Yes' : 'No' }</div>
}

错误二:在组件内部定义组件


function Test() {
const [value, setValue] = useState('')
const Count = () => {
return <h1>count 1</h1>
}
return (
<div>
<input value={value} onChange={e => setValue(e.target.value)} />
<Count />
</div>
)
}

<Count />这么写咋一看好像没啥问题。我们给 <Count /> 加点逻辑


function Test() {
const [value, setValue] = useState('')
const Count = () => {
const [count, setCount] = useState(0);
return (
<div>
<h1>count {count}</h1>
<button onClick={() => setCount((c) => c + 1)}>add</button>
</div>
);
};
return (
<div>
<input value={value} onChange={e => setValue(e.target.value)} />
<Count />
</div>
)
}

可以多点击几次「add」按钮,然后在输入框里随意输入任意字符,就能看到count被重置为0了。

这就是在组件内部定义组件的问题了,每次渲染,Count 都是一个新组件,内部的状态也会重新初始化,所以count被重置为了0。

错误三:直接执行组件函数


function Input() {
const [value, setValue] = useState('')
return <input value={value} onChange={e => setValue(e.target.value)} />
}
function Test() {
return (
<div>
{Input()}
</div>
)
}

Input()这么调用函数运行起来好像没啥问题啊,那 Input()<Input /> 有啥区别呢?我们加一个按钮来控制这个输入框的显示和隐藏


function Input() {
const [value, setValue] = useState('')
return <input value={value} onChange={e => setValue(e.target.value)} />
}
function Test() {
const [visible, setVisible] = useState(true)
const toggleVisible = () => {
setVisible(v => !v)
}
return (
<div>
<button onClick={toggleVisible}>{visible ? 'hide input' : 'show input'}</button>
{visible && Input()}
</div>
)
}

点击「hide input」


Error
Rendered fewer hooks than expected. This may be caused by an accidental early return statement.

当我们直接执行组件函数时,等价于下面这段代码

input.jsx

function Test() {
const [visible, setVisible] = useState(true)
const toggleVisible = () => {
setVisible(v => !v)
}
return (
<div>
<button onClick={toggleVisible}>{visible ? 'hide input' : 'show input'}</button>
{visible && (() => {
const [value, setValue] = useState('')
return <input value={value} onChange={e => setValue(e.target.value)} />
}()}
</div>
)
}

Input()在这里并不是一个组件,只是一个函数的封装。所以


const [value, setValue] = useState('')

就变成了 Test 组件的第二个Hook。而 visible 状态变化时,第二个Hook就可能执行,也可能不执行。这就违反了React Hook的要求,从而导致报错~

这就是 Input()<Input /> 的区别, <Input /> 这种JSX的写法, 最终会转换成React.createElement的函数调用,也就是创建一个组件,所以会有自己的state和hook。

结语

当你开始使用React时,可能会遇到一些错误和挑战。但是,只要你能够认真学习和理解React的工作原理,遵循React最佳实践,避免常见的错误,你就能够编写出高质量、高效的React代码。希望这篇文章能够帮助你避免React开发中常见的错误,让你的React应用更加稳定和可靠。不断学习和实践,相信你会成为一名出色的React开发者!