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
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.
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.
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 lesasset:///
Uris pour une raison quelconque. Vous avez pour obtenir le cheminfile:///
. Heureusement, c'est très facile. Les ressources sont regroupées dansfile:///Android_asset
(Attention, n'écrivez pas les ressources), etExpo.Asset.fromModule(require(‘./pathToFile.html')).localUri
renvoieasset:///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é!