reactjs - Valoarea implicită a proprietății într-un component React folosind TypeScript
Nu reușesc să îmi dau seama cum să setezi valorile implicite ale proprietăților pentru componentele mele folosind Typescript.
Acesta este codul sursă:
class PageState
{
}
export class PageProps
{
foo: string = "bar";
}
export class PageComponent extends React.Component<PageProps, PageState>
{
public render(): JSX.Element
{
return (
<span>Hello, world</span>
);
}
}
Și când încerc să folosesc componenta astfel:
ReactDOM.render(<PageComponent />, document.getElementById("page"));
Primesc o eroare care spune că proprietatea foo lipsește. Vreau să folosesc valoarea implicită. Am încercat, de asemenea, să folosesc static defaultProps=... în interiorul componentului, dar nu a avut niciun efect, așa cum bănuiesc.
src/typescript/main.tsx(8,17): error TS2324: Property 'foo' is missing in type 'IntrinsicAttributes & IntrinsicClassAttributes<PageComponent> & PageProps & { children?: ReactEle...'.
Cum pot folosi valorile implicite ale proprietăților? Multe componente JS pe care le folosește compania mea se bazează pe ele și a nu le folosi nu este o opțiune.
Răspuns #1
Props implicite cu componenta de clasă
Utilizarea static defaultProps este corectă. De asemenea, ar trebui să folosiți interfețe, nu clase, pentru props și state.
Actualizare 2018/12/1: TypeScript a îmbunătățit verificarea tipurilor legată de defaultProps de-a lungul timpului. Citiți mai departe pentru cele mai recente și mai bune utilizări, până la utilizările și problemele mai vechi.
Pentru TypeScript 3.0 și versiuni ulterioare
TypeScript a adăugat în mod specific suport pentru defaultProps pentru a face ca verificarea tipurilor să funcționeze așa cum te-ai aștepta. Exemplu:
interface PageProps {
foo: string;
bar: string;
}
export class PageComponent extends React.Component<PageProps, {}> {
public static defaultProps = {
foo: "default"
};
public render(): JSX.Element {
return (
<span>Hello, { this.props.foo.toUpperCase() }</span>
);
}
}
Care poate fi redat și compilat fără a trece un atribut foo:
<PageComponent bar={ "hello" } />
Observați că:
foonu este marcat ca opțional (adicăfoo?: string), chiar dacă nu este obligatoriu ca atribut JSX. Marcarea ca opțional ar însemna că ar putea fiundefined, dar de fapt nu va fi niciodatăundefineddeoarecedefaultPropsoferă o valoare implicită. Gândește-te la asta asemănător cu modul în care poți marca un parametru al unei funcții ca opțional, sau cu o valoare implicită, dar nu amândouă, totuși ambele înseamnă că apelul nu trebuie să specifice o valoare. TypeScript 3.0+ trateazădefaultPropsîntr-un mod similar, ceea ce este foarte tare pentru utilizatorii React!defaultPropsnu are o adnotare de tip explicită. Tipul său este dedus și utilizat de compilator pentru a determina care atribute JSX sunt obligatorii. Ai putea utilizadefaultProps: Pick<PageProps, "foo">pentru a te asigura cădefaultPropscorespunde unui sub-set dePageProps. Mai multe despre această atenționare sunt explicate aici.- Aceasta necesită versiunea
@types/react16.4.11pentru a funcționa corect.
Pentru TypeScript 2.1 până la 3.0
Înainte ca TypeScript 3.0 să implementeze suportul compilerului pentru defaultProps, puteai totuși să îl folosești, și funcționa 100% cu React la runtime, dar deoarece TypeScript lua în considerare doar props atunci când verifica atributele JSX, trebuia să marchezi props care au valori implicite ca fiind opționale cu ?. Exemplu:
interface PageProps {
foo?: string;
bar: number;
}
export class PageComponent extends React.Component<PageProps, {}> {
public static defaultProps: Partial<PageProps> = {
foo: "default"
};
public render(): JSX.Element {
return (
<span>Hello, world</span>
);
}
}
Observați că:
- Este o idee bună să adnotezi
defaultPropscuPartial<>astfel încât să fie verificate tipurile în raport cu proprietățile tale, dar nu trebuie să furnizezi fiecare proprietate necesară cu o valoare implicită, ceea ce nu are sens, deoarece proprietățile necesare nu ar trebui să aibă niciodată nevoie de o valoare implicită. - Când se folosește
strictNullChecks, valoarea luithis.props.foova fiposibil nedefinităși va necesita o aserțiune non-null (adicăthis.props.foo!) sau o verificare de tip (adicăif (this.props.foo) ...) pentru a eliminaundefined. Acest lucru este enervant deoarece valoarea implicită a prop-ului înseamnă că de fapt nu va fi niciodată nedefinită, dar TS nu a înțeles acest flux. Aceasta este unul dintre principalele motive pentru care TS 3.0 a adăugat suport explicit pentrudefaultProps.
Înainte de TypeScript 2.1
Acest lucru funcționează la fel, dar nu ai tipuri Partial, așa că pur și simplu omit Partial<> și fie furnizezi valori implicite pentru toate proprietățile necesare (chiar dacă acele valori implicite nu vor fi niciodată folosite), fie omiți complet adnotarea explicită a tipului.
Props implicite cu componente funcționale
Poți folosi defaultProps și pe componentele funcție, dar trebuie să-ți tipizezi funcția la interfața FunctionComponent (StatelessComponent în @types/react înainte de versiunea 16.7.2) astfel încât TypeScript să știe despre defaultProps pe funcție:
interface PageProps {
foo?: string;
bar: number;
}
const PageComponent: FunctionComponent<PageProps> = (props) => {
return (
<span>Hello, {props.foo}, {props.bar}</span>
);
};
PageComponent.defaultProps = {
foo: "default"
};
Rețineți că nu trebuie să folosiți Partial<PageProps> nicăieri pentru că FunctionComponent.defaultProps este deja specificat ca un partial în TS 2.1+.
O altă alternativă plăcută (asta folosesc eu) este să destrucuturezi parametrii tăi props și să atribui valori implicite direct:
const PageComponent: FunctionComponent<PageProps> = ({foo = "default", bar}) => {
return (
<span>Hello, {foo}, {bar}</span>
);
};
Atunci nu ai nevoie deloc de defaultProps! Fii conștient că dacă o faci și oferi defaultProps la un component de tip funcție, acestea vor avea prioritate față de valorile parametrilor impliciți, deoarece React va trece întotdeauna explicit valorile din defaultProps (așadar, parametrii nu sunt niciodată undefined, deci parametrul implicit nu este niciodată folosit). Așadar, vei folosi unul sau altul, nu ambele.
Răspuns #2
Cu Typescript 2.1+, folosește Partial < T > în loc să faci proprietățile interfeței tale opționale.
export interface Props {
obj: Model,
a: boolean
b: boolean
}
public static defaultProps: Partial<Props> = {
a: true
};
Răspunsul #3
Componentă funcțională
De fapt, pentru componenta funcțională cea mai bună practică este următoarea, am creat un exemplu de componentă Spinner:
import React from 'react';
import { ActivityIndicator } from 'react-native';
import { colors } from 'helpers/theme';
export interface SpinnerProps {
color?: string;
size?: 'small' | 'large' | 1 | 0;
animating?: boolean;
hidesWhenStopped?: boolean;
}
const Spinner = ({
color = colors.primary,
size = 'small',
animating = true,
hidesWhenStopped = true,
}: SpinnerProps): JSX.Element => (
<ActivityIndicator
color={color}
size={size}
animating={animating}
hidesWhenStopped={hidesWhenStopped}
/>
);
export default Spinner;
Dacă componenta ta are children, ar fi mai bine să folosești React.FC, astfel:
export interface TypographyProps {
color?: string;
}
const Typography: React.FC<TypographyProps> = ({
children,
color,
}) => (
<span style={{ color }}>
{children}
</span>
);
export default Typography;
Răspunsul #4
Poți folosi operatorul spread pentru a reatribui props într-un component funcțional standard. Ceea ce îmi place la această abordare este că poți combina props obligatorii cu cele opționale care au o valoare implicită.
interface MyProps {
text: string;
optionalText?: string;
}
const defaultProps = {
optionalText = "foo";
}
const MyComponent = (props: MyProps) => {
props = { ...defaultProps, ...props }
}
Răspunsul #5
Cu Typescript 3.0 există o nouă soluție pentru această problemă:
export interface Props {
name: string;
}
export class Greet extends React.Component<Props> {
render() {
const { name } = this.props;
return <div>Hello ${name.toUpperCase()}!</div>;
}
static defaultProps = { name: "world"};
}
// Type-checks! No type assertions needed!
let el = <Greet />
Rețineți că pentru ca acest lucru să funcționeze aveți nevoie de o versiune mai nouă de @types/react decât 16.4.6. Funcționează cu 16.4.11.
