React: союз условного рендеринга и дискриминации - PullRequest
1 голос
/ 05 августа 2020

Цель

Упростите следующую ситуацию

// Simplify
loading ? <Loading/> : (
  <Fragment>
    ....
  </Fragment>
) 

// Into something like
<LoadingContent loading={loading}>
 {() => (
   <Fragment>
     ...
   </Fragment>
 )}
</LoadingContent>

Проблема

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

Текущее решение

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

<LoadingContent loading={loading}>
 {() => loading && (
   <Fragment>
     ...
   </Fragment>
 )}
</LoadingContent>

Из предложения:

Проблема, общий тип c должен быть указан и не может быть выведен из-за https://github.com/microsoft/TypeScript/issues/16597

interface LoadingDiscriminatedContentProps<
  A extends string,
  S,
  SS extends S,
  O extends { [P in A]: S }
> {
  object: O;
  attribute: A;
  discriminant: SS;
  children: (loaded: Extract<O, O & { [P in A]: SS }>) => React.ReactElement;
}

export function LoadingDiscriminatedContent<
  A extends string,
  S,
  SS extends S,
  O extends { [P in A]: S }
>({
  object,
  attribute,
  discriminant,
  children
}: LoadingDiscriminatedContentProps<A, S, SS, O>): React.ReactElement {
  if (object[attribute] === (discriminant as S)) {
    return children(object as Extract<O, O & { [P in A]: SS }>);
  }
  return <Loading />;
}

1 Ответ

1 голос
/ 05 августа 2020

Мне удалось это решить. Ключевым моментом было то, что LoadingContent вызывает дочерний обратный вызов с типом T & {loaded: true}.

LoadingContent.tsx:

interface Props<L extends string, T extends { [P in L]: boolean }> {
    value: T;
    loadedProp: L;
    children: (loaded: T & { [P in L]: true }) => React.ReactElement;
}

const LoadingContent = <L extends string, T extends { [P in L]: boolean }>({value, loadedProp, children}: Props<L, T>): React.ReactElement => {
    if (value[loadedProp]) {
        return children(value as any);
    }
    return <>Loading...</>;
}

Использование:

interface LoadedData {
    loaded: true;
    data: string;
}

interface LoadingData {
    loaded: false;
}

type Data = LoadedData | LoadingData;

const obj: Data = {loaded: true, data: ''};

const el = (
    <LoadingContent value={obj} loadedProp="loaded">
        {(obj) => (
            <>
                {obj.data /* here we can use obj.data*/}
            </>
        )}
    </LoadingContent>
);

Если ваш "загружен" свойство всегда одно и то же, можно опустить generi c L type

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