Typescript React - условный рендеринг с использованием состояний вместо проверки переменной по переменной - PullRequest
0 голосов
/ 17 апреля 2020

Я видел, как многие разработчики (включая меня) выполняли условный рендеринг, проверяя значение переменных следующим образом:

import React, { useState, useMemo } from 'react'

const Page = () => {
  const [data, setData] = useState<any[]>()

  if (!data) return 'Loading...'

  if (data.length === 0) return 'Empty'

  if (data && data.length > 0) {
    return data.map(item => item)
  }

  return null
}

Но я не думаю, что это достаточно явно говорит о состоянии страницы и о том, что он должен оказать. Так что я подумал, есть ли способ сделать что-то вроде этого:

import React, { useState, useMemo } from 'react'

enum PageStatus {
  loading,
  empty,
  withResults
}

const Page = () => {
  const [data, setData] = useState<any[]>()

  const status = useMemo(() => {
    if (!data) {
      return PageStatus.loading
    }

    if (data.length > 0) {
      return PageStatus.withResults
    }

    return PageStatus.empty
  }, [data])

  if (status === PageStatus.empty) {
    return 'Empty'
  }

  if (status === PageStatus.withResults) {
    return data.map(item => item)
  }

  return null
}

К сожалению, Typescript не распознает правильный тип данных в последнем выражении. Вы можете поиграть и увидеть ошибку здесь, на TS Playground . Возможно, есть способ привести / связать переменную во время выполнения, но я не нашел ничего связанного с этим.

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

Ответы [ 3 ]

1 голос
/ 17 апреля 2020

Проблема в том, что TypeScript не может связать правильный статус с результатами функции. Нам нужен тип объединения, который приравнивает тип состояния к тому, какие данные фактически возвращаются.

import React, { useState, useMemo } from 'react'

enum PageStatus {
  loading,
  empty,
  withResults
}

type StatusData<T> = {
  status: PageStatus.loading,
  data: undefined
} | {
  status: PageStatus.empty,
  data: []
} | {
  status: PageStatus.withResults,
  data: T[];
}

const Page = () => {
  const [data, setData] = useState<string[]>();   // just assuming string here

  const statusData = useMemo((): StatusData<string> => {
    if (!data) {
      return {
        status: PageStatus.loading,
        data
      }
    }

    if (data.length > 0) {
      return {
        status: PageStatus.withResults,
        data
      }
    }

    return {
      status: PageStatus.empty,
      data: [],
    }
  }, [data]);

  if (statusData.status === PageStatus.empty) {
    return 'Empty'
  }

  if (statusData.status === PageStatus.withResults) {
    return statusData.data.map(item => item)
  }

  return null
}

Детская площадка

0 голосов
/ 17 апреля 2020

Я думаю, что ваша проблема в том, что вы полагаетесь на Typescript, чтобы признать, что data определен вне функции, которая принимает функцию в качестве параметра, который определяет data (useMemo).

И та функция в первом параметре, которая определяет data, не запускается в среде, отличной от react - после нее нет (). Мы знаем, что он управляется фреймворком, потому что мы доверяем useMemo, а Typescript - нет. Typescript не знает о useMemo и зависимостях, поэтому у него нет возможности узнать, что он определен ... если только он не совершил глубокое погружение в код, которое, я думаю, выходит за рамки.

Это один из немногих случаев, когда я буду использовать data!.map(. Я стараюсь избегать этого, насколько это возможно, но Framework Magi c - это место, где я буду его использовать.

0 голосов
/ 17 апреля 2020

Вы не инициализируете никакими данными, поэтому выведенный тип будет any[] | undefined. Вместо использования типа any было бы лучше использовать тип, а также инициализировать его некоторыми данными. (возможно и пустой массив)

import React, { useState, useMemo } from 'react'

enum PageStatus {
  loading,
  empty,
  withResults
}

interface dummy {
  name: string
}
const Page = () => {
  const [data, setData] = useState<dummy[]>([])

  const status = useMemo(() => {
    if (!data) {
      return PageStatus.loading
    }

    if (data.length > 0) {
      return PageStatus.withResults
    }

    return PageStatus.empty
  }, [data])

  if (status === PageStatus.empty) {
    return 'Empty'
  }

  if (status === PageStatus.withResults) {
    return data.map(item => item)
  }

  return null
}

Ссылка на детская площадка

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