Могу ли я определить функциональный компонент в теле другого функционального компонента в React? - PullRequest
3 голосов
/ 27 мая 2020

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

import * as React from "react";
import "./styles.css";

function UsualExample() {
  return <h1>This is the standard example…</h1>
}

export default function App() {
  const CustomComponent = (): JSX.Element => <h1>But can I do this?</h1>
  return (
    <div className="App">
      <UsualExample />
      <CustomComponent />
    </div>
  );
}

Кажется, он работает нормально, и я не вижу никаких немедленных побочных эффектов, но есть ли какая-то фундаментальная причина, по которой мы не должны определять функциональный компонент CustomComponent из другого компонента?

Пример CodeSandbox: https://codesandbox.io/s/dreamy-mestorf-6lvtd?file= / src / App.tsx: 0-342

Ответы [ 2 ]

5 голосов
/ 27 мая 2020

Это плохая идея. Каждый раз при рендеринге App создается новое определение CustomComponent. Он имеет ту же функциональность, но поскольку это другая ссылка, react нужно будет отключить старый и перемонтировать новый. Таким образом, вы будете вынуждены реагировать на дополнительную работу над каждым рендером, и вы также будете сбрасывать любое состояние внутри CustomComponent.

Вместо этого компоненты должны быть объявлены сами по себе, а не внутри рендеринга, чтобы они создаются только один раз, а затем используются повторно. При необходимости вы можете заставить компонент принимать реквизиты, чтобы настроить его поведение:

const CustomComponent = (): JSX.Element => <h1>But can I do this?</h1>

export default function App() {
  return (
    <div className="App">
      <UsualExample />
      <CustomComponent />
    </div>
  );
}

Иногда вы можете делать что-то повторяющееся внутри одного компонента и хотите упростить код, используя вспомогательную функцию. Это нормально, но тогда вам нужно будет вызвать его как функцию, а не визуализировать как компонент.

export default function App() {
  const customCode = (): JSX.Element => <h1>But can I do this?</h1>
  return (
    <div className="App">
      {customCode()}
      <UsualExample />
      {customCode()}
    </div>
  );
}

При таком подходе response будет сравнивать <h1> с <h1>, и поэтому его не нужно повторно монтировать.

3 голосов
/ 27 мая 2020

Это не только плохая идея, это ужасная идея. Вы еще не открыли для себя композицию (это нормально, это займет некоторое время, вы доберетесь до этого).

Единственная причина, по которой вы хотите объявить компонент внутри другого, - это закрыть prop (возможно, некоторые state тоже, возможно), которые вы хотите захватить в дочернем компоненте - вот трюк - передайте его в качестве опоры новому компоненту, и затем вы сможете объявить новый компонент НАРУЖУ.

Превратите это:

function UsualExample() {
  return <h1>This is the standard example…</h1>
}

export default function App({someProp}) {
  // the only reason you're declaring it in here is because this component needs "something" that's available in <App/> - in this case, it's someProp
  const CustomComponent = (): JSX.Element => <h1>I'm rendering {someProp}</h1>
  return (
    <div className="App">
      <UsualExample />
      <CustomComponent />
    </div>
  );
}

В это:

function UsualExample() {
  return <h1>This is the standard example…</h1>
}

const CustomComponent = ({someProp}) => <h1>I'm rendering {someProp}></h1>

export default function App({someProp}) {
  return (
    <div className="App">
      <UsualExample />
      { /* but all you have to do is pass it as a prop and now you can declare your custom component outside */ }
      <CustomComponent someProp={someProp} />
    </div>
  );
}
...