更喜欢内联事件处理程序以避免代码中的间接访问以提高人类可读性或使用处理程序函数?
Prefer inline event handler to avoid indirection in code to improve human readability or use handler function?
这是两个React组件源码编码风格(关注onSubmit
):
版本 1:
...
const ContactsEditPage = ({ match }) => {
...
const [updateContact] = useMutation(
gql`mutation updateContactById ($id: UUID!, $input: ContactPatch!) {
updateContactById(input: {id: $id, contactPatch: $input}) {
clientMutationId
}
}`
);
return (
...
<Formik
initialValues={data.contactById}
onSubmit={(values, actions) => {
updateContact({
variables: {
id: match.params.contactId,
input: values
}
})
.then(() => history.push('./'));
}}
>
<Form>
<FieldGroup>
<FieldRow>
<FieldLabel>Email:</FieldLabel>
<FieldInput
as={Field}
type='email'
name='email'
placeholder='Email'
/>
...
);
};
版本 2:
...
const ContactsEditPage = ({ match }) => {
...
const [updateContact] = useMutation(
gql`mutation updateContactById ($id: UUID!, $input: ContactPatch!) {
updateContactById(input: {id: $id, contactPatch: $input}) {
clientMutationId
}
}`
);
function handleContactSubmit(values) {
updateContact({
variables: {
id: match.params.contactId,
input: values
}
})
.then(() => history.push('./'))
}
return (
...
<Formik
initialValues={data.contactById}
onSubmit={handleContactSubmit}
>
<Form>
<FieldGroup>
<FieldRow>
<FieldLabel>Email:</FieldLabel>
<FieldInput
as={Field}
type='email'
name='email'
placeholder='Email'
/>
...
);
};
不同点:
- 版本 1 在事件处理程序属性
onSubmit
中使用内联 Javascript 代码
- 版本 2 使用
handleContactSubmit
函数代替内联 Javascript 代码
在我编码生涯的第一部分,我更喜欢使用处理函数(版本 2)。
现在我更喜欢内联代码(版本 1):
- 这段代码不太长
- 并且此代码仅使用一次
原因:与 avoid indirection in code to improve human readability(用于编码审查过程)相比,我更喜欢内联事件处理程序代码。
There is also a cost to use a function (inderection). When a new reader encounters this code, they need to jump between many function definitions in many files. This non-linear reading process requires more mental focus than reading linear code.
我遇到了大多数喜欢使用处理程序函数(在代码中产生间接)的 ReactJS 编码人员。
我的问题:
- 我是喜欢间接编码的少数程序员吗?
- 即使我的非线性阅读过程比阅读线性代码需要更多的精神集中,我是否必须接受多数偏好?并在我的项目中添加 ReactJS 样式指南中的处理函数规则使用?
注:
此致,
斯蒂芬
TL;DR 视情况和个人喜好而定。
-
Using an arrow function in render creates a new function each time the component renders, which may break optimizations based on strict identity comparison.
这仅适用于 class 组件,与功能组件无关。在功能组件中,函数在每个渲染器上都是 re-defined,无论它是定义为 function
、const myFunc = ...
还是 onClick={() => ...}
,因为它的范围是 component-function (这可以用 memo
解决,但这超出了你的问题范围)。
-
avoid indirection in code to improve human readability
IMO,我会说这取决于。有时,尤其是当查看 PR 或 returning 代码我有一段时间没见过了,我喜欢在组件的 prop 中看到一个函数名称,比如 onClick={doSomething}
因为那样我可以略过我不关心的内容专注于我需要为手头的任务做的事情。但是,更重要的是,它通过函数名称给出了正在发生的事情的上下文。
是的,你是对的,开发人员必须向下滚动以查看功能的使用位置,然后向上滚动以查看功能的作用,但如果我看到一个组件有多个按钮,每个按钮都有不同的处理程序,如下所示,我必须实际阅读每个处理程序,直到找到我想要更改的函数。如果将它们替换为函数名称,我可以很容易地理解“哦,这个按钮做这个,而另一个按钮做那个”,而不必向上滚动以找到函数声明。当函数很大或很复杂时,尤其如此; re-reading 一个没有名字的 30 行函数只是为了试图记住它的内容 does/why 当你 return 6 个月后你的代码时非常痛苦。
return (
<div>
<button onClick={() => ...}>Some text</button>
<button onClick={() => ...}>Some other text</button>
</div>
);
要考虑的另一件事是嵌套级别。如果你有一个有很多缩进的大渲染,然后你添加一个复杂的内联函数到道具,突然你必须在你的编辑器中滚动 left/right 这本质上是 re-introduces 你试图解决的原始问题。
-
Am I part of a coder minority?
不,内联和 separately-declared 处理程序都有用例。我在我的存储库中写,无论是在工作还是在工作之外。我的队友也一样。
如果处理程序很小并且不费吹灰之力就可以理解它的作用(例如onClick={() => location.hash = "/redirectedPage"}
),那么使用内联函数是完全合理的,并且可能更利于可读性(为什么要声明一个新函数redirectToPage
当它是 one-line 函数时?)。对于更复杂的函数或深层嵌套的组件,声明函数名称通常很有帮助。
还有其他因素可能会在这里发挥作用,例如保持代码库内的一致性、lint 规则等,但这些是我认为适用于特定调整之外的一些主要因素。
我的理念是编写可读性优化的代码,这就是为什么我尽量保持渲染/return 尽可能抽象,以后再回来更容易(用于调试、新功能...)和从 render / return 函数名称中快速了解函数的作用。
似乎是间接问题的问题是错误的问题,因为IMO:
- 您的文件和目录应该代表组件逻辑的一部分(大或小取决于您)and/or 应用程序,如果是这种情况,您自然会在右侧寻找正确的功能地方(命名应该/应该是明确的)。
- 这取决于您的编辑器(在我的例子中是 VScode),有不同的快捷方式可以将您带到 function/var/const 的定义......或者简单地通过 alt-click
- 写代码总是一个WIP,这就是为什么即使我不重复代码我更喜欢间接(version2)因为90%的时间函数会在不久的将来被修改,所以我更喜欢保留所有修改都集中在一个地方,而不是在渲染中。
- 当基本代码长大时,导航文件和目录不是一个选项,所以即使你使用内联样式你也会这样做。
- 更贴近React逻辑和源码:可复用组件
- 我个人更喜欢将函数定义与 return/painting/rendering...
保持在不同的位置
- 它/应该是你的团队/公司的一部分,同意它的编码风格。
您的问题:
Am I part of a coder minority who prefer indirection in code?
据我所知,在 React 世界中,版本 2 是首选和最常用的版本。
Do I have to accept majority preference even if my non-linear reading process requires more mental focus than reading linear code? And add handler function rule use in the ReactJS style guide in my project?
恕我直言,最重要的是团队及其速度。对我来说,这不是一种信仰,所以我最好的答案是选择一个更准确地 your/your 团队效率的答案。
TL;DR 如果不明显,请在范围基础上与您的团队达成一致。
这里已经发布了一些很好的答案,共识似乎是这取决于团队的偏好以及“objective”因素,例如:
- 如果是one-line函数,首选in-line
- 如果它被重用,更喜欢处理程序
但在您的示例中,处理程序不是上述任何一种,它执行多项操作并且比单个状态更新要复杂一些。有人可能会争辩说这种复杂性证明了使用处理程序是合理的,或者这种 non-reusability 证明了 in-line 样式是合理的;很难划清界线,因为可读性非常个人化,取决于开发人员的背景、个人喜好、IDE 等,详见
您的团队可能不同意 complex-enough-for-a-handler 的含义,但可以同意根据职能范围划定界线。例如:它是留在组件内(状态、表单验证、UI-changes)还是超出组件范围(localStorage、用户登录,在本例中为编程导航)?
scope-based 协议(无论您选择我描述的协议还是其他协议)比 complexity-based 协议更容易执行并且会更有效,因为 IDE 和偏好会发生变化, 编码风格应该保持不变。
这是两个React组件源码编码风格(关注onSubmit
):
版本 1:
...
const ContactsEditPage = ({ match }) => {
...
const [updateContact] = useMutation(
gql`mutation updateContactById ($id: UUID!, $input: ContactPatch!) {
updateContactById(input: {id: $id, contactPatch: $input}) {
clientMutationId
}
}`
);
return (
...
<Formik
initialValues={data.contactById}
onSubmit={(values, actions) => {
updateContact({
variables: {
id: match.params.contactId,
input: values
}
})
.then(() => history.push('./'));
}}
>
<Form>
<FieldGroup>
<FieldRow>
<FieldLabel>Email:</FieldLabel>
<FieldInput
as={Field}
type='email'
name='email'
placeholder='Email'
/>
...
);
};
版本 2:
...
const ContactsEditPage = ({ match }) => {
...
const [updateContact] = useMutation(
gql`mutation updateContactById ($id: UUID!, $input: ContactPatch!) {
updateContactById(input: {id: $id, contactPatch: $input}) {
clientMutationId
}
}`
);
function handleContactSubmit(values) {
updateContact({
variables: {
id: match.params.contactId,
input: values
}
})
.then(() => history.push('./'))
}
return (
...
<Formik
initialValues={data.contactById}
onSubmit={handleContactSubmit}
>
<Form>
<FieldGroup>
<FieldRow>
<FieldLabel>Email:</FieldLabel>
<FieldInput
as={Field}
type='email'
name='email'
placeholder='Email'
/>
...
);
};
不同点:
- 版本 1 在事件处理程序属性
onSubmit
中使用内联 Javascript 代码
- 版本 2 使用
handleContactSubmit
函数代替内联 Javascript 代码
在我编码生涯的第一部分,我更喜欢使用处理函数(版本 2)。
现在我更喜欢内联代码(版本 1):
- 这段代码不太长
- 并且此代码仅使用一次
原因:与 avoid indirection in code to improve human readability(用于编码审查过程)相比,我更喜欢内联事件处理程序代码。
There is also a cost to use a function (inderection). When a new reader encounters this code, they need to jump between many function definitions in many files. This non-linear reading process requires more mental focus than reading linear code.
我遇到了大多数喜欢使用处理程序函数(在代码中产生间接)的 ReactJS 编码人员。
我的问题:
- 我是喜欢间接编码的少数程序员吗?
- 即使我的非线性阅读过程比阅读线性代码需要更多的精神集中,我是否必须接受多数偏好?并在我的项目中添加 ReactJS 样式指南中的处理函数规则使用?
注:
此致,
斯蒂芬
TL;DR 视情况和个人喜好而定。
-
Using an arrow function in render creates a new function each time the component renders, which may break optimizations based on strict identity comparison.
这仅适用于 class 组件,与功能组件无关。在功能组件中,函数在每个渲染器上都是 re-defined,无论它是定义为 function
、const myFunc = ...
还是 onClick={() => ...}
,因为它的范围是 component-function (这可以用 memo
解决,但这超出了你的问题范围)。
-
avoid indirection in code to improve human readability
IMO,我会说这取决于。有时,尤其是当查看 PR 或 returning 代码我有一段时间没见过了,我喜欢在组件的 prop 中看到一个函数名称,比如 onClick={doSomething}
因为那样我可以略过我不关心的内容专注于我需要为手头的任务做的事情。但是,更重要的是,它通过函数名称给出了正在发生的事情的上下文。
是的,你是对的,开发人员必须向下滚动以查看功能的使用位置,然后向上滚动以查看功能的作用,但如果我看到一个组件有多个按钮,每个按钮都有不同的处理程序,如下所示,我必须实际阅读每个处理程序,直到找到我想要更改的函数。如果将它们替换为函数名称,我可以很容易地理解“哦,这个按钮做这个,而另一个按钮做那个”,而不必向上滚动以找到函数声明。当函数很大或很复杂时,尤其如此; re-reading 一个没有名字的 30 行函数只是为了试图记住它的内容 does/why 当你 return 6 个月后你的代码时非常痛苦。
return (
<div>
<button onClick={() => ...}>Some text</button>
<button onClick={() => ...}>Some other text</button>
</div>
);
要考虑的另一件事是嵌套级别。如果你有一个有很多缩进的大渲染,然后你添加一个复杂的内联函数到道具,突然你必须在你的编辑器中滚动 left/right 这本质上是 re-introduces 你试图解决的原始问题。
-
Am I part of a coder minority?
不,内联和 separately-declared 处理程序都有用例。我在我的存储库中写,无论是在工作还是在工作之外。我的队友也一样。
如果处理程序很小并且不费吹灰之力就可以理解它的作用(例如onClick={() => location.hash = "/redirectedPage"}
),那么使用内联函数是完全合理的,并且可能更利于可读性(为什么要声明一个新函数redirectToPage
当它是 one-line 函数时?)。对于更复杂的函数或深层嵌套的组件,声明函数名称通常很有帮助。
还有其他因素可能会在这里发挥作用,例如保持代码库内的一致性、lint 规则等,但这些是我认为适用于特定调整之外的一些主要因素。
我的理念是编写可读性优化的代码,这就是为什么我尽量保持渲染/return 尽可能抽象,以后再回来更容易(用于调试、新功能...)和从 render / return 函数名称中快速了解函数的作用。
似乎是间接问题的问题是错误的问题,因为IMO:
- 您的文件和目录应该代表组件逻辑的一部分(大或小取决于您)and/or 应用程序,如果是这种情况,您自然会在右侧寻找正确的功能地方(命名应该/应该是明确的)。
- 这取决于您的编辑器(在我的例子中是 VScode),有不同的快捷方式可以将您带到 function/var/const 的定义......或者简单地通过 alt-click
- 写代码总是一个WIP,这就是为什么即使我不重复代码我更喜欢间接(version2)因为90%的时间函数会在不久的将来被修改,所以我更喜欢保留所有修改都集中在一个地方,而不是在渲染中。
- 当基本代码长大时,导航文件和目录不是一个选项,所以即使你使用内联样式你也会这样做。
- 更贴近React逻辑和源码:可复用组件
- 我个人更喜欢将函数定义与 return/painting/rendering... 保持在不同的位置
- 它/应该是你的团队/公司的一部分,同意它的编码风格。
您的问题:
Am I part of a coder minority who prefer indirection in code?
据我所知,在 React 世界中,版本 2 是首选和最常用的版本。
Do I have to accept majority preference even if my non-linear reading process requires more mental focus than reading linear code? And add handler function rule use in the ReactJS style guide in my project?
恕我直言,最重要的是团队及其速度。对我来说,这不是一种信仰,所以我最好的答案是选择一个更准确地 your/your 团队效率的答案。
TL;DR 如果不明显,请在范围基础上与您的团队达成一致。
这里已经发布了一些很好的答案,共识似乎是这取决于团队的偏好以及“objective”因素,例如:
- 如果是one-line函数,首选in-line
- 如果它被重用,更喜欢处理程序
但在您的示例中,处理程序不是上述任何一种,它执行多项操作并且比单个状态更新要复杂一些。有人可能会争辩说这种复杂性证明了使用处理程序是合理的,或者这种 non-reusability 证明了 in-line 样式是合理的;很难划清界线,因为可读性非常个人化,取决于开发人员的背景、个人喜好、IDE 等,详见
您的团队可能不同意 complex-enough-for-a-handler 的含义,但可以同意根据职能范围划定界线。例如:它是留在组件内(状态、表单验证、UI-changes)还是超出组件范围(localStorage、用户登录,在本例中为编程导航)?
scope-based 协议(无论您选择我描述的协议还是其他协议)比 complexity-based 协议更容易执行并且会更有效,因为 IDE 和偏好会发生变化, 编码风格应该保持不变。