在 react 项目开发中,不同的前端开发者编码习惯也不相同,在 react16.8 hooks 全面转向函数式编程之后少了类的约束,使得开发者更加自由,各种写法和设计模式都有,其中就包括标题所说的函数返回 jsx 和函数式组件两种不同的写法:
const App = () => {
// 普通函数
const normalFn = () => {
return <div>普通函数返回jsx</div>;
};
// 函数式组件
const FnComp = () => {
return <div>函数式组件</div>;
};
return (
<div>
{normalFn()}
<FnComp />
</div>
);
};
export default App;
这两种写法在实际开发中都比较常见,但是这两者有何区别与联系呢,会存在什么坑呢?
- 两者都能接收
props,返回一个 jsx - 普通函数返回 jsx, 没有自身状态,不能在里面使用 hooks,使用上要调用
normalFn() - 函数式组件是标准的 react 组件,首字母必须要大写,使用上要写成
<FnComp />这样,最关键的是可以使用 hooks、有自己的状态和生命周期。
如果只是单纯返回 jsx 复用组件的话,两者其实效果都一样,但是在开发中随着需求变动,无状态组件的可能需要添加状态,尤其是普通函数返回了其他的库封装的组件,这时普通函数的写法就会出问题:
// 差不多的代码 ,添加了antd组件的使用
import { useState } from 'react';
import { Select } from 'antd';
const { Option } = Select;
const App = () => {
const [state, setstate] = useState(0);
const normalFn = () => {
function handleChange1(value: any) {
console.log(`selected ${value}`);
}
return (
<div>
<Select style={{ width: 120 }} onChange={handleChange1}>
<Option value="Jack111">Jack111</Option>
<Option value="Lucy111">Lucy111</Option>
</Select>
</div>
);
};
const FnComp = () => {
function handleChange2(value: any) {
console.log(`selected ${value}`);
}
return (
<div>
<Select style={{ width: 120 }} onChange={handleChange2}>
<Option value="Jack222">Jack222</Option>
<Option value="Lucy222">Lucy222</Option>
</Select>
</div>
);
};
return (
<div>
{normalFn()}
<FnComp />
<button
onClick={() => {
setstate(state + 1);
}}
>
add
</button>
<p>{state}</p>
</div>
);
};
export default App;
上述代码非常简单,普通函数normalFn 和函数式组件FnComp 都返回了同样的 Select 组件,同时App组件添加了useState的逻辑,点击按钮触发状态变动。
此时分别修改两个下拉选项,再点击按钮,两者出现表现不一致的情况,普通函数渲染出的Select保存之前的选项,函数式组件则重新渲染重置为空值,为何会存在这样的问题?
- 点击按钮之后 state 变动,
App组件重新渲染,也就是这个 App 函数重新执行 - 函数式组件
FnComp销毁并重新渲染,里面可能存在的状态或者函数也销毁重置,因此Select的值也重置为空 - 普通函数
normalFn返回了一个antd的Select组件, 从源码来看 实际上使用的是react-component/select 这个组件, 这个组件在内部实现上使用了React.useState点此查看源码, 因此相当于在普通函数内部使用了 hooks,这是导致差异出现的根本原因。
最后总结一下
- 如果是特别简单的逻辑或者确定你返回的组件没有内部状态,那么在开发中可以使用普通的函数去封装代码。
- 而当你的组件存在内部状态,或者需要缓存优化性能时,只能使用函数式组件。
- 总之为了以后不去苦逼的改代码,尽量函数式组件。