使用 Hooks 在交互式 React 代码中动态渲染 Markdown 内容(钩子数量/顺序不同问题!

Render dynamically Markdown Content in interactive React Code with Hooks (Different number/order of hooks problem!)

提问人:bi3ri 提问时间:11/14/2023 最后编辑:bi3ri 更新时间:11/17/2023 访问量:64

问:

你好!

我想创建一个移动应用程序,它呈现用 markdown 编写的内容,类似于 Obsidian。到目前为止,我成功地使用该库来渲染包含非交互式内容的 Markdown 页面,例如文本、图像和 son-on。现在我想添加带有自定义规则的交互式 Markdown 内容,例如地图视图,它需要钩子才能正常工作。由于页面可以根据用户输入进行更改,并且每个页面都有不同的(交互式)内容,因此我遇到了“渲染的钩子比之前渲染期间更多的钩子”的问题。react-native-markdown-display

解决这个问题的最佳策略是什么?

在玩的时候,我发现如果我创建两个 react 组件和 ,它们基本上是相同的组件,只是名称不同。我可以为每个组件提供不同的 Markdown 内容,并有条件地呈现一个或另一个,而不会出错。这是解决这个问题的好方法吗?如果是,react 如何区分 react 组件?我怎样才能为不同的交互式内容动态创建组件,而这些组件可以区分哪些 react?MarkdownMarkdown1Markdown

为每个 Markdown 页面动态创建一个屏幕是一个好的解决方案吗?react-navigation


编辑:在 React-Navigation 中,屏幕不能在运行时动态添加,但它不会改变原来的问题。

我详细阐述了 和 方法,发现您还可以分配密钥,这似乎以类似的方式工作。我在这里提出了一个关于这个问题的新问题MarkdownMarkdown1

reactjs react-native react-hooks markdown react-navigation

评论

0赞 Iorweth333 11/14/2023
我想我知道你的问题是什么,但为了确定,我想看看抛出错误的代码段。有问题的钩子是什么?是useState吗?
0赞 Ashley Coolman 11/14/2023
我认为您可能粘贴了错误的库 - “react-native-render-display”用于渲染到外部屏幕
1赞 bi3ri 11/15/2023
@ lorweth33 :钩子的种类无关紧要,因为错误@AshleyCoolman:是的,谢谢我编辑了我的问题。

答:

0赞 Dineshkumar T 11/14/2023 #1

我也拖入了同样的问题,但我通过拆分组件解决了

当错误发生时,代码如下:

import React, { useState } from 'react'
import { ScrollView, StyleSheet, Text, TouchableOpacity, View, 
useWindowDimensions } from 'react-native';
import InsureIcon from './InsureIcon';
import { icons } from '../constants';
import InputText from './common/InputText';
import { ButtonLoad } from './common/ButtonLoad';

const InsureDetails = ({handleChange,proposerDetail}) => {
const [addMoreMembers,setAddMoreMembers]=useState(false)
const handleAddMember=()=>{
setAddMoreMembers(true)
  }
return (
  <ScrollView>
    <Text style={{fontSize:35,fontFamily:'serif',fontWeight:'bold', 
  marginVertical:30,color:'#696ac3'}}>
      Select members to insure
    </Text>
    <View style={styles.insurers}>
      <InsureIcon icon={icons.person} type="self" />
      <InsureIcon icon={icons.female} type="spouse" />
      <InsureIcon icon={icons.son} type="son" />
      <InsureIcon icon={icons.daughter} type="daughter" />
      <InsureIcon icon={icons.father} type="father" />
      <InsureIcon icon={icons.mother} type="mother" />
      {addMoreMembers &&
      <>
      <InsureIcon icon={icons.father} type="grand-father" />
      <InsureIcon icon={icons.mother} type="grand-mother" />
      </>
      }

      {!addMoreMembers &&
      <View style={{width:useWindowDimensions().width*.8,marginHorizontal:useWindowDimensions().width*.1}}>
        <ButtonLoad onPress={()=>handleAddMember()} title='Add more members'/>
      </View>}

    </View>
    <InputText
      label="Age of Eldest Member"
      icon={icons.calender}
      id='age'
      type={'numeric'}
      onChangeText={({ value }) => handleChange(value, id='age')}
      value={proposerDetail.age}

      />
  </ScrollView>
 );
 };

export default InsureDetails

像这样拆分组件后没有出现错误

import React, { useState } from 'react'
import { ScrollView, StyleSheet, Text, TouchableOpacity, View, 
useWindowDimensions } from 'react-native';
import InsureIcon from './InsureIcon';
import { icons } from '../constants';
import InputText from './common/InputText';
import { ButtonLoad } from './common/ButtonLoad';

const MoreMember=()=>{
return (
<>
<InsureIcon icon={icons.father} type="grand-father" />
<InsureIcon icon={icons.father} type="grand-father" />
</>
)
}

const AddMember=({handleAddMember})=>{
return <View style= 
{{width:useWindowDimensions().width*.8, 
marginHorizontal:useWindowDimensions().width*.1}}>
<ButtonLoad onPress={handleAddMember} title='Add more members'/>
</View>
}

const InsureDetails = ({handleChange,proposerDetail}) => {
const [addMoreMembers,setAddMoreMembers]=useState(false)
const handleAddMember=()=>{
setAddMoreMembers(true)
}
return (
  <ScrollView>
    <Text style={{ fontSize:35,fontFamily:'serif',fontWeight:'bold', 
marginVertical:30,color:'#696ac3'}}>
      Select members to insure
    </Text>
    <View style={styles.insurers}>
      <InsureIcon icon={icons.person} type="self" />
      <InsureIcon icon={icons.female} type="spouse" />
      <InsureIcon icon={icons.son} type="son" />
      <InsureIcon icon={icons.daughter} type="daughter" />
      <InsureIcon icon={icons.father} type="father" />
      <InsureIcon icon={icons.mother} type="mother" />
     {addMoreMembers&& <MoreMember/>}

      {!addMoreMembers && <AddMember handleAddMember={handleAddMember}/>
      }

    </View>
    <InputText
      label="Age of Eldest Member"
      icon={icons.calender}
      id='age'
      type={'numeric'}
      onChangeText={({ value }) => handleChange(value, id='age')}
      value={proposerDetail.age}

      />
  </ScrollView>
  );
 };

 export default InsureDetails

评论

0赞 Iorweth333 11/14/2023
这不是要走的路,因为它不是真正动态的。您可以添加或不添加两个成员,但不能添加任何其他数量的成员。
0赞 Iorweth333 11/14/2023
此外,我看不出“渲染的钩子比上一个渲染期间的钩子多”错误会出现在哪里,在第一个示例和第二个示例中都是如此 - 您确定您在那里得到了它吗?
0赞 Iorweth333 11/16/2023 #2

条件/动态渲染

你不能有条件地调用钩子。在循环中调用钩子,虽然我认为这可能是合法的,但至少也是阴暗的,应该避免。React Hooks 必须在每个组件渲染中以完全相同的顺序调用。在组件的顶层调用它们(即不要将其嵌套在组件 ifself 以外的任何内容中)。

有关“渲染的钩子比上一次渲染期间更多”错误主题的更多信息

我确实问过是否是钩子导致了错误,因为我想您做了如下操作:useState

if (showAnotherMarkdown) {
  const [markdown, setMarkdown] = React.useState();
}

更改组件的状态

如何为不同的交互式内容动态创建 Markdown 组件

我对此一无所知,但我查看了他们的文档,似乎如果您想更改 Markdown 组件显示的内容,您只需为其提供不同的数据即可。假设你有两个常量来保存 markdown 和两个按钮,用于更改使用哪一个:react-native-markdown-display

const markdown1 = `# h1 This is a markdown`;
const markdown2 = `# h1 This is another markdown`;

const [markdown, setMarkdown] = React.useState(markdown1);

return (
  <div>
    My Markdown:

    <Markdown>
      {markdown}
    </Markdown>

    <Button onClick={() => setMarkdown(markdown1)}>
      show first markdown 
    </Button>

    <Button onClick={() => setMarkdown(markdown2)}>
      show the other markdown
    </Button>
  </div>
);

这些不是常量,而是文件、从服务器获取的数据、用户输入 - 天空是极限。重要的是调用什么,这反过来又决定了什么位于状态中,然后传递给组件。setMarkdownMarkdown

呈现列表

如果需要根据某些任意数据显示一组不同的元素,可以通过多种方式实现。

您以状态保存数据,如下所示:

const [markdownList, setMarkdownList] = React.useState([]);

然后将数据映射到元素,例如:

return (
  <div>
    Markdown list:
    
    {markdownList.map((markdown, index) => (
      <Markdown key={index}>
        {markdown}
      </Markdown>
    ))}
    
  </div>
);

我不知道你的 markdown 是从哪里来的,所以我只提供一种将一些添加到列表中的一般方法:

function addMarkdown(newMarkdown) {
  setMarkdownList(prevList => [...prevList, newMarkdown]); //appending to the end 
}

如果你将一个对象或数组保持在状态中,请确保每次要更新它时创建一个新对象或数组,因为 React 会比较引用以判断是否进行了更改,并且不检查它们的内容。

一个典型的例子是待办事项列表。我鼓励你看看一些例子。这是 codesandbox 上待办事项列表的完整示例

组件树

React 如何区分 React 组件?

它创建一个虚拟 DOM 树。此树的元素与屏幕上显示的元素相对应。

此外,JSX 被转译为调用:React.createElement

const element = (
  <h1 className="greeting">
    Witaj, świecie!
  </h1>
);

const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Witaj, świecie!'
);

你可以看到它被传递给了,所以这是 React 区分组件的另一部分 - 通过用于创建 DOM 树的函数(或标签)。'h1'createElement

在上面的示例中,我使用映射来创建任意一组元素。有一点需要注意。

直接在 map() 调用中的 JSX 元素总是需要键!

(渲染列表上的 React 文档)

也用于区分元素。React 保存每个元素的数据,并且需要将状态分配给拥有它的元素。我用了as,只要你不对列表执行任何操作,除了添加到末尾之外,这很好。例如,如果要从列表的开头删除一个元素,并且索引会移动,则状态不会随之移动,并且第二个元素将从第一个元素获取状态,从第二个元素获取第三个元素,依此类推。需要是独一无二的。ID 和哈希值之所以好,是因为它们通常是唯一的。keykeyindexkeykeykey

免責聲明

你没有提供和编码,所以我不得不猜测一些事情,我不确定我上面写的任何内容是否有任何帮助。请随时提出更深层次的问题。