状態管理はReactアプリケーションの中核であり、Reactは単なるUIライブラリであるため、アプリの状態を処理するための何かが必要です。 状態管理は面倒になる可能性があり、状態に一貫性がないため、管理しにくいReactアプリケーションを簡単に作成できます。

この記事では、ReactNativeアプリケーションで状態管理ソリューションとしてMobXを使用する方法について学習します。

状態管理とは何ですか?

State は、アプリが処理しているデータにすぎません。 状態は、コンポーネントに必要なデータを保存し、コンポーネントのレンダリング方法に影響を与えます。 状態管理は、そのデータを管理するプロセスです。 特定のアプリでデータを監視および取得することは困難な場合があり、そこで状態管理ライブラリが役に立ちます。 ReduxやReactContext API を使用するなど、状態を管理する方法は複数ありますが、ここではMobXについて説明します。

MobXとは何ですか?

MobX は、任意のJavaScriptフレームワークで使用できる状態管理ライブラリです。 ReactとMobXは一緒に強力であり、完全なフレームワークとして機能します。 MobXは、ReactNativeがコンポーネントのレンダリングに使用するアプリケーションの状態を保存および更新するメカニズムを提供します。 MobXの背後にある哲学は次のとおりです。*「アプリケーションの状態から派生できるものはすべて、派生する必要があります。 自動的。”*

核心概念

派生はMobXのバックボーンを形成し、繰り返される状態を破棄できるようにします。 アイデアは次のとおりです。

最小限の状態(観察可能な状態)を見つけ、すべてを派生させ(派生状態)、状態をそれ以上の状態に変えないでください。

MobXのコアには、 Observables Actions 、およびReactionsという3つの重要な概念があります。 ストアにはこれら3つが含まれており、ReactNativeアプリケーションによって使用されます。

オブザーバブル

MobXのオブザーバブルは、アプリケーションのコア状態を保持する単なるコンテナーです。 アイデアは、オブザーバーが反応できる新しい変化をオブジェクトが放出できるようにすることです。 これは、@observableデコレータを使用して実現できます。
時間の経過とともに変化するcountという名前の変数があるとします。 次の方法で簡単に観察できるようになります。

// import observable from mobx
import { observable } from "mobx";

//create a store with count observable
class Store {
  @observable
  count = 0;
}

//export Store
export default new Store();

計算されたオブザーバブル

MobXの原則を覚えておいてください。*「最小状態(観測可能な状態)を見つけ、すべてを導き出す(派生状態)」*。
すでに定義された観測量から導き出すことができる値は、計算値です。 MobXは、より多くの状態の作成を阻止することにより、状態の不整合を回避します。 count変数が何かが遅れる分数を保持していると想像してください。 監視可能なcountから派生するcomputed遅延メッセージを追加できます。

import { observable, computed } from "mobx";

class Store {
  @observable
  count = 0;

  @computed
  get delayMessage = () => {
    return 'The train is delayed by' + this.count;
  };
}

export default new Store();

ここで、@computedは、countからその値を導出するゲッター関数として機能しています。 delayMessageは、countの値が変更されると、自動的に変更を発行します。

行動

アクションは、単に状態を変更する関数です。 MobXは一方向のデータフローをサポートします。つまり、アクションが状態を変更すると、その状態を消費しているすべてのビューが自動的に更新されます。 遅延が増加するとcount変数を更新するactionを追加しましょう。

