React 最佳实践 (1)

组件

1、基本规则

  1. 一个文件声明一个组件
  2. 使用 JSX 语法
  3. 使用 Class、函数或 Hooks 声明组件,不使用 React.createElement

2、组件名

(1) 组件名称和定义该组件的文件名称尽量保持一致,名称尽量简短

// Good
import MyComponent from './MyComponent';

// Bad
import MyComponent from './MyComponent/index';

(2) 应在 class、function(箭头函数就是 const 关键字) 关键字或 声明后面直接声明组件名称,不要使用 displayName 来命名 React 模块

// Good
export default class MyComponent extends React.Component {}

const MyField = (props) => {
  return <></>;
};

// bad
export default React.createClass({
  displayName: 'MyComponent',
});

(3) 组件名称应全局唯一
很多业务都会有相同的文件命名,此时应在声明组件名称时保持全局唯一。一般是将业务链路名称全拼至组件声明

// Account/User/List.jsx

// Good
export default class AccountUserList extends React.Component {}

// Bad
export default class List extends React.Component {}

(4)高阶组件名

// bad
export default function withFoo(WrappedComponent) {
  return function WithFoo(props) {
    return <WrappedComponent {...props} foo />;
  };
}

// good
export default function withFoo(WrappedComponent) {
  function WithFoo(props) {
    return <WrappedComponent {...props} foo />;
  }

  const wrappedComponentName =
    WrappedComponent.displayName || WrappedComponent.name || 'Component';

  WithFoo.displayName = `withFoo(${wrappedComponentName})`;
  return WithFoo;
}

3、组件文件名

(1)组件文件后缀为 jsx、tsx包含 jsx 语法的应启用 jsx 后缀作为组件后缀
(2)组件文件名为 PascalCase 大驼峰格式

components/
|- MyComponents.jsx

(3) 组件文件夹名称和默认导出一个组件可以被拆分成多个组件编写的应建立目录,目录使用 PascalCase。建立 index.jsx 导出对象。

components/
|- MyComponent/
|----index.jsx
|----MyComponentList.jsx
|----MyComponentListItem.jsx

4、创建组件

组件有内部状态或是 refs 引用 使用 React.Component`

export default class ShowName extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: 'foo',
    };
  }
  render() {
    const { name } = this.state;
    return <div>{name}</div>;
  }
}

组件没有状态或 refs 可以使用函数组件

const ShowName = ({ name }) => <div>{name}</div>;

5、Hooks

组件中有状态或事件响应且运行更新逻辑清晰的才可以使用 Hooks,否则都应使用 Class

// Good
import React, { useState, useEffect } from 'react';
import { Modal, Form, Checkbox } from 'antd';

const AutoModal = (props) => {
  const { visible, formItemLayout, customerFlag, csFlag } = props;
  const [confirmLoading, setConfirmLoading] = useState(false);
  const [form] = Form.useForm();
  const [csFlagState, setCsFlagState] = useState(csFlag);
  const [customerFlagState, setCustomerFlagState] = useState(customerFlag);
  const handleFinish = async (values) => {
    try {
      setConfirmLoading(true);
      await handleOk(values);
    } catch (e) {
      console.error(e);
    } finally {
      setConfirmLoading(false);
    }
  };
  const handleOk = () => {
    form.submit();
  };
  const handleCancel = () => {};
  const handleCSFlagChange = (e) => {
    setCsFlagState(e.target.checked);
  };
  const handleCustomerFlagChange = (e) => {
    setCustomerFlagState(e.target.checked);
  };
  const checkFlag = () => {
    if (csFlagState || customerFlagState) {
      return Promise.resolve();
    }
    message.warning('服务客服或服务客户至少选一项');
    return Promise.reject(new Error('服务客服或服务客户至少选一项'));
  };

  useEffect(() => {
    // 使用visible防止出现useForm报错
    if (visible) {
      form.setFieldsValue({
        newTskId: modifyObj.tskId,
        creIdList: modifyObj.creIdList,
        custIdList: modifyObj.custIdList,
      });
    }
  }, [modifyObj, visible, form]);

  useEffect(() => {
    if (visible) {
      form.setFieldsValue({
        csFlag,
      });
      setCsFlagState(csFlag);
    }
  }, [csFlag, visible, form]);

  useEffect(() => {
    if (visible) {
      form.setFieldsValue({
        customeFlag: customerFlag,
      });
      setCustomerFlagState(customerFlag);
    }
  }, [customerFlag, visible, form]);
  return (
    <Modal
      forceRender
      title={title}
      visible={visible}
      onOk={handleOk}
      confirmLoading={confirmLoading}
      onCancel={handleCancel}
      destroyOnClose
    >
      <Form
        form={form}
        preserve="false"
        onFinish={handleFinish}
        layout="horizontal"
        {...formItemLayout}
      >
        <Form.Item
          {...formItemLayout}
          style={{ marginBottom: 0 }}
          label={
            <Form.Item
              noStyle
              name="csFlag"
              rules={[{ validator: checkFlag }]}
              validateTrigger="onSubmit"
              initialValue={csFlagState}
              valuePropName="checked"
            >
              <Checkbox onChange={handleCSFlagChange}>{csLabel}</Checkbox>
            </Form.Item>
          }
        ></Form.Item>
        <Form.Item
          {...formItemLayout}
          style={{ marginBottom: 0 }}
          label={
            <Form.Item
              noStyle
              name="customeFlag"
              rules={[{ validator: checkFlag }]}
              validateTrigger="onSubmit"
              initialValue={customerFlagState}
              valuePropName="checked"
            >
              <Checkbox onChange={handleCustomerFlagChange}>
                {customerLabel}
              </Checkbox>
            </Form.Item>
          }
        ></Form.Item>
      </Form>
    </Modal>
  );
};

export default AutoModal;

6、组件中的命名规则

  • 属性名称: 使用小驼峰命名
  • style 样式: 使用小驼峰命名的样式属性

7、组件样式

使用 CSS Modules 编写局部样式组件,因为其对象是作为对象在 jsx 中使用,样式应使用小驼峰命名`

.myWrapper {
  &-Text {
  }
}
import styles from 'style.less';

export default class MyComponent extends React.Component {
  render() {
    return (
      <div className={styles.myWrapper}>
        <p className={styles.myWrapperText}></p>
      </div>
    );
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值