React-native: Suspense: not yet implemented

Created on 12 Dec 2018  路  10Comments  路  Source: facebook/react-native

Environment

[skip envinfo]

{
  "private": true,
  "main": "./app-entry.js",
  "scripts": {
    "dev": "expo start",
    "prod": "expo start --no-dev"
  },
  "dependencies": {
    "expo": "^31.0.2",
    "react": "^16.7.0-alpha.2",
    "react-cache": "^2.0.0-alpha.1",
    "react-native": "https://github.com/expo/react-native/archive/sdk-31-with-hooks-dangerzone.tar.gz"
  },
  "devDependencies": {
    "babel-preset-expo": "^5.0.0",
    "expo-cli": "^2.6.12"
  }
}

Description

When trying to use Suspense on React Native, I am receiving a Not yet implemented issue, after the react-cache package has resolved the data.

Reproducible Demo

import React, { useState, ConcurrentMode, Suspense } from "react";
import { StyleSheet, SafeAreaView, Button, Text } from "react-native";
import { unstable_createResource as createResource } from "react-cache";

const myPokemon = createResource(function fetchPokemon(name) {
  const pokemonQuery = `
      query ($name: String) {
        pokemon(name: $name) {
          id
          number
          name
        }
      }
    `;

  return fetch("https://graphql-pokemon.now.sh", {
    method: "POST",
    headers: {
      "content-type": "application/json;charset=UTF-8"
    },
    body: JSON.stringify({
      query: pokemonQuery,
      variables: { name }
    })
  })
    .then(r => r.json())
    .then(response => response.data.pokemon);
});

function PokemonInfo({ pokemonName }) {
  const pokemon = myPokemon.read(pokemonName);
  console.log(pokemon);

  return <Text>{JSON.stringify(pokemon || "Unknown", null, 2)}</Text>;
}

export default function App() {
  const [pokemonName, setPokemonName] = useState(null);
  const [search, setSearch] = useState(false);

  return (
    <SafeAreaView style={styles.container}>
      <Button title="Get Pikachu" onPress={() => setPokemonName("Pikachu")} />
      {pokemonName ? (
        <Suspense fallback={<Text>loading...</Text>}>
          <PokemonInfo pokemonName={pokemonName} />
        </Suspense>
      ) : null}
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    backgroundColor: "#fff",
    flex: 1,
    margin: 20
  },
  textInput: {
    backgroundColor: "#eaeaea",
    borderColor: "#cccccc",
    borderWidth: 1,
    height: 40,
    padding: 10
  }
});
Bug React Locked

Most helpful comment

@Taylor123 not necessary, you can use Suspense not to lazy load components, but for suspending rendering when for example, making a http request.

All 10 comments

Can you run react-native info and edit your issue to include these results under the Environment section?

If you believe this information is irrelevant to the reported issue, you may write [skip envinfo] under Environment to let us know.

Haven't used Suspense yet, but based off the docs it looks like you need to use it with React.lazy

https://reactjs.org/docs/react-api.html#reactsuspense

Today, lazy loading components is the only use case supported by <React.Suspense>

@Taylor123 not necessary, you can use Suspense not to lazy load components, but for suspending rendering when for example, making a http request.

Yes, @Taylor123 we can use react suspense functionality without react.lazy as far as in the context of web. now trying to use in react-native to which i'm facing the same issue.

Any idea if this is on the roadmap / coming any time soon?

I got the reproduceable demo above (minus the react-cache - did my own test cache) to work in 0.59.0-rc.3.

It's working in progress, I think that soon will be stable.

react-native/Libraries/Renderer/oss/ReactNativeRenderer-dev.js

Line 4319 in d4ce846

function unhideInstance(instance, props) {
Thank you React-Native Team!

I'm using this implementation to test my apps, and so far I could go it's working well. I'm found some troubles with expo integration.

Note: 0.59-rc was shipped with support to hooks

This should be working in the latest release - if it's not please let us know and we can re-open

Hi, I'm using react-native 0.59.5 and tried to implement Suspense but it never falls back to the fallback component
ProductsList.js

function ProductsList() {
  const { navigate } = useNavigation()
  const mobileNumber = useNavigationParam("mobileNumber")
  const [departmentData, departmentFetching, departmentError] = useRequest(() =>
    webApi.api.getCallBackDepartments()
  )
  const rows = getRows({ departments: departmentData ? departmentData : [] })

  const handleButtonPress = department => {
    navigate("SelectCallBackTimeScreen", {
      mobileNumber: mobileNumber,
      department: department
    })
  }

  console.log("departmentData", !departmentData.length)
  if (!departmentData.length) {
    return null
  }

  return (
    <View style={{ paddingHorizontal: 10, flex: 1 }}>
      {rows.map(value => (
        <View
          key={shortid.generate()}
          style={[styles.bodyCardRow, { marginTop: -10, marginBottom: 10 }]}
        >
          <View style={styles.bodyCardColumn}>
            <Card
              handlePress={() => handleButtonPress(departmentData[2 * value])}
              name={departmentData[2 * value] ? departmentData[2 * value].name : "test"}
            />
          </View>
          {departmentData[2 + 2 * value] && (
            <Card
              handlePress={() => handleButtonPress(departmentData[1 + 2 * value])}
              name={departmentData[1 + 2 * value] && departmentData[1 + 2 * value].name}
            />
          )}
        </View>
      ))}
    </View>
  )
}

selectAproduct.js

import React, { useEffect, Suspense, lazy } from "react"
import { connect } from "react-redux"
import { BackHandler, SafeAreaView, View } from "react-native"

import { useNavigation } from "react-navigation-hooks"
import BaseStyles from "../../Styles/App"
import CustomHeader from "../../../Components/CustomHeader"
import GoBack from "../../../Components/GoBack"
import Loader from "../../../Components/Loader"
import Text from "../../../Components/Text"
const ProductsLazy = lazy(() => import("./ProductsList"))

function SelectAProduct() {
  const { pop } = useNavigation()

  useEffect(() => {
    const backPressed = () => {
      pop()
      return true
    }
    BackHandler.addEventListener("hardwareBackPress", backPressed)
    return () => {
      BackHandler.removeEventListener("hardwareBackPress", backPressed)
    }
  }, [pop])

  return (
    <View style={BaseStyles.applicationView}>
      <CustomHeader
        popBack={() => pop()}
        left={<GoBack backScreen={"Call Me Back"} />}
        heading={"Select a Product"}
      />
      <Suspense fallback={<Text>loading ...</Text>}>
        <ProductsLazy />
      </Suspense>
      <SafeAreaView />
    </View>
  )
}

function Loading({ departmentFetching }) {
  return (
    <View style={BaseStyles.applicationView}>
      <Loader animating={departmentFetching} />
    </View>
  )
}

const mapStateToProps = state => ({
  // cart: state.cart.items
})

export default connect(
  mapStateToProps,
  {}
)(SelectAProduct)

Was this page helpful?
0 / 5 - 0 ratings