2017年6月19日月曜日

re-natalでlist-viewなどリストに関する機能を使う方法


書き方が分かるまでに10時間以上かかったので、情報を共有します。

全体像

この流れで説明します。
  1. 使ったもの
  2. ハマった点
  3. flat-listの書き方
  4. list-viewの書き方
  5. まとめ

使ったもの

  • re-natalで開発しているアプリ
    re-natalとはreact-nativeをclojure scriptで開発するためのフレームワークです。
    下記のアプリを作っている時に、list-viewやflat-listの書き方が分からず、時間がかかりました。
    re-natal-esp32control-app
  • 上記アプリの開発環境
    $ re-natal --version
    0.4.0
    $ react-native -v
    react-native-cli: 2.0.1
    react-native: 0.43.4
    

ハマった点

listのitemをレンダリングする処理をreagent/as-elementで囲って、reactのエレメントにする必要があります。
そうでないと、下記のようなエラーが出て期待通りに表示されません。


StaticRenderer.render(): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.

as-elementの具体的な書き方は、この後説明するflat-listやlist-viewを見ると分かると思います。

flat-listの書き方


下記のような記述で、上の画像のようなflat-listを描画できました。

(ns your-file-name-space
  (:require [clojure.string :as str]
            [reagent.core :as r]))

(def ReactNative (js/require "react-native"))

(def text (r/adapt-react-class (.-Text ReactNative)))
(def view (r/adapt-react-class (.-View ReactNative)))
(def flat-list (r/adapt-react-class (.-FlatList ReactNative)))

(defn item-flat-list []
  (let [items [{:id "item-A"} {:id "item-B"}]]
    (fn []
      [view
       [flat-list
        {:data (clj->js items)

         :key-extractor
         (fn [item index]
           (:id (js->clj item :keywordize-keys true)))

         :render-item
         (fn [item-info-js]
           (let [item-info (js->clj item-info-js :keywordize-keys true)
                 {:keys [item index]} item-info]
             (r/as-element [text (str index " " (:id item))])))}]])))

(defn your-page []
  [view
   [text "flat list sample"]
   [item-flat-list]])

注意点
  • ハマった点で説明したとおり、render-itemに渡す処理はreagentのas-elementでreactの要素にします
  • flat-listはitemにkeyが含まれない場合、key-extractorでkeyの作成方法の指定します
  • render-itemで渡されるデータはitemとindexのmapです

list-viewの書き方


下記のような記述で、上のようなlist-viewを描画できました。

(ns your-file-name-space
  (:require [reagent.core :as r]))

(def ReactNative (js/require "react-native"))

(def text (r/adapt-react-class (.-Text ReactNative)))
(def view (r/adapt-react-class (.-View ReactNative)))
(def list-view-raw (.-ListView ReactNative))
(def list-view (r/adapt-react-class list-view-raw))
(def data-source (.-DataSource list-view-raw))

(defn item-list-view []
  (let [items [{:id "item-A"} {:id "item-B"}]
        data-source (new data-source
                         (clj->js {:rowHasChanged (fn [r1 r2] (not= r1 r2))}))]
    (r/create-class
     {:reagent-render
      (fn []
        [list-view
         {:data-source (.cloneWithRows data-source (clj->js items))
          :render-row (fn [item]
                       (let [item (js->clj item :keywordize-keys true)]
                         (r/as-element
                          [text (:id item)])))}])})))

(defn your-page []
  [view
   [text "list view sample"]
   [item-list-view]])

注意点

  • ハマった点で説明したとおり、render-rowに渡す処理はreagentのas-elemntでreactの要素にします
  • list-viewに渡すデータはdata-sourceでの変換が必要です
  • data-sourceのrowHasChangedの設定は、上記の記述で良いのか自信はありません(とりあえず動いてはいます)


まとめ

as-elementやclj->jsなどを駆使して、react-nativeのflat-listとlist-viewをre-natalで扱えました。
同じような書き方でsection-viewも使えると思います。

ちなみに、scroll-view + for文でもリストを表示できるので、headerなどを利用しないシンプルなリストの場合は、その組み合わせで十分かも知れません。

共有する情報は以上です。

参考

FlatList
ListView
reagent.core/as-element

0 件のコメント :