once.go 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. package templ
  2. import (
  3. "context"
  4. "io"
  5. "sync/atomic"
  6. )
  7. // onceHandleIndex is used to identify unique once handles in a program run.
  8. var onceHandleIndex int64
  9. type OnceOpt func(*OnceHandle)
  10. // WithOnceComponent sets the component to be rendered once per context.
  11. // This can be used instead of setting the children of the `Once` method,
  12. // for example, if creating a code component outside of a templ HTML template.
  13. func WithComponent(c Component) OnceOpt {
  14. return func(o *OnceHandle) {
  15. o.c = c
  16. }
  17. }
  18. // NewOnceHandle creates a OnceHandle used to ensure that the children of its
  19. // `Once` method are only rendered once per context.
  20. func NewOnceHandle(opts ...OnceOpt) *OnceHandle {
  21. oh := &OnceHandle{
  22. id: atomic.AddInt64(&onceHandleIndex, 1),
  23. }
  24. for _, opt := range opts {
  25. opt(oh)
  26. }
  27. return oh
  28. }
  29. // OnceHandle is used to ensure that the children of its `Once` method are are only
  30. // rendered once per context.
  31. type OnceHandle struct {
  32. // id is used to identify which instance of the OnceHandle is being used.
  33. // The OnceHandle can't be an empty struct, because:
  34. //
  35. // | Two distinct zero-size variables may
  36. // | have the same address in memory
  37. //
  38. // https://go.dev/ref/spec#Size_and_alignment_guarantees
  39. id int64
  40. // c is the component to be rendered once per context.
  41. // if c is nil, the children of the `Once` method are rendered.
  42. c Component
  43. }
  44. // Once returns a component that renders its children once per context.
  45. func (o *OnceHandle) Once() Component {
  46. return ComponentFunc(func(ctx context.Context, w io.Writer) (err error) {
  47. _, v := getContext(ctx)
  48. if v.getHasBeenRendered(o) {
  49. return nil
  50. }
  51. v.setHasBeenRendered(o)
  52. if o.c != nil {
  53. return o.c.Render(ctx, w)
  54. }
  55. return GetChildren(ctx).Render(ctx, w)
  56. })
  57. }