Привет. Это перевод второй статьи из серии “Лучшие подходы и решения для уменьшения кода на React” автора Rahul Sharma.

Всего в данной серии 3 статьи, и найти их все вы можете по ссылкам ниже:

Не будем терять время, давайте продолжим рассмотри лучших подходов и решений для уменьшения кода. Мы начнем с простого примера.

Если в компонент не передаются дочерние элементы, используйте самозакрывающийся тэг.

Плохой код:

return <Component></Component>;

Хороший код:

return <Component />;

Не пишите функции внутри jsx элементов.

Плохой код:

return (
  <div>
    <button
      onClick={() => {
        setCount(1);
        // ...
      }}
    >
      Click
    </button>
  </div>
);

Хороший код:

const onClick = useCallback(() => {
  setCount(1);
  // ...
}, [deps]);

return (
  <div>
    <button onClick={onClick}>Click</button>
  </div>
);

Используйте объект в хуке useState, если вам необходимо обновлять несколько состояний приложения вместе.

Избегайте использование нескольких вызов setState подряд. Это стандартная ошибка, которая может привести к ненужным ререндерам. Лучше использовать один вызов.

Плохой код:

const [count, setCount] = useState(0);
const [name, setName] = useState("");

const onClick = () => {
  setTimeout(() => {
    setName("John");
    setCount(count + 1);
  }, 1000);
};

Хороший код:

const [state, setState] = useState({
  count: 0,
  name: "",
});

const onClick = () => {
  setTimeout(() => {
    setState((prevState) => ({
      ...prevState,
      name: "John",
      count: prevState.count + 1,
    }));
  }, 1000);
};

Примечание: React 18 добавил автоматическую группировку, так что реакт теперь сам решает проблему нескольких одновременных обновлений.


Используйте styled-components для стилей ваших компонентов. Это хороший выбор для избегания написания css в jsx и также убирает необходимость настройки css для приложения.

Это полностью основано на личном мнении

Плохой код:

return <div style={{ backgroundColor: "red" }}></div>;

Хороший код:

const Container = styled.div`
  background-color: ${({ theme }) => theme.colors.background};
  padding: 1rem;
`;

Лучший код:

const getPrimaryColor = ({ theme }) => theme.colors.primary;
const getDefaultColor = ({ theme }) => theme.colors.secondary;

const Button = styled.button`
  background-color: ${getPrimaryColor};
  color: ${getDefaultColor};
`;

Примечание: создайте функцию для получения цвета и других стилей из темы приложения и указывайте их при стилизации компонента. Это также поможет уменьшить код.


Старайтесь избегать классовых компонентов и используйте функциональные.

Плохой код:

class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
    this.onClick = this.onClick.bind(this);
  }

  onClick = () => {
    this.setState({
      count: this.state.count + 1,
    });
  };

  render() {
    return <button onClick>Click me</button>;
  }
}

Хороший код:

const Counter = () => {
  const [count, setCount] = useState(0);
  const onClick = () => setCount(count + 1);
  return <button>Click me</button>;
};

Примечание: функциональные компоненты не только уменьшают время написание кода, но также и уменьшают вес собранного приложения почти на 60%.


React.memo поможет избежать ненужные ререндеры.

Плохой код:

return (
  <ui>
    {items.map((item) => (
      <Component>{item}</Component>
    ))}
  </ui>
);

Хороший код:

const MemoComponent = React.memo(Component);
return (
  <ui>
    {items.map((item) => (
      <MemoComponent>{item}</MemoComponent>
    ))}
  </ui>
);

Примечание: Используйте React.memo() с умом. Не используйте useMemo когда компонент должен часто меняться из-за изменения пропсов.


Используйте JSX сокращения при передаче Boolean переменных.

Плохой код:

return <button disabled={true}>Submit</button>;

Хороший код:

return <button disabled>Submit</button>;

Используйте тернарный оператор вместо блока if-else.

Плохой код:

if (isLoading) {
  return <div>Loading...</div>;
} else {
  return <div>Data</div>;
}

Хороший код:

return isLoading ? <div>Loading...</div> : <div>Data</div>;

