LoginSignup
1
1

More than 3 years have passed since last update.

JavaScript軽量フレームワークxattoのcontextについて

Last updated at Posted at 2019-07-03

JavaScript軽量フレームワークxatto触って見るを書いてからデモのリポジトリが更新されていました。

フレームワーク本体 https://github.com/atomita/xatto
デモ https://github.com/atomita/xatto-starter-kit

xattoのデモが更新されていましたので一緒にcontextについて説明します

大きく変わったのはsrc/app.jsxだけです。

src/app.jsx
import { x, atto, Context } from 'xatto';

import { Counter } from './components/Counter';

const view = (props, children, context) => (
  <div>
    <Context slice="counters.0"><Counter /></Context>
    <Context slice="counters.1"><Counter /></Context>

    <h1>{context.counters.reduce((acc, v) => acc + v.count, 0)}</h1>
  </div>
);

atto(view, document.getElementById('app'))({
  counters: [{ count: 0 }, { count: 10 }],
});

ポイントとなるのは下記の変更で、 contextの抜粋方法 が更新されています。
(書き方が違うだけでやっていることは同じです。)
xattoを使う上でこのcontextの理解が大事になります。

- <Counter xa={{ slice: 'counters.0' }} />
+ <Context slice="counters.0"><Counter /></Context>

xattoでのcontextとは

contextはアプリ全体で必要になる(状態管理の?)データで、特に何もしなければルートのコンポーネントから末端のコンポーネントまで同一のcontextが渡されてきます。
ただ、今回のカウンターの例ではCounterコンポーネントは自身のcountの値以外には興味がありません。
そんな時にContextコンポーネントが役にたちます。Contextコンポーネントはcontextの一部だけを切り取って、その抜粋した値を以降の子孫に渡しています。
Contextコンポーネントの子孫のコンポートは渡されたcontextが抜粋されたものであることを意識する必要はありません。

逆にいうと抜粋された部分以外のcontextを参照できなくなってしまうのですが、そんな時にはextraにメソッドを追加して、コンポーネントからそのメソッドを実行することで回避できます。

contextの更新方法 - イベントハンドラー編

まずはCounterコンポーネントをみてください。

src/components/Counter/view.jsx
import { x } from 'xatto';

/*
 * この down / up イベントハンドラーの返り値でcontextを更新します。
 * <Context slice="counters.0"><Counter /></Context>
 * の場合ですとフルパス(?)でいうところの
 * context.counters[0].count
 * を更新することになります。
 * 
 * Contextコンポーネントの子孫はcontextが抜粋されていることを
 * 意識しなくてもうまいこと処理してくれます。
 */
const down = context => ({ count: context.count - 1 });
const up = context => ({ count: context.count + 1 });

export const CounterView = (props, children, context) => (
  <div class="c-counter">
    <h1>{context.count}</h1>
    <button onclick={down}>-</button>
    <button onclick={up}>+</button>
  </div>
);

このクリックイベントのようにイベントハンドラーの関数の戻り値でcontextが更新できます。そして再レンダリングが走ります。
このCounterコンポーネントはContextコンポーネントによってcontextが抜粋されていることを意識する必要はありません。

contextの更新方法 - mutate編

もう一つ更新する方法があります。
今回のsrc/app.jsxを少し書き換えてみましょう。

src/app.jsx
import { x, atto, Context } from 'xatto';

import { Counter } from './components/Counter';

const view = (props, children, context, extra) => (
  <div
    xa={{
      extra: {
        test: () => {
          return context
        }
      }
    }}>
    <Context slice="counters.0"><Counter /></Context>
    <Context slice="counters.1"><Counter /></Context>

    <h1>{context.counters.reduce((acc, v) => acc + v.count, 0)}</h1>
  </div>
);

/*
 * atto()を実行するとmutateが返ってきます。
 * このmutateにオブジェクトを渡して実行すると
 * その値をcontextとして再レンダリングしてくれます。
 */
const mutate = atto(view, document.getElementById('app'));

// 新しいcontextで再レンダリング
mutate({
  counters: [{ count: 0 }, { count: 10 }],
});

setTimeout(() => {
  // contextの一部を更新したい場合は第2引数で指定します。
  mutate({ count: 11 }, 'counters.0')
}, 1000)

このようにアプリの初期化に使われたatto関数の戻り値は実行可能で、これにオブジェクト形式のデータを渡して実行するとそのデータをcontextとしてレンダリングしてくれます。またcontextの一部を更新したい場合は第2引数にパスを指定することもできます。

関連

JavaScript軽量フレームワークxatto触って見る

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1