expo / examples

Example projects that demonstrate how to use Expo APIs and integrate Expo with other popular tools
2.23k stars 867 forks source link

fix(with-tinybase) Introduce FlatList #495

Closed jamesgpearce closed 1 month ago

jamesgpearce commented 1 month ago

As @brentvatne suggested! No change, cosmetically, but also uses SafeAreaView to be clean.

brentvatne commented 1 month ago

I realized while reviewing this that I wasn't sure what react-compiler would do in this case: will react-compiler memoize the callback if it's inlined in the renderItem prop? renderItem={({ item }) => <Component item={item} />}

It turns out that no, it won't. But if we pull it out to const renderItem = ({ item }) => <Component item={item} /> and then use <FlatList renderItem={renderItem} />, react-compiler will take care of optimizing that for us. Although react-compiler isn't enabled by default yet, I think this is likely the pattern we want to demonstrate here since I expect most folks will use it in the near future.

Here's the output if you're curious -- I used expo-atlas to examine the compiled output and toggled react-compiler in app.json under experiments

Function inlined in prop

// A list component to show all the todos.
const Todos = () => (
  <FlatList
    data={useSortedRowIds(TODO_TABLE, DONE_CELL)}
    renderItem={({ item: id }) => <Todo id={id} />}
    style={styles.todos}
  />
);

Compiled output (without react-compiler)

  var Todos = function () {
    _s3();
    return /*#__PURE__*/(0, _jsxDevRuntime.jsxDEV)(_reactNative.FlatList, {
      data: (0, _uiReact.useSortedRowIds)(TODO_TABLE, DONE_CELL),
      renderItem: function (_ref3) {
        var id = _ref3.item;
        return /*#__PURE__*/(0, _jsxDevRuntime.jsxDEV)(Todo, {
          id: id
        }, void 0, false, {
          fileName: _jsxFileName,
          lineNumber: 84,
          columnNumber: 35
        }, _this);
      },
      style: styles.todos
    }, void 0, false, {
      fileName: _jsxFileName,
      lineNumber: 82,
      columnNumber: 3
    }, _this);
  };

Compiled output (with react-compiler)

  var Todos = function () {
    _s3();
    return /*#__PURE__*/_jsxDEV(FlatList, {
      data: useSortedRowIds(TODO_TABLE, DONE_CELL),
      renderItem: function (_ref) {
        var id = _ref.item;
        return /*#__PURE__*/_jsxDEV(Todo, {
          id: id
        }, void 0, false, {
          fileName: _jsxFileName,
          lineNumber: 84,
          columnNumber: 35
        }, _this);
      },
      style: styles.todos
    }, void 0, false, {
      fileName: _jsxFileName,
      lineNumber: 82,
      columnNumber: 3
    }, _this);
  };

Function extracted to a named const

// A list component to show all the todos.
const Todos = () => {
  const renderItem = ({ item: id }) => <Todo id={id} />;

  return (
    <FlatList
      data={useSortedRowIds(TODO_TABLE, DONE_CELL)}
      renderItem={renderItem}
      style={styles.todos}
    />
  );
};

Compiled output (without react-compiler)

  var Todos = function () {
    _s3();
    var renderItem = function (_ref3) {
      var id = _ref3.item;
      return /*#__PURE__*/(0, _jsxDevRuntime.jsxDEV)(Todo, {
        id: id
      }, void 0, false, {
        fileName: _jsxFileName,
        lineNumber: 82,
        columnNumber: 40
      }, _this);
    };
    return /*#__PURE__*/(0, _jsxDevRuntime.jsxDEV)(_reactNative.FlatList, {
      data: (0, _uiReact.useSortedRowIds)(TODO_TABLE, DONE_CELL),
      renderItem: renderItem,
      style: styles.todos
    }, void 0, false, {
      fileName: _jsxFileName,
      lineNumber: 85,
      columnNumber: 5
    }, _this);
  };

Compiled output (with react-compiler)

  var Todos = function () {
    _s3();
    var $ = _c(4);
    if ($[0] !== "fcf3e12218f2c29fcc1d829b4bc4126d77208f0170595d581b00c51bee03e168") {
      for (var $i = 0; $i < 4; $i += 1) {
        $[$i] = Symbol.for("react.memo_cache_sentinel");
      }
      $[0] = "fcf3e12218f2c29fcc1d829b4bc4126d77208f0170595d581b00c51bee03e168";
    }
    var t0;
    if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
      t0 = function (t1) {
        var id = t1.item;
        return /*#__PURE__*/_jsxDEV(Todo, {
          id: id
        }, void 0, false, {
          fileName: _jsxFileName,
          lineNumber: 82,
          columnNumber: 40
        }, _this);
      };
      $[1] = t0;
    } else {
      t0 = $[1];
    }
    var renderItem = t0;
    var t1 = useSortedRowIds(TODO_TABLE, DONE_CELL);
    var t2;
    if ($[2] !== t1) {
      t2 = /*#__PURE__*/_jsxDEV(FlatList, {
        data: t1,
        renderItem: renderItem,
        style: styles.todos
      }, void 0, false, {
        fileName: _jsxFileName,
        lineNumber: 85,
        columnNumber: 5
      }, _this);
      $[2] = t1;
      $[3] = t2;
    } else {
      t2 = $[3];
    }
    return t2;
  };
jamesgpearce commented 1 month ago

Eh, the safe area doesn't work that well... (and yes, I think I am using the right one!) image

Edit: got it working, but the documentation there could be improved... not just the hooks, but even the top-level SafeAreaView needs to be wrapped in a SafeAreaProvider. Probably works normally because everyone else is using the router that I think does that for you.

jamesgpearce commented 1 month ago

ok @brentvatne ready when you are!