Используйте объекты вместо switch. Я уже упоминал это в прошлой статье для reducers.

Плохой код:

switch (props.type) {
  case "ADMIN":
    return <Admin />;
  case "USER":
    return <User />;
  default:
    return <NotFound />;
}

Хороший код:

const componentMap = {
  ADMIN: Admin,
  USER: User,
  NOT_FOUND: NotFound,
};

const Component = componentMap[props.type];
return <Component />;

Лучший код:

const componentMap = {
  ADMIN: React.lazy(() => import("../components/Admin")),
  USER: React.lazy(() => import("../components/User")),
  NOT_FOUND: React.lazy(() => import("../components/NotFound")),
};

const Component = componentMap[props.type];
return <Component />;

Используйте деструктуризацию вместо передачи нескольких пропсов по имени в компонент.

(от переводчика: спорный момент, для помощи в отладке приложения лучше передавать пропсы по отдельности)

Плохой код:

const { name, age, role } = props;
return (
  <>
    <Component name={name} age={age} role={role} />
  </>
);

Хороший код:

return (
  <>
    <Component {...props} />
  </>
);

Нет необходимости указывать фигурные скобки при передаче строчек в компонет.

Плохой код:

return <Component name={"John"} />;

Хороший код:

return <Component name="John" />;

Не называйте пропсы как встроенные названия у react элементов, к примеру className, style.

Плохой код:

return (
  <Component style="bordered">
);

Хороший код:

return (
  <Component variant="bordered">
);

Не используйте else часть, если if что-то возвращает.

Плохой код:

if (props.name) {
  return <div>{props.name}</div>;
} else {
  return <div>No name</div>;
}

Хороший код:

if (props.name) {
  return <div>{props.name}</div>;
}
return <div>No name</div>;

Используйте React.Fragment вместо создания обертки из div, span, и т.п.

Плохой код:

return (
  <div>
    <span>{props.name}</span>
    <span>{props.age}</span>
  </div>
);

Хороший код:

return (
  <>
    <span>{props.name}</span>
    <span>{props.age}</span>
  </>
);

Вы также можете передавать key проп в React.Fragment.

Плохой код:

return (
  <container>
    {list.map((item) => (
      <div key={item.id}>
        <SomeComponent />
        <SomeAnotherComponent />
      </div>
    ))}
  </container>
);

Хороший код:

return (
  <>
    {list.map((item) => (
      <React.Fragment key={item.id}>
        <SomeComponent />
        <SomeAnotherComponent />
      </React.Fragment>
    ))}
  </>
);

Спасибо за прочтение!

Комментарии (4)


  1. Sybe
    29.03.2022 01:23
    +3

    Пример, где используется React.memo, довольно обманчивый

    Мемоизация работает:

    const MemoComponent = React.memo(Component);
    
    return (
      <ui>
        {items.map((item) => (
          <MemoComponent>{item}</MemoComponent>
        ))}
      </ui>
    );


    Мемоизация НЕ работает:

    const MemoComponent = React.memo(Component);
    
    return (
      <ui>
        {items.map((item) => (
          <MemoComponent><span>{item}<span></MemoComponent>
        ))}
      </ui>
    );


    Кейс, когда в качестве children в компонент прокидывается примитивный тип, как мне кажется, довольно редкий, чаще всё-таки рендерится какой-то кастомный компонент, куда item передаётся через props в том или ином виде – поэтому стоит помнить, что тогда мемоизация не будет работать.


    1. asnow
      29.03.2022 15:58

      Полностью согласен. Про тернарки вообще вкусовщина. По мне jsx с тернарками это полное извращение


  1. ToTheHit
    29.03.2022 09:12

    Был бы намного познавательнее формат «делай вот так, потому что в этом есть вот такие плюсы», как немного в пункте про классовые и функциональные компоненты. Сейчас это выглядит как информация, взятая с потолка, т.е. без объяснения причин.

    P.S. Если что я не имею в виду, что все эти советы ненужные) Спасибо за перевод!)


  1. azizoid
    31.03.2022 15:45

    Я так понимаю, единственный минус использования css/scss/less - это то что придёться его отдельно настраивать, например, в webpack?