Store {
import { observable, computed, action } from "mobx";

class Store {
 @observable
  count = 0;

  @computed
  get delayMessage = () => {
    return 'The train is delayed by' + this.count;
  };

  @action
  updateDelay = delay => {
    this.count = delay;
  };
}

export default new Store();

すべての状態の変更は、アクションによってのみ実行する必要があることに注意してください。

反応

オブザーバブルは、オブザーバブルの変更をサブスクライブし、それらを使用するコンポーネントを再レンダリングします。 反応は、これらの状態変化の単なる副作用です。 これは計算された値と非常に似ていますが、違いは値を計算して返す代わりに、リアクションが単に副次的な操作を実行することです。 簡単に言うと、リアクションは次のとおりです。

状態の変化に反応して発生するはずの副作用(コンポーネントの再レンダリング)

MobXは、 autorun whenおよびreactionの3つの主要なタイプの反応関数を提供します。

1. 自動実行

autorunは、状態が変化するたびに実行される関数です。

autorun(() => {
  console.log('delay is', this.count);
} );

この関数は、countの値が変更されるたびに実行されます。 重要なのは、count変数の変更を監視する必要があることを明示的に述べていないことです。 自動実行内でcountを使用したという事実は、それを依存関係の1つとして使用し、依存関係が変更されるたびに関数をトリガーするのに十分です。

2. いつ

whenは、特定の条件が満たされるたびに副作用を引き起こします。 2つのパラメータを取ります。 最初のパラメーターは、trueを返すまで再評価される関数であり、2番目のパラメーターは、最初の関数がtrueを返すと実行される別の関数です。 簡単な例は次のとおりです。

class MyResource {
  constructor() {
    when(
      // once...
      () => this.count > 60,
      // ... then
      () => console.log("Guest is too late, maybe he's not coming");
    );
  }
}

ここで、whenは、遅延が1時間(60分)を超えているかどうかを確認し、彼が来ていない可能性があることを出力します。

3. 反応

reactionは、autorunのバリエーションであり、関数で使用されるデータ(依存関係)をより細かく制御できます。 2つの関数の引数とオプションの3番目の引数を受け入れます。

  1. 最初の引数(データ関数)はデータの変更を監視し、2番目の引数である効果関数の入力として使用されるデータを返します。
  2. 2番目の関数は、最初の関数が受け取ったデータを最初の引数として受け入れ、副作用を実行しますが、データ関数が新しい値を返す場合に限ります。 また、実行中に反応を処理するために使用できる2番目の引数を受け取ります。

次の例は、1回だけ呼び出される反応を示しています。

const reactionDemo = reaction(
  () => this.count,
  (count, reaction) => {
    console.log("reaction demo: invoked. delay is " + count);
    reaction.dispose();
  }
);

this.count = 1;
// prints:
// reaction demo: invoked. delay is = 1

this.count = 2;
// prints:
// (There are no logging, because of reaction disposed. But, count continue reaction)

console.log(this.count);
// prints:
// 2

MobXの動作

3つの簡単なステップでReactNativeアプリを作成することで、MobXの動作を理解します。

  1. 状態を定義し、それを観察可能にする
  2. 状態の変化を監視するビューを作成する
  3. アクションを使用して状態を変更する

私たちが構築しているもの

ここでは、Unsplashから画像を取得してユーザーに表示するシンプルなアプリを作成しています。 ユーザーは画像をクリックしてお気に入りに追加することもできます。

アプリケーションはUnsplashAPIを使用してランダムな画像をフェッチします。 ここでAPIキーを生成できます

プロジェクトをまだ作成していない場合は、以下の手順に従ってください。

  1. ReactNativeアプリケーションを作成する
$ react-native init UnsplashDemo

または、Expoを使用します。

$ expo init UnsplashDemo
  1. MobXを追加
$ npm install mobx mobx-react
  1. プロジェクトを実行する
$ react-native run-<your-os>

または、Expoを使用します。

$ expo start

ステップ1。 状態を定義し、それを観察可能にする

いくつかの画像を検索して結果を保存します。 次に、任意の画像をクリックしてお気に入りに追加できるようにします。 コメントは自明です:

// importing observables and decorate
import { decorate, observable, action } from "mobx";

class Store {
  // observable to save search query
  text = '';

  // action to update text
  updateText = (text) => {
    this.text = text;
  }

  // observable to save image response from api
  data = null;

  // action to call API and search images
  searchImages = () => {
    fetch(`https://api.unsplash.com/search/photos?client_id=${API_KEY}&page=1&query=${this.text}&orientation=landscape`)
      .then(response => response.json())
      .then(data => this.setData(data));
  };

  // observables can be modifies by an action only
  setData = (data) => {
    this.data = data;
  };
}

// another way to decorate variables with observable
decorate(Store, {
  text: observable,
  updateText: action,
  data: observable,
  searchImage: action,
  setData: action,
});

// export class
export default new Store();

ステップ2。 状態の変化を監視するビューの作成

画像のリストをレンダリングするコンポーネントImageList.jsを作成します。 また、スイッチを切り替えるだけで、お気に入りに追加された画像が表示されます。

  1. ImageListコンポーネントのボイラープレート:
import React from "react";
import { View, TextInput, Button, FlatList } from 'react-native';

// imports inject and observer from 'mobx-react':
import { inject, observer } from "mobx-react";

// components receive Store values as props which we will inject while exporting
function ImageList(props) {
  // destructure variables from store to use 
  const { text, updateText, data, searchImages } = props.store;
  return (
    <>
      <TextInput // TextInput to get search query from user
        style={styles.input} 
        value={text}
        onChangeText={updateText}
      />
      <Button // Button to call API
          title="Search"
          style={styles.button}
          onPress={searchImages}
        />
      />
      <FlatList      
        data={data.results} // response from API
        keyExtractor={(item) => item.id}
        renderItem={({ item }) => (
          <ImageView // reusable component to render image
            source={{ uri: item.urls.small }} // passing the url
            onPress={() => {}} // action to add item to favorite
          />
        )}
      />
    </>
  );
}

// inject Store as props to ImageList and make it observe changes in Store
export default inject("store")(observer(ImageList));

TextInputから入力を取得し、Buttonを押してUnsplash検索APIを呼び出しています。 応答はdata監視可能に保存されており、FlatListコンポーネントでそれを使用して画像のリストをレンダリングしています。 簡単ですよね? それでは、お気に入りに画像を追加することに移りましょう。

Unsplash API応答の詳細については、unsplash.com/developersにアクセスしてください。

ステップ3。 アクションを使用した状態の変更

ご存知のように、アクションは状態の変更に関与します。これまでのところ、updateTexttext observableを変更し、setDatadataを変更しました]観察可能。 ここで、favoritesに画像を追加します。これは、状態を保存するために1つの監視可能オブジェクトと、この状態を変更するための1つのアクションが必要であることを意味します。 それらを追加しましょう。

