11// Copyright (c) Microsoft Corporation. All rights reserved.
22// Licensed under the MIT License.
33
4- import { ComponentRef , TemplateRef } from '@angular/core' ;
4+ import { ComponentRef , EmbeddedViewRef , TemplateRef } from '@angular/core' ;
55import * as React from 'react' ;
66import { CHILDREN_TO_APPEND_PROP , ReactContent , ReactContentProps } from '../renderer/react-content' ;
77
8+ export interface RenderPropContext < TContext extends object > {
9+ readonly render : ( context : TContext ) => JSX . Element ;
10+ }
11+
812function renderReactContent ( rootNodes : HTMLElement [ ] , additionalProps ?: ReactContentProps ) : JSX . Element {
913 return React . createElement ( ReactContent , {
1014 ...additionalProps ,
@@ -16,51 +20,71 @@ function renderReactContent(rootNodes: HTMLElement[], additionalProps?: ReactCon
1620 * Wrap a `TemplateRef` with a `JSX.Element`.
1721 *
1822 * @param templateRef The template to wrap
19- * @param context The context to pass to the template
2023 * @param additionalProps optional additional props to pass to the `ReactContent` object that will render the content.
2124 */
22- export function renderTemplate < TContext extends object > (
25+ export function createTemplateRenderer < TContext extends object > (
2326 templateRef : TemplateRef < TContext > ,
24- context ?: TContext ,
2527 additionalProps ?: ReactContentProps
26- ) : JSX . Element {
27- const viewRef = templateRef . createEmbeddedView ( context ) ;
28- viewRef . detectChanges ( ) ;
28+ ) : RenderPropContext < TContext > {
29+ let viewRef : EmbeddedViewRef < TContext > | null = null ;
30+ let renderedJsx : JSX . Element | null = null ;
31+
32+ return {
33+ render : ( context : TContext ) => {
34+ if ( ! viewRef ) {
35+ viewRef = templateRef . createEmbeddedView ( context ) ;
36+ renderedJsx = renderReactContent ( viewRef . rootNodes , additionalProps ) ;
37+ } else {
38+ // Mutate the template's context
39+ Object . assign ( viewRef . context , context ) ;
40+ }
41+ viewRef . detectChanges ( ) ;
2942
30- return renderReactContent ( viewRef . rootNodes , additionalProps ) ;
43+ return renderedJsx ;
44+ } ,
45+ } ;
3146}
3247
3348/**
3449 * Wrap a function resolving to an `HTMLElement` with a `JSX.Element`.
3550 *
3651 * @param htmlRenderFunc The function to wrap
37- * @param context The context to pass to the function
3852 * @param additionalProps optional additional props to pass to the `ReactContent` object that will render the content.
3953 */
40- export function renderFunc < TContext extends object > (
54+ export function createHtmlRenderer < TContext extends object > (
4155 htmlRenderFunc : ( context : TContext ) => HTMLElement ,
42- context ?: TContext ,
4356 additionalProps ?: ReactContentProps
44- ) : JSX . Element {
45- const rootHtmlElement = htmlRenderFunc ( context ) ;
46-
47- return renderReactContent ( [ rootHtmlElement ] , additionalProps ) ;
57+ ) : RenderPropContext < TContext > {
58+ return {
59+ render : context => {
60+ const rootHtmlElement = htmlRenderFunc ( context ) ;
61+ return renderReactContent ( [ rootHtmlElement ] , additionalProps ) ;
62+ } ,
63+ } ;
4864}
4965
5066/**
5167 * Wrap a `ComponentRef` with a `JSX.Element`.
5268 *
5369 * @param htmlRenderFunc The component reference to wrap
54- * @param context The context to pass to the component as `@Input`
5570 * @param additionalProps optional additional props to pass to the `ReactContent` object that will render the content.
5671 */
57- export function renderComponent < TContext extends object > (
72+ export function createComponentRenderer < TContext extends object > (
5873 componentRef : ComponentRef < TContext > ,
59- context ?: TContext ,
6074 additionalProps ?: ReactContentProps
61- ) : JSX . Element {
62- Object . assign ( componentRef . instance , context ) ;
63- componentRef . changeDetectorRef . detectChanges ( ) ;
75+ ) : RenderPropContext < TContext > {
76+ let renderedJsx : JSX . Element | null = null ;
77+
78+ return {
79+ render : context => {
80+ if ( ! renderedJsx ) {
81+ renderedJsx = renderReactContent ( [ componentRef . location . nativeElement ] , additionalProps ) ;
82+ }
83+
84+ Object . assign ( componentRef . instance , context ) ;
85+ componentRef . changeDetectorRef . detectChanges ( ) ;
6486
65- return renderReactContent ( [ componentRef . location . nativeElement ] , additionalProps ) ;
87+ return renderedJsx ;
88+ } ,
89+ } ;
6690}
0 commit comments