Skip to content

Replacing a child component when state changes

Nathan Ridley edited this page Aug 6, 2016 · 1 revision

Sometimes we want to be able to switch out one component for another depending on the state of our application. A simple example would be changing a login form in the sidebar to a profile picture and account menu if the user is logged in. Obviously this isn't too difficult to handle manually, but using a collection can make it a breeze.

This example assumes the existence of an auth$ source stream. The hypothetical stream emits {profile: null} if not logged in, or {profile: {...}} otherwise, and uses @most/hold to retain the last emitted value so that when the stream is observed by a new component, it'll receive the most recent auth state straight away.

function LoginForm(sources) {
  // ...
  return { DOM: view$ };
}

function AccountControls(sources) {
  // ...
  return { DOM: view$ };
}

function App(sources) {
  const components$ = sources.auth$

    // Filter out values where the login state has not changed. Emitting a value
    // past this point causes the component to be recreated and replaced in the
    // collection. Seeing as the component will itself observe the auth$ source,
    // it will take care of reacting to secondary changes to auth information.
    .skipRepeatsWith((a, b) => !a.profile === !b.profile)

    // Use `scan` to initialize a new collection and keep track of the child
    // component that will be swapped out depending on login state.
    .scan((components, auth) => {
      const account = a.profile ? AccountControls(sources) : LoginForm(sources);
      return components.setInstance('account', account);
    }, Collection())

    // Skip the initial blank collection that `scan` will emit first
    .skip(1);

    // At this point we could perform further operations to append and manage
    // more child components in the list as well. Alternatively or additionally,
    // we could pre-initialize the collection with the components that we know
    // won't be changing externally.

    // ...

    // Because we have a stream of collections, rather than access to the
    // collection itself, we can use the static equivalents of the available
    // 'emit' functions:
    const view$ = Collection
      .combineObject('DOM', components$)
      .map(({accountView}) => render(accountView));

    return {
      DOM: view$
    };
}

Tutorial and Guide

  • [Managing child components](Managing child components)
  • [Replacing a child component when state changes](Replacing a child component when state changes)
  • [Combining multiple sinks from child components](Combining multiple sinks from child components)
  • [Simple merging of a common sink to a derivative stream](Simple merging of a common sink to a derivative stream)
  • [Dynamic lists of child components](Dynamic lists of child components)
  • [Managing lists of components that don't have a key](Managing lists of components that don't have a key)
  • [Handling multiple component types in a single list](Handling multiple component types in a single list)
  • [Taking control of a component's lifecycle within a list](Taking control of a component's lifecycle within a list)

API Reference

  • [Quick Reference](API Quick Reference)
  • [Detailed Reference](API Detailed Reference)
Clone this wiki locally