web-dev-qa-db-fra.com

réagir aux fichiers de démarque de chargement natif (expo)

J'ai des problèmes pour charger des fichiers de démarques (.md) Dans mon natif réactif (projet d'exposition non détaché).

Trouvé ce package génial qui me permet de le rendre. Mais je ne sais pas comment charger le fichier local .md Sous forme de chaîne.

import react from 'react';
import {PureComponent} from 'react-native';
import Markdown from 'react-native-markdown-renderer';

const copy = `# h1 Heading 8-)

| Option | Description |
| ------ | ----------- |
| data   | path to data files to supply the data that will be passed into templates. |
| engine | engine to be used for processing templates. Handlebars is the default. |
| ext    | extension to be used for dest files. |
`;

export default class Page extends PureComponent {

  static propTypes = {};
  static defaultProps = {};

  render() {
    return (
        <Markdown>{copy}</Markdown>
    );
  }
}

BTW: J'ai essayé de googler, mais je n'arrive pas à faire fonctionner les suggestions

https://forums.expo.io/t/loading-non-media-assets-markdown/522/2?u=norfeldtconsulting

J'ai essayé les réponses suggérées pour reactjs sur SO, mais le problème semble être qu'il n'accepte que les fichiers .js Et .json

11
Norfeldt

Grâce à la réponse de @ Filipe, j'ai obtenu des conseils et obtenu un exemple de travail qui répondra à vos besoins.

Dans mon cas, j'avais un fichier .md Dans le dossier assets/markdown/, Le fichier s'appelle test-1.md

L'astuce consiste à obtenir un url local pour le fichier, puis à utiliser l'API fetch pour obtenir son contenu sous la forme d'un string.

import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Markdown from 'react-native-markdown-renderer';
const copy = `# h1 Heading 8-)

| Option | Description |
| ------ | ----------- |
| data   | path to data files to supply the data that will be passed into templates. |
| engine | engine to be used for processing templates. Handlebars is the default. |
| ext    | extension to be used for dest files. |
`;

export default class App extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      copy: copy
    }
  }

  componentDidMount() {
    this.fetchLocalFile();
  }

  fetchLocalFile = async () => {
    let file = Expo.Asset.fromModule(require("./assets/markdown/test-1.md"))
    await file.downloadAsync() // Optional, saves file into cache
    file = await fetch(file.uri)
    file = await file.text()

    this.setState({copy: file});
  }


  render() {
    return (
        <Markdown>{this.state.copy}</Markdown>
    );
  }
}

EDIT: Afin de se débarrasser de l'erreur

Impossible de résoudre "./assets/markdown/test-1.md" à partir de "App.js"

vous devez ajouter la partie packagerOpts de l'extrait de code @ Filipe dans votre fichier app.json.

app.json

{
  "expo": {
    ...
    "assetBundlePatterns": [
      "**/*"
    ],
    "packagerOpts": {
      "assetExts": ["md"]
    },
    ...
  }
}

EDIT 2: Répondre au commentaire de @ Norfeldt: Bien que j'utilise react-native init Lorsque je travaille sur mes propres projets, et que je ne suis donc pas très familier avec Expo, j'ai eu ce Snack Expo qui pourrait avoir des réponses pour vous: - https://snack.expo.io/Hk8Ghxoqm .

Cela ne fonctionnera pas sur le goûter expo en raison des problèmes de lecture des fichiers non JSON, mais vous pouvez le tester localement si vous le souhaitez.

L'utilisation de file.downloadAsync() empêchera l'application de faire des appels XHR vers un serveur sur lequel votre fichier est hébergé dans cette session d'application (tant que l'utilisateur ne ferme pas et ne rouvre pas l'application).

Si vous modifiez le fichier ou modifiez le fichier (simulé avec un appel à Expo.FileSystem.writeAsStringAsync()), il devrait afficher la mise à jour tant que votre composant restitue et télécharge à nouveau le fichier.

Cela se produira à chaque fois que votre application est fermée et rouverte, car le file.localUri N'est pas persistant par session en ce qui me concerne, donc votre application appellera toujours file.downloadAsync() au moins une fois à chaque ouverture. Vous ne devriez donc avoir aucun problème à afficher un fichier mis à jour.

J'ai également pris un certain temps pour tester la vitesse d'utilisation de fetch par rapport à l'utilisation de Expo.FileSystem.readAsStringAsync(), et ils étaient en moyenne les mêmes. Souvent, Expo.FileSystem.readAsStringAsync Était ~ 200 ms plus rapide, mais ce n'est pas un facteur décisif à mon avis.

J'ai créé trois méthodes différentes pour récupérer le même fichier.

