Giter Club home page Giter Club logo

Comments (2)

lifeart avatar lifeart commented on August 17, 2024
// src/utils/context.ts
import { registerDestructor } from './glimmer/destroyable';
import { Component, RENDER_TREE } from './shared';

const CONTEXTS = new WeakMap<Component<any>, Map<symbol, any>>();

export function getContext<T>(ctx: Component<any>, key: symbol): T | undefined {
  let current = ctx;
  while (current) {
    const context = CONTEXTS.get(current);
    if (context?.has(key)) {
      return context.get(key);
    }

    const parent = findParentComponent(current);
    current = parent;
  }

  return undefined;
}

export function setContext<T>(ctx: Component<any>, key: symbol, value: T): void {
  if (!CONTEXTS.has(ctx)) {
    CONTEXTS.set(ctx, new Map());
  }

  CONTEXTS.get(ctx)!.set(key, value);

  registerDestructor(ctx, () => {
    CONTEXTS.get(ctx)!.delete(key);
  });
}

function findParentComponent(component: Component<any>): Component<any> | null {
  for (const [parent, children] of RENDER_TREE) {
    if (children.has(component)) {
      return parent;
    }
  }

  return null;
}

Explanation:

  • findParentComponent: This new function utilizes the RENDER_TREE to efficiently find the parent component of a given component.
  • getContext:
    • It now iterates up the component tree using findParentComponent.
    • For each ancestor component, it checks if a context value exists for the given key.
    • If found, it returns the value.
    • If not found in any ancestor, it returns undefined.

Benefits:

  • Leverages Existing Structure: This approach leverages the existing RENDER_TREE for context lookup, avoiding the need for a separate context tree.
  • Efficient Lookup: Traversing the RENDER_TREE provides a direct path to ancestor components, making context lookup efficient.
  • No Additional Overhead: This implementation doesn't introduce any significant overhead as it reuses the existing rendering tree structure.

This updated implementation provides a more integrated and efficient way to manage context in GXT by utilizing the internal component tree structure.

from glimmer-next.

lifeart avatar lifeart commented on August 17, 2024

Context API: Sharing Data Across Components

GXT's Context API provides a mechanism for sharing data across components without explicitly passing props through every level of the component tree. This is particularly useful for sharing data that is globally relevant, such as:

Use Cases:

  • Theming: Share theme information, like colors, fonts, and styles, across the entire application.
  • Localization: Provide localized strings and formatting rules to components.
  • User Authentication: Make user authentication status and data accessible to components that need it.
  • Feature Flags: Control the visibility or behavior of features based on feature flags stored in context.
  • Application State: Share application-wide state, such as loading indicators or error messages.

Integration Tests

// src/tests/integration/context-test.gts
import { module, test } from 'qunit';
import { render } from '@lifeart/gxt/test-utils';
import { Component, getContext, provideContext } from '@lifeart/gxt';

const ThemeContext = Symbol('ThemeContext');

class ThemeProvider extends Component {
  <template>
    {{yield}}
  </template>

  constructor(args: any) {
    super(args);
    provideContext(this, ThemeContext, this.args.theme);
  }
}

class ThemedButton extends Component {
  <template>
    <button class={{this.theme.buttonClass}}>
      {{yield}}
    </button>
  </template>

  get theme() {
    return getContext(this, ThemeContext) || {};
  }
}

module('Integration | Context API', function () {
  test('provides and consumes context', async function (assert) {
    await render(
      <template>
        <ThemeProvider @theme={{hash buttonClass="bg-blue-500"}}>
          <ThemedButton data-test-button>Click me</ThemedButton>
        </ThemeProvider>
      </template>
    );

    assert.dom('[data-test-button]').hasClass('bg-blue-500', 'Button receives theme from context');
  });

  test('context is not available outside the provider', async function (assert) {
    await render(
      <template>
        <ThemeProvider @theme={{hash buttonClass="bg-blue-500"}} />
        <ThemedButton data-test-button>Click me</ThemedButton>
      </template>
    );

    assert.dom('[data-test-button]').doesNotHaveClass('bg-blue-500', 'Button does not receive theme outside the provider');
  });

  test('context lookup traverses the component tree', async function (assert) {
    class Layout extends Component {
      <template>
        {{yield}}
      </template>
    }

    await render(
      <template>
        <ThemeProvider @theme={{hash buttonClass="bg-blue-500"}}>
          <Layout>
            <ThemedButton data-test-button>Click me</ThemedButton>
          </Layout>
        </ThemeProvider>
      </template>
    );

    assert.dom('[data-test-button]').hasClass('bg-blue-500', 'Button receives theme from context through nested components');
  });
});

Explanation of Tests:

  1. provides and consumes context:

    • Renders a ThemeProvider that provides a theme context.
    • Renders a ThemedButton inside the provider.
    • Asserts that the button receives the theme from the context.
  2. context is not available outside the provider:

    • Renders a ThemeProvider and a ThemedButton outside the provider.
    • Asserts that the button does not receive the theme context.
  3. context lookup traverses the component tree:

    • Renders a ThemeProvider, a Layout component, and a ThemedButton nested within.
    • Asserts that the button still receives the theme context even though it's not a direct child of the provider.

These tests demonstrate the basic functionality of the Context API, including providing, consuming, and looking up context values within the component tree.

from glimmer-next.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.