应该在哪里定义react函数——关于react开发规范的一些理解

应该在哪里定义react函数——关于react开发规范的一些理解

前言

我之前有一个同事,从vue技术栈转入react技术栈,对于函数式编程还有一些不太熟悉。

最近他问了我一个问题,react函数应该定义在什么地方?他发现有些函数写在react组件之内,有些却写在react函数之外。他想知道哪种做法是最合适的。这是一个很好的问题,因为这体现了对于代码规范的追求。现在我希望在此妥善的梳理一下这个问题。

看法

先开门见山的说一下我的看法,我的看法就是应外尽外,意思就是能够放到组件外部的函数都应该放到外部。但是需要注意的是,只有满足一定条件的函数才能够放到组件外部。在具体的讨论这些规则之前,我们应该先来看一下函数写在组件内与组件外部的区别

区别

其实两者的区别十分显著,我们在编写jsx文件的时候一般每个文件会暴露一个文件入口,也就是我们的组件函数。当我们在父组件使用我们的子组件的时候,实际上就相当于new出了一个新的组件实例。而在组件外的函数或者变量,则与组件形成了闭包。(不太了解闭包的可以理解为组件内的东西是原型模式,而组件外的东西是单例模式)

我们来看一个例子:

首先我们有一个Test组件:

1
2
3
4
5
6
7
8
9
10
11
12
let a = "123";

const Test = (props) => {
return(
<>
<button onClick={()=>{console.log(a);a = props.value;}}>change</button>
</>

)
}

export default Test;

然后我们在App.js中调用这个组件:

1
2
3
4
5
6
7
8
function App() {
return (
<div className="App">
<Test value={666}></Test>
<Test value={777}></Test>
</div>
);
}

可以看到我们在这里调用了两次Test组件,页面上应当有两个按钮。

这时我们应该想一下,当我们依次点击这两个按钮的时候输出的是

123 123

还是

123 666

呢?

答案显而易见,是123 666,各位可以自己去尝试一下。导致这种现象出现的原因其实不是react的组件,而是我们的esm。我们应该都对闭包有一定的概念,其实我们的模块化正是使用了闭包这一概念。当我们像上文那样编写Test组件的时候,其实闭包就已经形成了。

下面我们来梳理一下哪些函数是需要放到组件内的以及这其中的原因。

需要在组件内的函数

Hook函数

一个hook函数只能在以下两个位置定义:

  1. 自定义hook函数内部

  2. React组件内部

但是其实我们的自定义hook最终也是在React组件内部调用,所以这就相当于每一个Hook函数最终都必须在React组件内部环境中执行。

依赖Hook函数的函数

上文说过,hook函数只能在React组件内部调用,那么调用hook函数的函数也理所应当的需要写在组件内部。

依赖State的函数

如果我们的函数需要直接使用State的值,那么我们需要将这个函数放置在组件内部,因为state是每一个组件实例私有的,在外部的函数无法读取到这个值。但是对于可能会复用的函数我们还是更推荐大家使用参数传递的方式传递state的值然后再使用setState的方式设置返回值。

不推荐:

1
2
3
4
5
6
7
8
9
10
11
12
// :(   not recommend

// a.jsx
const [count,setCount] = useState(1);
function a(){
setCount(count+2);
}
// b.jsx
const [count,setCount] = useState(2);
function b(){
setCount(count+2);
}

推荐

1
2
3
4
5
6
7
8
9
10
11
12
// :)  recommend

//calcCount.js
function calcCount(count){
return count+2;
}
//a.jsx
const [count,setCount] = useState(1);
setCount(calcCount(count));
// b.jsx
const [count,setCount] = useState(2);
setCount(calcCount(count));

依赖props的函数

依赖props的函数当然也不能将其写在组件外面。我们很大程度上是通过props去区别某一个组件函数的不同实例的,所以props也是组件实例私有的,需要写在外面。

事件处理函数

虽然事件处理函数从理论上来说完全可以放置到组件外部(只要不依赖上面所说的),但是我们会认为事件处理函数是比较私人的东西,从习惯上来说我们还是更习惯将其放到组件的内部而不是抽出到一个util文件中,而且对于不同组件的事件处理函数,我们能抽象的部分也优先。

而且比起将事件处理函数抽象出来,我们更推荐将事件处理函数中处理逻辑的部分代码抽取出来,类似我们在MVC框架中所作的那样,model与controller的分离是值得推崇的设计思想。

其他依赖组件实例的方法、变量的函数

其实我们可以看到,凡是我们的函数依赖了我们组件实例的私有变量、方法,我们都需要放在组件内部。

为啥应外尽外?

以上我们整理出很多需要放到组件内部的情况,那为啥还推荐放到函数外部呢?直接全部放到函数内部不是会简单很多吗?

尽量将函数放到外面其实是一种函数式与模块化的设计思想,他驱使我们尽可能去复用我们的函数以及使用纯函数,综合来说,将函数与变量放到外部有如下几个好处:

  • 减少内存占用:减少组件内部的函数与变量可以减少组件实例中的内容,减少对内存的占用

  • 重用代码:我们完全可以将一些负责逻辑、可以重用的函数归类并且放置到方法类(utils.js之类)中,减少我们的代码量

  • 模块化:我们将逻辑与视图抽离完全可以减少我们组件的耦合度,并且可以降低模块体积,提高我们模块化的“效率”。而且能够使我们尽量去使用纯函数编写更优雅、更容易维护的代码

  • 共享信息:我们可以刻意的通过将数据放置到组件外让这个组件的不同实例去共享信息。不过需要注意的是,我们放到组件外的数据肯定不是React State,这就意味着它们不是响应式的(一般不会有人这么做吧?)

综上所述,对于能够放置到组件函数外的应该尽量放置到函数外,对于能够复用的代码片,最好是能够抽取成全局的函数,这样才更符合模块化的要求。


应该在哪里定义react函数——关于react开发规范的一些理解
2023/09/20/technology/react/where-put-react-func/
作者
charlesix59
发布于
2023年9月20日
许可协议