export default class MarkdownRenderer extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      copy: ""
    }
  }

  componentDidMount() {
    this.fetch()
  }

  fetch = () => {
    if (this.state.copy) {
      // Clear current state, then refetch data
      this.setState({copy: ""}, this.fetch)
      return;
    }
    let asset = Expo.Asset.fromModule(md)
    const id = Math.floor(Math.random()  * 100) % 40;
    console.log(`[${id}] Started fetching data`, asset.localUri)
    let start = new Date(), end;

    const save = (res) => {
      this.setState({copy: res})
      let end = new Date();
      console.info(`[${id}] Completed fetching data in ${(end - start) / 1000} seconds`)
    }

    // Using Expo.FileSystem.readAsStringAsync.
    // Makes it a single asynchronous call, but must always use localUri
    // Therefore, downloadAsync is required
    let method1 = () => {
      if (!asset.localUri) {
        asset.downloadAsync().then(()=>{
          Expo.FileSystem.readAsStringAsync(asset.localUri).then(save)
        })
      } else {
        Expo.FileSystem.readAsStringAsync(asset.localUri).then(save)
      }
    }

    // Use fetch ensuring the usage of a localUri
    let method2 = () => {
      if (!asset.localUri) {
        asset.downloadAsync().then(()=>{
          fetch(asset.localUri).then(res => res.text()).then(save)
        })
      } else {
        fetch(asset.localUri).then(res => res.text()).then(save)
      }
    }

    // Use fetch but using `asset.uri` (not the local file)
    let method3 = () => {
      fetch(asset.uri).then(res => res.text()).then(save)
    }

    // method1()
    // method2()
    method3()
  }

  changeText = () => {
    let asset = Expo.Asset.fromModule(md)
    Expo.FileSystem.writeAsStringAsync(asset.localUri, "Hello World");
  }

  render() {
    return (
        <ScrollView style={{maxHeight: "90%"}}>
          <Button onPress={this.fetch} title="Refetch"/>
          <Button onPress={this.changeText} title="Change Text"/>
            <Markdown>{this.state.copy}</Markdown>
        </ScrollView>
    );
  }
}

Il suffit d'alterner entre les trois pour voir la différence dans les journaux.

9
Luis Rizo

D'après ce que je sais, cela ne peut pas être fait dans expo. J'utilise react-native et je l'exécute sur mon mobile pour le développement.

react-native Utilise Metro comme bundle par défaut, qui souffre également de problèmes similaires. Vous devez utiliser haul bundler à la place.

npm install --save-dev haul

npx haul init

npx haul start --platform Android

Dans un terminal séparé, exécutez react-native run-Android. Cela utiliserait haul au lieu de metro pour regrouper les fichiers.

Pour ajouter le fichier de démarque, installez raw-loader et éditez le fichier haul.config.js. raw-loader Importe n'importe quel fichier sous forme de chaîne.

Personnalisez votre haul.config.js Pour qu'il ressemble à ceci:

import { createWebpackConfig } from "haul";
export default {
 webpack: env => {
  const config = createWebpackConfig({
    entry: './index.js',
  })(env);
  config.module.rules.Push({
      test: /\.md$/,
      use: 'raw-loader'
   })
  return config;
 }
};

Vous pouvez maintenant importer le fichier de démarque en utilisant const example = require('./example.md')

Haul prend en charge la configuration du webpack afin que vous puissiez ajouter n'importe quelle transformation babel personnalisée que vous souhaitez.

2
illiteratewriter

Je ne sais pas exactement où se situe le problème, mais j'ai ajouté des fichiers html au projet, et j'imagine que ce serait très similaire.

Dans votre app.json, essayez d'ajouter ces champs:

"assetBundlePatterns": [
  "assets/**",
],
"packagerOpts": {
  "assetExts": ["md"]
},

Le packagerOpts le rend ainsi autonome, il regroupera les fichiers .md. J'imagine que vous avez déjà un dossier d'actifs, mais au cas où vous n'en auriez pas, vous en aurez besoin.

Ensuite, sur AppLoading, le chargement des ressources avec Asset.loadAsync Peut ne pas être nécessaire, mais c'est une bonne idée de l'exclure. Consultez la documentation sur la façon de l'utiliser.

Lors de l'importation du fichier, vous pouvez procéder de trois manières différentes, qui varient en fonction de l'environnement. Je vais copier cet extrait de mon article moyen :

Dans le simulateur, vous pouvez accéder à n'importe quel fichier du projet. Ainsi, source={require(./pathToFile.html)} fonctionne. Cependant, lorsque vous créez une version autonome, cela ne fonctionne pas tout à fait de la même manière. Je veux dire, au moins pour Android ce n'est pas le cas. Le Android webView ne reconnaît pas les asset:/// Uris pour une raison quelconque. Vous avez pour obtenir le chemin file:///. Heureusement, c'est très facile. Les ressources sont regroupées dans file:///Android_asset (Attention, n'écrivez pas les ressources), et Expo.Asset.fromModule(require(‘./pathToFile.html')).localUri renvoie asset:///nameOfFile.html. Mais ce n'est pas tout. Pour la première fois, cet uri sera correct. Cependant, après un certain temps, il se transforme en un autre schéma de fichiers et n'est pas accessible de la même manière. Au lieu de cela, vous ' Je dois accéder directement à localUri. Ainsi, la solution complète est:

/* Outside of return */
const { localUri } = Expo.Asset.fromModule(require('./pathToFile.html'));
/* On the webView */
source={
  Platform.OS === ‘Android’
  ? {
    uri: localUri.includes('ExponentAsset')
      ? localUri
      : ‘file:///Android_asset/’ + localUri.substr(9),
  }
  : require(‘./pathToFile.html’)
}

(Une partie constante de l'uri est ExponentAsset, c'est pourquoi j'ai choisi de vérifier si cela en faisait partie)

Cela devrait probablement résoudre votre problème. Si ce n'est pas le cas, commentez ce qui ne va pas et je vais essayer de vous aider davantage. À votre santé!

2
Filipe