reactjs - React Native (expo) 加载 Markdown 文件

标签 reactjs react-native expo

我在将 markdown 文件 (.md) 加载到我的 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的建议答案,但问题似乎是它只接受.js.json文件

最佳答案

感谢 @Filipe 的回复,我得到了一些指导并得到了一个适合您需求的工作示例。

就我而言,我在 assets/markdown/ 文件夹中有一个 .md 文件,该文件名为 test-1.md >

诀窍是获取文件的本地url,然后使用fetch API 获取其字符串 形式的内容。

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>
    );
  }
}

编辑:为了消除错误

Unable to resolve "./assets/markdown/test-1.md" from "App.js"

您需要将 @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() 将阻止应用程序向该应用程序 session 中托管文件的服务器发出 XHR 调用(只要用户不关闭并重新打开应用程序) )。

如果您更改文件或修改文件(通过调用 Expo.FileSystem.writeAsStringAsync() 进行模拟),只要您的组件重新渲染并重新渲染,它就应该显示更新的内容。下载文件。

每次关闭并重新打开您的应用程序时都会发生这种情况,因为就我而言,file.localUri 不会在每个 session 中保留,因此您的应用程序将始终调用 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>
    );
  }
}

只需交替使用这三者即可查看日志中的差异。

关于reactjs - React Native (expo) 加载 Markdown 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52571450/

相关文章:

javascript - react | Ant design 选择默认值

javascript - 如何将 JSON 样式对象转换为 CSS 字符串?

reactjs - 观察者加载时间太长(NodeWatcher) - React Native

react-native - 如何在 React Native 中实现 'almost' 全屏模式?

javascript - 如何减少 expo 项目中的 apk 大小?

reactjs - 容器化的 ReactJs 应用程序(来自 nginx 图像)不提供所有路由

reactjs - react 测试渲染器错误: TypeError: Cannot read property 'offsetWidth' of undefined

javascript - 错误 : attempted to update component that has already been unmounted (or failed to mount)

ios - 在Windows 10上开发React-Native APP时是否可以打开IOS模拟器?

android - React Native + Expo - 触发 android Intent 打开本地存储的文件。 (安卓)