Hyperapp の重要なテクニックhypercraft（Cross-namespace Action-calling 編）

# 1. はじめに

Hyperappでアプリを開発中、課題解決の為に、
[loteoo](https://github.com/loteoo)さんに[zaceno](https://github.com/zaceno)さんが書かれた素晴らしいページを教えてもらいました。
いつでも読み返せるように翻訳します。

本記事では原文サイトの中の、
**Cross-namespace Action-calling** [訳: ネームスペース間でのアクション呼び出し] について翻訳したものを記述します。
完璧な翻訳ではないので、齟齬が無いよう原文とパラレルで記述します。

以下原文ページのリンク

* [hypercraft - index](https://zaceno.github.io/hypercraft/index.html)
 * [cross-namespace-action-calling](https://zaceno.github.io/hypercraft/post/cross-namespace-action-calling/)
 * [initialization](https://zaceno.github.io/hypercraft/post/initialization/)
 * [memoization](https://zaceno.github.io/hypercraft/post/memoization/)
 * [modular-apps](https://zaceno.github.io/hypercraft/post/modular-apps/)
 * [static-vnodes](https://zaceno.github.io/hypercraft/post/static-vnodes/)
 * [utility-actions](https://zaceno.github.io/hypercraft/post/utility-actions/)
 * [decorator-components](https://zaceno.github.io/hypercraft/post/decorator-components/)
 * [stateful-components](https://zaceno.github.io/hypercraft/post/stateful-components/)
 * [keying-components](https://zaceno.github.io/hypercraft/post/keying-components/)
 * [about](https://zaceno.github.io/hypercraft/about/index.html)

# 2. 翻訳
> ## Cross-namespace Action-calling

ネームスペース間でのアクションの呼び出し


> ### How can an action in a namespace (a k a "slice") call an action outside the namespace?

あるactionから、名前空間(スライスとも言う)の外にある別のactionをどうやって呼ぶか？


   

> Using namespaces ("slices") for state lets you avoid very long state-property names. Scoping actions to a namespace makes it easy to update it. See my article on [Modular Apps](https://zaceno.github.io/hypercraft/post/modular-apps/) if this is the first you're hearing about this.

Stateで名前空間（スライス）を使うと、stateでの長いプロパティ名を避けることができます。
Actionsは名前空間にスコープする事で、そのstateの名前空間を簡単に更新できます。
もしご存知なければ、[モジュラー アプリケーション](https://zaceno.github.io/hypercraft/post/modular-apps/)に関する私の記事を読んでください。

> Namespaces are great, but also restrict actions in a namespace, from calling other actions outside it. And sometimes you really need that.

名前空間は素晴らしいですが、名前空間内のアクションが、その名前空間以外のアクションを呼び出すことはできません。そして、たまにそれをどうしてもやりたいと言うケースがあります。

> Usually the need is of the form: "When X happens over here, then Y should happen over there". For example, one namespace might be for syncing data to and from the server, while another is for editing the data. When the data is changed, you want it synced back to the server.

一般的には`こっちでXすると、そっちでYとなるべき`と言った形式です。
例えば、サーバーとデータをやり取りする名前空間と、データを更新する別の名前空間があります。
データ変更したら、あたなはサーバへそのデータを同期したいでしょう。

> So how do we solve situations like these?

では、このような状況をどうやって解決しましょうか？

> First, ask yourself if the namespace-separation really is helpful. If you can put dependent actions together in the same namespace the problem goes away.

まず、「名前空間分け」が本当に役立つかどうか問いかけてください。
同じ名前空間の中に依存するアクションをまとめることができれば、問題ないでしょう。

> But flattening the state doesn't always make things better.

でも、stateをフラットにする事が必ずしも良いことではありません。

> You can't call "Y" from the action where "X" happens. The view, on the other hand, has access to all the actions. You could put the "when X then Y"-logic in the view.

「X」が起こるアクションから「Y」を呼び出すことはできません。
一方、ビューはすべてのアクションにアクセスできます。
ビューの中で `when X then Y` このようなロジックを置くことができます。

> But you want to avoid mixing business-logic and view-logic.

でも、ビジネスロジックとビューロジックを混在させたくはないですね。

> You could use a third-party event-emitter-library to wire the actions together. It's a pretty good solution that goes naturally with the phrasing of the problem as " When X ... then Y...".

サードパーティ製のイベント・エミッター・ライブラリーを使用して、アクションを繋げる事ができます。それは、```When X ... then Y ...```このフレーズの問題に関して、
かなり良いいい感じに書ける解決策です。

> But using event emitters can get pretty confusing, and debugging them is difficult.

しかしながら、イベント・エミッターの使用は、結構混乱しやすくデバッグが大変です。

> The pattern I like to use builds on the initialization pattern outlined [here](https://zaceno.github.io/hypercraft/post/initialization/). The essence of it is: if actions in a namespace need to call outside actions, pass those outside actions to the **init** action of the namespace. It stores them in the state, where they can be called as needed.

私が使いたいパターンは、[ここ](https://zaceno.github.io/hypercraft/post/initialization/)で概説されている初期化パターンに基づいています。
その本質は：ネームスペース内のアクションが外部のアクションを呼び出したい場合、
それらの外部アクションを名前空間の初期処理で渡すことです。いつでも呼び出せるstateの中にストアしておきます。

> Looking at the example I mentioned above: You have a namespace **backend** which loads and saves data.

先ほど述べた例を見てみる: データをロードして保存する**backend**と言う名前空間があります。

```javascript:
const actions = {
    foo: {
        load: data => ...,
        ...
    },
    bar: {
        load: data => ...,
        ...
    }
    backend: {
        fetch: id => (state, actions) => {
            fetch(`https://example.com/api/${id}`)
            .then(data => data.json())
            .then(({foo, bar})=> {
                /*
                    Now what? How do we get
                    to: actions.foo.load(foo)?
                */
            })
        }
    }
}
```

> We can make sure that **actions.backend.fetch** knows how to call the **load** actions of **foo** and **bar**, by passing them to it at init-time:

確かに、
初期設定時に、**actions.backend.fetch**に、actionsを渡す事で、**actions.backend.fetch**は**foo**と**bar**のアクションを呼ぶ事ができますね:

```javascript:

const actions = {
    init: _ => (_, actions) => {
        actions.backend.init({
            onfetch: ({foo, bar}) => {
                actions.foo.load(foo)
                actions.bar.load(bar)
            }
        })
    },
    foo: ...,
    bar: ...,
    backend: {
        init: ({onfetch}) => ({onfetch}),
        fetch: id => (state, actions) => {
            fetch(`https://example.com/api/${id}`)
            .then(data => data.json())
            .then(state.onfetch)
        }
    }
}
```

> Also, any time the state in foo or bar changes, we want to persist the data back to the server:

また、fooまたはbarの状態が変わるたびに、データをサーバーに保存し直す必要があります: 

```javascript:

const actions = {
    foo: {
        _set: s => s,
        setX: x => (state, actions) => {
            actions._set({x})
            /*
                We changed the state of foo.X
                Now how do we make backend save {foo, bar}?
            */
        }
    },
    backend: {
        save: ({foo, bar}) => ...
    }
}
```

> Same thing:

同じ事です:

```javascript:

const actions = {
    getState: _ => s => s,
    init: _ => (_, actions) => {
        actions.backend.init({
            onfetch: ({foo, bar}) => {
                actions.foo.load(foo)
                actions.bar.load(bar)
            }
        })

        const doSave = _ => actions.backend.save({
            foo: actions.getState().foo,
            bar: actions.getState().bar,
        })

        actions.foo.init({onchange: doSave})
        actions.bar.init({onchange: doSave})
    },
    foo: {
        init: ({onchange}) => ({onchange}),
        _set: s => s,
        setX: x => (state, actions) => {
            actions._set({x})
            state.onchange()
        },
        ...
    },
    bar: { /*... similarly ...*/ },
    backend: {
        init: ({onfetch}) => ({onfetch}),
        save: ({foo, bar}) => ...,
        ...
    }
}
```

> There are a downsides to this too, of course. There will be functions stored in the state, which some dislike. It doesn't look especially clean or elegant. Still, the debugability and the fact that all dependencies between slices are kept out of the view makes it my preferred approach to this type of problem.

もちろんこれには欠点もあります。stateの中に関数を定義していますが、これは嫌ですね。見た目がエレガントではないからです。それでも、デバッガビリティと、スライス間のすべての依存関係がビューの外で実装されているのは、この問題に対するアプローチとしては私好みの方法です。