[英]How to handle React components that have intensive logic?
Hey guys!大家好!
Recently I've been noticing some very logic-intensive react components being created in the application I work in, and this components usually have dozens of line that I call "setup logic" (variables that needs to be set before I render my component).最近我注意到在我工作的应用程序中创建了一些非常逻辑密集型的反应组件,这些组件通常有几十行,我称之为“设置逻辑”(在我呈现我的组件之前需要设置的变量) . Some of this stuff can be:其中一些东西可以是:
This begs the question of "Are we doing too much in these components?"这就引出了一个问题:“我们在这些组件中做得太多了吗?”
Undoubtedly yes, but how exactly do we break this apart?毫无疑问是的,但我们究竟如何将其分开? Where do we draw the line?我们在哪里画线?
Ah, and a heads up, this is our setup currently:啊,请注意,这是我们目前的设置:
Stack info:堆栈信息:
Frontend: React前端:反应
API: GraphQL接口:GraphQL
Separating the setup logic (the getters, mappers and queries) from the presentation logic (the JSX).将设置逻辑(getter、mapper 和查询)与表示逻辑(JSX)分开。
Question 1. Should there be a component that's sole responsibility is to serve the presentation component with its logic?问题 1.是否应该有一个组件的唯一职责是为呈现组件及其逻辑提供服务?
Question 2. Should this be a component at all or should this logic be served by the graphQL API?问题 2.这应该是一个组件还是应该由 graphQL API 提供这个逻辑? How coupled should the GraphQL be to the React component? GraphQL 应该如何与 React 组件耦合?
This is an example that illustrates the issue:这是一个说明问题的示例:
// 30 lines of *import*
// ...
const MyForm = ({
onSubmit,
initialValues,
children,
loading,
isCreating,
cycleId,
permissions,
otherPermissions,
showWeightBalance,
balance,
}) => {
const { t } = useTranslation();
const currentUser = useCurrentUser();
const cycle = useCycle({ id: cycleId });
const canUpdateFields =
isCreating || (permissions && permissions.update);
const canUpdateContributors =
isCreating ||
(permissions && permissions.updateContributors);
const canReassignResponsible =
isCreating ||
(permissions && permissions.reassignResponsible);
const canUpdateWeight =
isCreating ||
(otherPermissions &&
otherPermissions.updateWeight);
const canShowContributorsInput =
isCreating ||
(permissions && permissions.showContributorsInputOnForm);
return (
<Form
initialValues={{
name: {},
description: {},
type: {
kind: kind.NUMBER,
direction: direction.ASC,
},
baseValue: null,
target: null,
unit: null,
weight: 1,
progressCalculus: false,
responsible: null,
contributors: [],
tasks: [],
scale: null,
...initialValues,
}}
onSubmit={onSubmit}
key={JSON.stringify(initialValues)} // This is made to reset the form when new initial values get loaded, should be enableReinitialize but richtexteditor wouldn't reset
>
{formProps => (
<Fragment>
<FormFieldTextTranslations
name="name"
label={t('yml_path')}
subtitle={t('yml_path')}
placeholder={t('yml_path')}
locales={locale.availableLocales()}
validate={[
requiredTranslation(t('yml_path')),
]}
disabled={loading || !canUpdateFields}
/>
<FormFieldRichTextTranslations
name="description"
label={t('yml_path')}
subtitle={t('yml_path')}
placeholder={t(
'yml_path',
)}
locales={locale.availableLocales()}
optional
hideToolbar
minimumLines={4}
/>
<FormFieldGroup
name="type"
label={t('yml_path')}
validate={[required('yml_path')]}
>
<Layout display="flex" flexWrap="wrap">
<FormFieldRadio
mr="px32"
mb={['px8', 'none']}
disabled={loading || !isCreating}
name="type"
value={{
kind: kind.NUMBER,
direction: direction.ASC,
}}
label={(checked, disabled, error) => (
<RadioCard
iconProps={{
iconName: 'chart-line',
solid: true,
fontSize: '20px',
}}
text={t(
'yml_path',
)}
checked={checked}
disabled={disabled}
error={error}
/>
)}
/>
<FormFieldRadio
mr="px32"
mb={['px8', 'none']}
disabled={loading || !isCreating}
name="type"
value={{
kind: kind.NUMBER,
direction: direction.DESC,
}}
label={(checked, disabled, error) => (
<RadioCard
iconProps={{
iconName: 'chart-line-down',
solid: true,
fontSize: '20px',
}}
text={t(
'yml_path',
)}
checked={checked}
disabled={disabled}
error={error}
/>
)}
/>
<FormFieldRadio
mr="px32"
mb={['px8', 'none']}
disabled={loading || !isCreating}
name="type"
value={{
kind: kind.KEEP,
direction: formProps.values.type.direction,
}}
label={(checked, disabled, error) => (
<RadioCard
iconProps={{
iconName: 'chart-keep',
solid: true,
fontSize: '20px',
}}
text={t('yml_path')}
checked={checked}
disabled={disabled}
error={error}
/>
)}
/>
<FormFieldRadio
mb={['px8', 'none']}
disabled={loading || !isCreating}
name="type"
value={{
kind: kind.BINARY,
direction: null,
}}
label={(checked, disabled, error) => (
<RadioCard
iconProps={{
iconName: 'check',
solid: true,
fontSize: '20px',
}}
text={t(
'yml_path',
)}
checked={checked}
disabled={disabled}
error={error}
tooltip={t(
'yml_path',
)}
/>
)}
/>
</Layout>
</FormFieldGroup>
{formProps.values.type.kind === kind.NUMBER &&
formProps.values.type.direction === direction.ASC && (
<AscendingForm
formProps={formProps}
loading={loading}
canUpdateFields={canUpdateFields}
canUpdateWeight={canUpdateWeight}
showWeightBalance={showWeightBalance}
balance={balance}
isCreating={isCreating}
/>
)}
{formProps.values.type.kind === kind.NUMBER &&
formProps.values.type.direction === direction.DESC && (
<DescendingForm
formProps={formProps}
loading={loading}
canUpdateFields={canUpdateFields}
canUpdateWeight={canUpdateWeight}
showWeightBalance={showWeightBalance}
balance={balance}
isCreating={isCreating}
/>
)}
{formProps.values.type.kind === kind.KEEP && (
<KeepForm
loading={loading}
canUpdateFields={canUpdateFields}
canUpdateWeight={canUpdateWeight}
showWeightBalance={showWeightBalance}
balance={balance}
isCreating={isCreating}
/>
)}
{formProps.values.type.kind === kind.BINARY && (
<BinaryForm
loading={loading}
canUpdateWeight={canUpdateWeight}
showWeightBalance={showWeightBalance}
balance={balance}
isCreating={isCreating}
/>
)}
{cycle &&
(cycle.allowCustomScore ||
cycle.allowScale) &&
formProps.values.type.kind !== kind.BINARY && (
<FormFieldGroup
name="progressCalculus"
label={t('yml_path')}
validate={[
required(t('yml_path')),
]}
>
<FormFieldRadio
name="progressCalculus"
mb="px4"
label={t(
'yml_path',
)}
disabled={loading || !isCreating}
value={false}
/>
{cycle.allowCustomScore && (
<FormFieldRadio
name="progressCalculus"
mb="px4"
label={t(
'yml_path',
)}
disabled={loading || !isCreating}
value={ProgressCalculusEnum.customScore}
/>
)}
{cycle.allowScoreScale && (
<Fragment>
<FormFieldRadio
name="progressCalculus"
mb="px4"
label={t(
'yml_path',
)}
disabled={loading || !isCreating}
value={ProgressCalculusEnum.scale}
/>
<ScaleRadioHelperNegativeMargin>
<FieldHelper
message={t(
'yml_path',
)}
iconName="info-circle"
/>
</ScaleRadioHelperNegativeMargin>
</Fragment>
)}
</FormFieldGroup>
)}
{isCreating &&
cycle &&
cycle.allowScoreScale &&
formProps.values.type.kind === kind.NUMBER &&
formProps.values.progressCalculus ===
ProgressCalculusEnum.scale && (
<ScoreScaleForm
name="scale.partitions"
formProps={formProps}
/>
)}
<FormFieldSelectContract
name="responsible"
label={t('yml_path')}
placeholder={t(
'yml_path',
)}
filter={{ active: true }}
validate={[
required(
t('yml_path'),
),
]}
disabled={loading || !canReassignResponsible}
allowClear
/>
<AssignToMeWrapper>
<Button
kind="primary"
size="adaptative"
appearance="text"
onMouseDown={() => {
setTimeout(
() =>
formProps.setFieldValue('responsible', {
key: currentUser.id,
label: currentUser.name,
}),
20,
);
}}
>
{t('assign_to_me')}
</Button>
</AssignToMeWrapper>
{canShowContributorsInput && (
<FormFieldSelectContract
name="contributors"
mode="multiple"
label={t('yml_path')}
placeholder={t(
'yml_path',
)}
filter={{ active: true }}
optional
disabled={loading || !canUpdateContributors}
/>
)}
{isCreating && (
<FormFieldGroup
name="tasks"
label={t('yml_path')}
optional
>
<TasksForm name="tasks" tasks={formProps.values.tasks} />
</FormFieldGroup>
)}
<Layout mt="px40">{children(formProps)}</Layout>
</Fragment>
)}
</Form>
);
};
export default MyForm;
您需要创建功能组件来分离重复的部分,以便您可以将其用作主组件中的元素,也可以重复使用它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.