import { decorate, observable, action } from "mobx";

class Store {
  text = '';
  updateText = (text) => {...};

  data = null;
  searchImages = () => {...};

  setData = (data) => {...};

  // array to save favourite images
  favorites = [];

  // action to add images to favorites
  addToFavorite = (image) => {
    this.favorites.push(image);
    this.data = null;
    this.text = '';
  };
}

decorate(Store, {
  text: observable,
  updateText: action,
  data: observable,
  searchImage: action,
  setData: action,
  //adding decorators
  favorites: observable,
  addToFavorite: action,
});

export default new Store();

次に、これらの追加されたオブザーバブルとアクションのビューを更新します。 以前に検索した画像をクリアして、追加したお気に入りの画像を表示したいのですが、これは次の方法で簡単に実行できます。

// previous destructuring
const { favorite, addToFavorite} = this.props.store
return (
  <>
  {/* TextInput and Button added earlier */}
  {/* If data is available then show search results otherwise show the favorite images */}
  {data ?
    <FlatList // To render list of images
      style={styles.container}
      data={data.results}
      keyExtractor={(item) => item.id}
      renderItem={({ item }) => (
        <ImageView
          source={{ uri: item.urls.small }}
          onPress={() => addToFavorite(item.urls.small)} // action to add url to favorite
        />
      )}
    /> :
    <FlatList
      style={styles.container}
      data={favorites}
      keyExtractor={(item, index) => index.toString()}
      renderItem={({ item }) => (
        <ImageView
          source={{ uri: item }} // render favorite images
        />
      )}
    />
  }
  </>
);

これまで、observersobservablesactionsを使用してきました。 computedを追加して、お気に入りに追加された画像の数を表示しましょう。 computedは、オブザーバブルから派生状態を取得するゲッター関数のように機能します。 次のように追加できます。

import { decorate, observable, action, computed } from "mobx";

class Store {
  // previously added value
  get getFavoriteCount() {
    return this.favorites.length;
  }
}

decorate(Store, {
  // previously added values
  getFavoriteCount: computed,
});

export default new Store();

ビューにもすばやく追加しましょう。

const { getFavoriteCount } = this.props.store;

return (
  // TextInput, Button
  <Text style={styles.count}>
    Images added: {getFavoriteCount}
  </Text>
  // FlatList
);

ここで、最後に行う必要があるのは、Providerstoreをルートコンポーネントに提供することです。 ルートファイルは次のようになります。

import React from 'react';
import ImageList from './src/container/ImageList';

// imports Provider and store
import { Provider } from 'mobx-react';
import store from './src/store';

const App = () => {
  return (
    <Provider store={store}>
      <ImageList />
    </Provider>
  );
};

export default App;

それでおしまい。 動作中のアプリのGIFを見てみましょう。

React Native app demo animated GIF


MobXのobservablesactionsobservers、およびcomputedプロパティについて学習し、単純なReactNativeアプリを構築することでそれらを正常に使用しました。 MobXの学習を楽しんでいただければ幸いです。このチュートリアルは、React Native+MobXの使用を開始するのに役立ちました。 ハッピーコーディング! 👨‍💻