utilizzando children
const Wrapper = ({children}) => (
<div>
<div>header</div>
<div>{children}</div>
<div>footer</div>
</div>
);
const App = ({name}) => <div>Hello {name}</div>;
const WrappedApp = ({name}) => (
<Wrapper>
<App name={name}/>
</Wrapper>
);
render(<WrappedApp name="toto"/>,node);
Questo è anche noto come transclusion
in Angolare.
children
è un oggetto speciale in React e conterrà ciò che è all'interno dei tag del tuo componente (qui <App name={name}/>
è dentro Wrapper
, quindi è ilchildren
Nota che non devi necessariamente usare children
, che è unico per un componente, e puoi usare anche oggetti di scena normali se vuoi, o mescolare oggetti di scena e bambini:
const AppLayout = ({header,footer,children}) => (
<div className="app">
<div className="header">{header}</div>
<div className="body">{children}</div>
<div className="footer">{footer}</div>
</div>
);
const appElement = (
<AppLayout
header={<div>header</div>}
footer={<div>footer</div>}
>
<div>body</div>
</AppLayout>
);
render(appElement,node);
Questo è semplice e va bene per molti casi d'uso, e lo consiglierei per la maggior parte delle app di consumo.
rendering oggetti di scena
È possibile passare le funzioni di rendering a un componente, questo modello viene generalmente chiamato render prop
e l' children
elica viene spesso utilizzata per fornire quel callback.
Questo modello non è pensato per il layout. Il componente wrapper viene generalmente utilizzato per conservare e gestire alcuni stati e iniettarli nelle sue funzioni di rendering.
Contro esempio:
const Counter = () => (
<State initial={0}>
{(val, set) => (
<div onClick={() => set(val + 1)}>
clicked {val} times
</div>
)}
</State>
);
Puoi avere ancora più fantasia e persino fornire un oggetto
<Promise promise={somePromise}>
{{
loading: () => <div>...</div>,
success: (data) => <div>{data.something}</div>,
error: (e) => <div>{e.message}</div>,
}}
</Promise>
Nota che non è necessario utilizzare necessariamente children
, è una questione di gusti / API.
<Promise
promise={somePromise}
renderLoading={() => <div>...</div>}
renderSuccess={(data) => <div>{data.something}</div>}
renderError={(e) => <div>{e.message}</div>}
/>
Ad oggi, molte biblioteche stanno utilizzando oggetti di rendering (contesto React, React-motion, Apollo ...) perché le persone tendono a trovare questa API più facile di quella HOC. reagire-powerplug è una raccolta di semplici componenti render-prop. reagire adotta ti aiuta a fare composizione.
Componenti di ordine superiore (HOC).
const wrapHOC = (WrappedComponent) => {
class Wrapper extends React.PureComponent {
render() {
return (
<div>
<div>header</div>
<div><WrappedComponent {...this.props}/></div>
<div>footer</div>
</div>
);
}
}
return Wrapper;
}
const App = ({name}) => <div>Hello {name}</div>;
const WrappedApp = wrapHOC(App);
render(<WrappedApp name="toto"/>,node);
Un componente / HOC di ordine superiore è generalmente una funzione che accetta un componente e restituisce un nuovo componente.
L'uso di un componente di ordine superiore può essere più efficace dell'uso children
o render props
, poiché il wrapper può avere la possibilità di cortocircuitare il rendering con un passo avanti shouldComponentUpdate
.
Qui stiamo usando PureComponent
. Quando si WrappedApp
esegue nuovamente il rendering dell'app, se il nome prop non cambia nel tempo, il wrapper ha la capacità di dire "Non ho bisogno di render perché i puntelli (in realtà, il nome) sono gli stessi di prima". Con la children
soluzione di base di cui sopra, anche se lo è il wrapper PureComponent
, non è così perché l'elemento children viene ricreato ogni volta che viene eseguito il rendering del genitore, il che significa che probabilmente il wrapper eseguirà nuovamente il rendering, anche se il componente wrapping è puro. C'è un plug-in babel che può aiutare a mitigarlo e garantire un children
elemento costante nel tempo.
Conclusione
I componenti di ordine superiore possono offrirti prestazioni migliori. Non è così complicato ma all'inizio sembra sicuramente ostile.
Non migrare l'intera base di codice in HOC dopo aver letto questo. Ricorda solo che su percorsi critici della tua app potresti voler usare HOC invece dei wrapper di runtime per motivi di prestazioni, in particolare se lo stesso wrapper viene usato molte volte vale la pena considerare di renderlo un HOC.
Redux ha inizialmente utilizzato un wrapper di runtime <Connect>
e successivamente è passato a un HOC connect(options)(Comp)
per motivi di prestazioni (per impostazione predefinita, il wrapper è puro e utilizzabile shouldComponentUpdate
). Questa è la perfetta illustrazione di ciò che volevo evidenziare in questa risposta.
Nota se un componente ha un'API render-prop, in genere è facile creare un HOC sopra di esso, quindi se sei un autore lib, dovresti scrivere prima un'API di rendering prop e infine offrire una versione HOC. Questo è ciò che Apollo fa con il <Query>
componente render-prop e l' graphql
HOC che lo utilizza.
Personalmente, utilizzo entrambi, ma in caso di dubbio preferisco gli HOC perché:
- È più idiomatico comporli (
compose(hoc1,hoc2)(Comp)
) rispetto al rendering di oggetti di scena
- Mi può dare prestazioni migliori
- Conosco questo stile di programmazione
Non esito a utilizzare / creare versioni HOC dei miei strumenti preferiti:
- Reagire di
Context.Consumer
comp
- di unstated
Subscribe
- usando
graphql
HOC di Apollo invece di Query
render prop
Secondo me, a volte render props rende il codice più leggibile, a volte meno ... Cerco di usare la soluzione più pragmatica secondo i vincoli che ho. A volte la leggibilità è più importante delle prestazioni, a volte no. Scegli saggiamente e non seguire in modo vincolante la tendenza del 2018 di convertire tutto in render-props.