【发布时间】:2020-02-19 12:07:08
【问题描述】:
模板问题
问题
大家好!
最近我注意到在我工作的应用程序中创建了一些逻辑密集型的反应组件,这些组件通常有几十行我称之为“设置逻辑”(需要在渲染之前设置的变量)我的组件)。其中一些内容可以是:
- 向翻译服务发送请求以检索该组件所需的翻译字符串
- 从状态创建派生数据,例如:
- 是否显示来自 API 的给定功能标志的组件
- 给定 API 收到的初始值和最终值的进度条百分比
- 获取当前用户
- 获取默认品牌颜色和字体大小
- 用户单击按钮时将执行的突变/查询
- 将所有这些信息映射到样式组件和子组件
这就引出了“我们在这些组件上做得太多了吗?”的问题
毫无疑问是的,但我们究竟如何将其分开?我们在哪里画线?
啊,请注意,这是我们目前的设置:
堆栈信息:
前端:反应
API:GraphQL
可能的解决方案
将设置逻辑(getter、mapper 和查询)与表示逻辑(JSX)分开。
问题 1。 是否应该有一个组件专门负责为其逻辑服务于表示组件?
问题 2。 这应该完全是一个组件,还是应该由 graphQL API 提供这个逻辑? GraphQL 与 React 组件的耦合度如何?
示例代码
这是一个说明问题的示例:
// 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;
【问题讨论】:
-
您可以创建可重用的功能组件来分离出重复的部分。
-
@Niraj 这是个好主意!只是想看看我是否理解,当您说
function components时,您的意思是functional components,还是还有其他我不知道的概念?干杯! -
是的,相同的功能组件只负责获取道具并在重新渲染时返回更新的组件。
标签: reactjs architecture refactoring