マークダウンファイル(.md
)をreactネイティブ(非分離expoプロジェクト)にロードするときに問題が発生します。
レンダリングできる素晴らしいパッケージを見つけました。しかし、ローカル.md
ファイルを文字列としてロードする方法を理解できません。
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>
);
}
}
ところで、グーグルで試しましたが、提案が機能しません
https://forums.expo.io/t/loading-non-media-assets-markdown/522/2?u=norfeldtconsulting
私はSOでreactjsの提案された答えを試しましたが、問題はそれが.js
と.json
ファイルしか受け入れないということです
@Filipeの応答のおかげで、私はいくつかのガイダンスを受け取り、あなたのニーズに合う実用的な例を得ました。
私の場合、_.md
_フォルダーに_assets/markdown/
_ファイルがあり、このファイルは_test-1.md
_と呼ばれています
コツは、ファイルのローカルurl
を取得し、fetch
APIを使用してそのコンテンツを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>
);
}
}
_
編集:エラーを取り除くために
「App.js」から「./assets/markdown/test-1.md」を解決できません
@FilipeのスニペットのpackagerOpts
部分を_app.json
_ファイルに追加する必要があります。
app.json
_{
"expo": {
...
"assetBundlePatterns": [
"**/*"
],
"packagerOpts": {
"assetExts": ["md"]
},
...
}
}
_
編集2:@Norfeldtのコメントへの回答:自分のプロジェクトで作業するときは_react-native init
_を使用しているため、Expoについてはあまり詳しくありませんが、いくつかの答えがあるかもしれないこのExpo Snackを入手しました:- https://snack.expo.io/Hk8Ghxoqm 。
JSON以外のファイルの読み取りに問題があるため、エキスポスナックでは機能しませんが、必要に応じてローカルでテストできます。
file.downloadAsync()
を使用すると、ファイルがホストされているサーバーへのXHR呼び出しがアプリによって行われなくなります(ユーザーがアプリを閉じて再度開かない限り)。
ファイルを変更または変更した場合(Expo.FileSystem.writeAsStringAsync()
の呼び出しでシミュレート)、コンポーネントがファイルを再レンダリングして再ダウンロードする限り、更新されたファイルが表示されます。
_file.localUri
_はセッションごとに永続化されていないため、これはアプリが閉じて再度開くたびに発生するため、アプリは常にfile.downloadAsync()
を少なくとも1回呼び出します開くたびに。したがって、更新されたファイルの表示に問題はありません。
また、fetch
を使用した場合とExpo.FileSystem.readAsStringAsync()
を使用した場合の速度をテストするのにも少し時間がかかりましたが、平均は同じでした。多くの場合、_Expo.FileSystem.readAsStringAsync
_は200ミリ秒高速でしたが、私の意見では、それは取引ブレーカーではありません。
同じファイルを取得するための3つの異なる方法を作成しました。
_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>
);
}
}
_
ログの違いを確認するには、3つを交互に切り替えます。
私が知っていることから、これは博覧会の中で行うことはできません。私はreact-nativeを使用し、開発用にモバイルで実行します。
_react-native
_デフォルトのバンドラーとしてMetroを使用しますが、これも同様の問題を抱えています。代わりに haul bundlerを使用する必要があります。
_npm install --save-dev haul
_
_npx haul init
_
_npx haul start --platform Android
_
別のターミナルで_react-native run-Android
_を実行します。これは、haul
の代わりにmetro
を使用してファイルをバンドルします。
マークダウンファイルを追加するには、 raw-loader をインストールし、_haul.config.js
_ファイルを編集します。 _raw-loader
_は、任意のファイルを文字列としてインポートします。
_haul.config.js
_を次のようにカスタマイズします。
_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;
}
};
_
これで、const example = require('./example.md')
を使用してマークダウンファイルをインポートできます
HaulはWebpack構成をサポートしているため、必要に応じてカスタムバベル変換を追加できます。
問題がどこにあるのか正確にはわかりませんが、htmlファイルをプロジェクトに追加しましたが、非常に似ていると思います。
App.json内で、次のフィールドを追加してみてください。
_"assetBundlePatterns": [
"assets/**",
],
"packagerOpts": {
"assetExts": ["md"]
},
_
packagerOpts
を使用すると、スタンドアロンで.mdファイルがバンドルされます。すでにアセットフォルダがあると思いますが、持っていない場合に備えて、必要になります。
次に、AppLoading
で_Asset.loadAsync
_を使用してアセットをロードする必要はないかもしれませんが、除外することをお勧めします。使い方は documentation をご覧ください。
ファイルをインポートする場合、3つの方法があり、環境によって異なります。この抜粋を私の 中程度の記事 からコピーします:
シミュレータでは、プロジェクト内の任意のファイルにアクセスできます。したがって、
source={require(./pathToFile.html)}
は機能します。ただし、スタンドアロンを構築すると、まったく同じようには機能しません。つまり、少なくともAndroidは認識しません。Android webViewは何らかの理由で_asset:///
_ urisを認識しません。 _file:///
_パスを取得します。ありがたいことに、これは非常に簡単です。アセットは_file:///Android_asset
_内にバンドルされており(慎重に、アセットを記述しないでください)、Expo.Asset.fromModule(require(‘./pathToFile.html')).localUri
は_asset:///nameOfFile.html
_を返します。しかし、それだけではありません。最初の数回は、このURIは正しくなります。ただし、しばらくすると、別のファイルスキームに変更され、同じ方法でアクセスできなくなります。代わりに、 localUriを直接使用するため、完全なソリューションは次のとおりです。
_/* 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’)
}
_
(URIの定数部分は
ExponentAsset
です。そのため、それがその一部であるかどうかを確認することにしました)
それはおそらくあなたの問題を解決するはずです。表示されない場合は、問題点をコメントしてください。さらにサポートさせていただきます。乾杯!