Как провести рефакторинг трех компонентов, которые асинхронно загружают и отображают данные в один? - PullRequest
0 голосов
/ 24 января 2019

У меня есть следующий код TypeScript. Я упростила / удалила столько, сколько могла.

interface DataPullingPageState
{
  loading: boolean;
  displayedEntries: string[];
};

export class EntriesPageOne extends React.Component<{}, DataPullingPageState>
{
  constructor(props: any)
  {
    super(props);

    this.state = { loading: false, displayedEntries: [] };
  }

  async componentDidMount()
  {
    this.setState({ loading: true });

    const entries = await api.loadAll();

    this.setState({ loading: false, displayedEntries: entries });
  }

  render()
  {
    if (this.state.loading)
    {
      return <div>loading</div>;
    }
    else if (this.state.displayedEntries.length === 0)
    {
      return <div>nothing found</div>;
    }
    else
    {
      return this.state.displayedEntries.map((entry, i) => <div key={i}>{entry}</div>);
    }
  }
}

export class EntriesPageTwo extends React.Component<{}, DataPullingPageState>
{
  constructor(props: any)
  {
    super(props);

    this.state = { loading: false, displayedEntries: [] };
  }

  async componentDidMount()
  {
    this.setState({ loading: true });

    const param = "my param";
    const entries = await api.loadByStringParam(param);

    this.setState({ loading: false, displayedEntries: entries });
  }

  render()
  {
    if (this.state.loading)
    {
      return <div>loading</div>;
    }
    else if (this.state.displayedEntries.length === 0)
    {
      return <div>nothing found</div>;
    }
    else
    {
      return this.state.displayedEntries.map((entry, i) => <div key={i}>{entry}</div>);
    }
  }
}

export class EntriesPageThree extends React.Component<{}, DataPullingPageState>
{
  constructor(props: any)
  {
    super(props);

    this.state = { loading: false, displayedEntries: [] };
  }

  async componentDidMount()
  {
    this.setState({ loading: true });

    const param = 123;
    const entries = await api.loadByNumberParam(param);

    this.setState({ loading: false, displayedEntries: entries });
  }

  render()
  {
    if (this.state.loading)
    {
      return <div>loading</div>;
    }
    else if (this.state.displayedEntries.length === 0)
    {
      return <div>nothing found</div>;
    }
    else
    {
      return this.state.displayedEntries.map((entry, i) => <div key={i}>{entry}</div>);
    }
  }
}

Как вы можете видеть, это три разных компонента, которые отображаются одинаково, но имеют три разных способа их загрузки.

Я хотел бы знать, как я могу сделать только один компонент из этих трех. Я уже слышал о HoC, но не знаю, подходят ли они мне.

1 Ответ

0 голосов
/ 24 января 2019

Да, вы можете HoC, давайте немного упростим ваш код:

Метод HoC

class EntriesPage extends React.Component {
  // you don't need state for loading
  render() {
    const { loading, entries } = this.props
  }
}
EntriesPage.defaultProps = { loading: true, entries: [] }

const withEntries = (apiCall) => (Page) => {
  return async (props) => {
     const entries = await apiCall()
     <Page {...props} loading={false} entries={entries} />
  }
}

Теперь вы можете создать первую страницу следующим образом:

// PageOne
export default withEntries(api.loadAll)(EntriesPage)
// PageTwo
export default withEntries(() => api.loadByStringParam('param'))(EntriesPage)
// PageThree
export default withEntries(() => api.loadByNumberParam(123))(EntriesPage)

Это будетсоздайте HoC, который принимает метод динамической выборки и передает результат в качестве пропуска конечному компоненту.Надеюсь, что это поможет

Специальный метод с param в качестве prop

Вы даже можете выставить params для конечного компонента, изменив его на что-то вроде

const withEntries = (apiCall) => (Page) => {
  return async (props) => {
     const { fetchParam, ...rest } = props
     const entries = await apiCall(fetchParam)
     <Page {...rest} loading={false} entries={entries} />
  }
}

// EntriesPageComposed.tsx
export default withEntries(api.loadByStringParam)(EntriesPage)

<EntriesPageComposed fetchParams={123} />

Компонент Loader

Или вы можете даже сделать его полностью изолированным без HoC и передать все как prop и сделать компонент «загрузчик данных», что является довольно распространенным шаблоном в приложениях React, который будет действовать только как загрузчик для подготовки следующих реквизитов.

const ComposedComponent = async (props) => {
  const { fetchMethod, fetchParam, ...rest } = props
  const entries = await fetchMethod(fetchParam)

  return (
    <EntriesPage {...rest} loading={false} entries={entries} />
  )
}


<ComposedComponent fetchMethod={api.loadByStringParam} fetchParam={'param'} />

Таким образом, у вас есть начальная реализация изолированной, и вы можете добавлять новые методы выборки на лету, просто передав пропеллер.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...