Hyperapp: How do you communicate between multiple components?

Created on 17 Aug 2017  路  10Comments  路  Source: jorgebucaran/hyperapp

First of all I apologize if this ain't the right platform to ask this question. I'm usually not a big fan of single page apps style of components so I like controlling my routes through the server and have multiple components in a page. Now I'm going over the documentation and the examples but can't figure out how to have multiple components share the same state or if thats even possible. I'm coming from a react state of mind where I would have multiple components share the same store.

This is what I got so far. Again I'm a complete noob to ELM or HyperApp but I'm super excited to drink the koolaid.
https://codepen.io/codingphase/pen/QMaBbM?editors=1111

Inquiry

Most helpful comment

WOW I'm loving this community... really thank you guys. For explaining this in such a detail. I definitely will be going through this framework this weekend like crazy. I just cant tackle things here like I would with react or vue. @zaceno appreciate that you forked it because i can see what you did and understand how I should implement this in our website at 39dollarglasses.com

All 10 comments

Hi @codingphasedotcom

Interoperability is explained to some extent here: https://github.com/hyperapp/hyperapp/blob/master/docs/events.md#interoperability

But using that technique only makes sense if you want to communicate with your app from the outside, from another existing non-hyperapp codebase, etc. If you are making an app from scratch, you should ideally have only one app() call.

How to have multiple components share the same state or if thats even possible.

The container passes props down to a component and that component may father other components and pass props down to their children from the received props as well. Top-down.

Whatever props you pass down will always come from the same global state, so by definition, all components share the same state.

Does that help?

hey @jbucaran,

Thanks for sending me in the right direction I don't know how I missed this.

If you are making an app from scratch, you should ideally have only one app() call.

I bring back this example because 90% of the time I wont be able to have a parent component and then 2 children components. So in this example of the counter I'm trying to have two separate components but when I click up on 1 i want the other to have the same result.
https://codepen.io/codingphase/pen/QMaBbM?editors=1111

I ask this because right now I have a filter section and a content section that are in complete different sections of the html. I want to be able to have access to the state of the filter component on the content component.

Image Example

@codingphasedotcom Hmm, I think you are confused about components, which is okay, since you already said you are coming from react.

See Components.

In Hyperapp components are stateless, pure functions. There is no local state. Hyperapp components are not like your react components. 馃槃

See also State.

The notion of representing the application state as a single source of truth is known as single state tree. The tree is manipulated using actions.

To achieve what you are trying to do, multiple counters, you need to rethink the entire app in terms of a single state tree.

You can go about this in several ways and there are also "patterns" that let you mimic scoped state to a certain extent.

See this example. Those are not counters like the ones you are trying to make, but it's close. It's just a random example. There are many ways to do this.

/cc @zaceno @MatejMazur @jamen

@codingphasedotcom,

Like @jbucaran said, you generally want to strive for a single app(), because that way all your components will share a common state. Within that state, concerns may still be neatly separated (using some kind of lib to help like rambda in the example above or (shameless plug alert!) https://github.com/zaceno/hyperapp-partial)

That said, if you must have multiple app() calls, as in your codepen example, it is fully possible for them to communicate and synchronize. Every call to app({...}) returns a function; emit which you can use to send messages to that app. The messages are captured in the app's events property.

I forked your codepen and made some minor adjustments to make the counters sync. Have a look at:
https://codepen.io/zaceno/pen/vJpPBY

I will just reiterate what has already been said:

  1. There is no "private state", so all your components have access to the same state.
  2. There is no "private actions", so all your components can mutate the state the same ways.

Provided you pass them the state and actions they need to perform their job.

So, _communicating between components_ should be really simple, they just use and mutate the pieces of state they are given access to. Take an example of two components, one that displays some state and another that mutates it:

function Feed (props) {
  return props.items.map(item =>
    <div class='feed-item'>{item}</div>
  )
}

function Input (props) {
  return <input onclick={props.onsubmit} placeholder='Add to feed' />
}

When these are initialized, we pass them the proper info from the view:

// The display:
<Feed items={state.items} />

// The input:
<Input onsubmit={actions.createItem} />

This is a watered down example, hopefully it makes sense. :smile:

Kudos @jamen for clarifying what a "component" means in hyperapp-land. @codingphasedotcom can be forgiven for being confused, as an app({...}) looks a lot like a component in Vue. (And, I presume, React also)

Thanks. I think it is also important to clarify how they don't just _have access_ to state and actions, as @jbucaran described it:

In Hyperapp components are stateless, pure functions. There is no local state. Hyperapp components are not like your react components.

They are "stateless", and you pass pieces of the app's state and actions (so it knows how to mutate state).

As some "workaround" to this, I've seen people just do:

<Component state={state} actions={actions} />

This is nice! But also, it's a nice modeling what data/actions your component needs sometimes.

_A_ Solution

You can also you use the emit pattern! Kind of like EventHub in Vue if you are familiar with that pattern.

Caveats

I understand that everyone explains that a single app() is preferred but it is not that easy to do in a legacy code base (template partials being loaded, shared template partials, etc..). So I can feel your pain here 馃槃

Example

Here is an example of a plain JS function talking to an instance of hyperapp (which two apps can do vice versa no problem): https://codepen.io/selfup/pen/jLMRjO

JS:

/** @jsx h */
const { h, app } = hyperapp;

const HYPERHUB = app({
  state: { num: 0 },
  actions: {
    hello: (state, actions, { num }) => {
      console.log(num);
      return { num };
    },
  },
  events: {
    dispatch: (state, actions, { action, data }) => {
      return actions[action]
        ? actions[action](data)
        : null;
    },
  },
  view: (state, actions) => 
    <div>
      <h1>{state.num}</h1>
    </div>,
  root: document.querySelector('#app'),
});

const dispatch = (action, data) => HYPERHUB(
  'dispatch',
  { action, data },
);

dispatch('hello', { num: 42 });

// not hyperapp stuff calling hyperapp through hyperhub
document.addEventListener('click', ({ target }) => {
  switch(target.id) {
    case '9000':
      dispatch('hello', { num: 9000 });
      break;
    case '42':
      dispatch('hello', { num: 42 });
      break;
    default:
      null;
      break;
  }
});

HTML:

<button id="9000">HyperHub 9000 from regular JS</button>
<button id="42">Back to 42</button>

<div id="app"></div>

<script src="https://unpkg.com/hyperapp"></script>

Another Example

@zaceno seems to have forked your codepen and made some adjustments 馃憤

https://github.com/hyperapp/hyperapp/issues/331#issuecomment-323161686

Direct Link to @zaceno's codepen: https://codepen.io/zaceno/pen/vJpPBY

Endgame

The end goal here is to eventually not need a hub, but to use it to facilitate incrementally adding apps until you have removed the "technical debt". Eventually just a few lines of HTML will be what seperates two apps, and at that point you can just turn them into one.


Hope this helps!

WOW I'm loving this community... really thank you guys. For explaining this in such a detail. I definitely will be going through this framework this weekend like crazy. I just cant tackle things here like I would with react or vue. @zaceno appreciate that you forked it because i can see what you did and understand how I should implement this in our website at 39dollarglasses.com

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jacobtipp picture jacobtipp  路  3Comments

jamen picture jamen  路  4Comments

jorgebucaran picture jorgebucaran  路  4Comments

ghost picture ghost  路  3Comments

zaceno picture zaceno  路  3Comments