react native (expo) загрузка файлов уценки
у меня возникли проблемы с загрузкой файлов markdown (.md
) в my react native (отдельный проект 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
я попробовал предложенные ответы для reactjs на SO, но проблема, похоже, в том, что он принимает только .js
и .json
файлы
3 ответов
благодаря ответу @Filipe я получил некоторые рекомендации и получил рабочий пример, который будет соответствовать вашим потребностям.
в моем случае, у меня было на 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>
);
}
}
EDIT: чтобы избавиться от ошибки
невозможно разрешить ". /активы / уценка / тест-1.md "from" App.js"
вам нужно будет добавить packagerOpts
часть фрагмента @Filipe в ваш .
app.в JSON
{
"expo": {
...
"assetBundlePatterns": [
"**/*"
],
"packagerOpts": {
"assetExts": ["md"]
},
...
}
}
редактирование 2:
Ответ на комментарий @Norfeldt:
Хотя я использую react-native init
при работе над собственными проектами, и поэтому я не очень хорошо знаком с Expo, я получил эту закуску Expo, которая может иметь некоторые ответы для вас:https://snack.expo.io/Hk8Ghxoqm.
это не будет работать на EXPO snack из-за проблем с чтением файлов, отличных от JSON, но вы можете проверить его локально, если хотите.
используя file.downloadAsync()
предотвратит приложение, выполняющее вызовы XHR на сервер, где ваш файл размещен в этом сеансе приложения (до тех пор, пока пользователь не закроет и не откроет приложение).
если вы измените файл или измените файл (имитируется с помощью вызова Expo.FileSystem.writeAsStringAsync()
), он должен отображать обновленное, пока ваш компонент повторно отображает и повторно загружает файл.
это произойдет каждый раз, когда ваше приложение будет закрыто и снова открыто, как file.localUri
не сохраняется на сеансах, насколько я обеспокоен, поэтому ваше приложение всегда будет вызывать file.downloadAsync()
по крайней мере один раз каждый раз, когда он открывается. Таким образом, у вас не должно быть проблем с отображением обновленного файла.
Я также взял некоторое время, чтобы проверить скорость использования fetch
по сравнению с использованием Expo.FileSystem.readAsStringAsync()
, и они были в среднем одинаковы. Часто Expo.FileSystem.readAsStringAsync
была ~200 мс быстрее, но это не сделка прерыватель на мой взгляд.
Я создал три разных метода для извлечения одного и того же файла.
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>
);
}
}
просто чередуйте между тремя, чтобы увидеть разницу в журналах.
из того, что я знаю, это не может быть сделано в рамках expo. Я использую react-native и запускаю его на своем мобильном телефоне для разработки.
react-native
используйте Metro в качестве пакета по умолчанию, который также страдает от подобных проблем. Вы должны использовать перегона bundler вместо этого.
npm install --save-dev haul
npx haul init
npx haul start --platform android
в отдельном терминале react-native run-android
. Это будет использовать haul
вместо metro
объединить файлы.
чтобы добавить файл markdown, установите raw-loader редактировать . 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;
}
};
теперь вы можете импортировать файл Markdown с помощью const example = require('./example.md')
Haul поддерживает конфигурацию webpack, поэтому вы можете добавить любое пользовательское преобразование babel.
я точно не знаю, в чем проблема, но я добавил html-файлы в проект, и я бы предположил, что это будет очень похоже.
внутри вашего приложения.json, попробуйте добавить эти поля:
"assetBundlePatterns": [
"assets/**",
],
"packagerOpts": {
"assetExts": ["md"]
},
на packagerOpts
делает его таким образом, автономный будет связывать .файлы md. Я бы предположил, что у вас уже есть папка assets, но на всякий случай вам она понадобится.
затем на AppLoading
загрузка активов с Asset.loadAsync
возможно, не понадобится, но это хорошая идея, чтобы исключить. Проверьте документация о том, как использовать его.
при импорте файла есть три способа, которые вы можете захотеть сделать, это изменение в зависимости от среды. Я скопирую этот отрывок из моего средне-статьи:
в симуляторе, вы можете получить доступ к файлам в проекте. Таким образом,
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
, вот почему я решил проверить, было ли это частью этого)
это, вероятно, должно решить вашу проблему. Если это не так, прокомментируйте, что происходит не так, и я попытаюсь помочь вам дальше. Ура!