skip to Main Content

Bonjour!

I want to create an mobile App which renders content written in markdown, similar to Obsidian. So far I successfully used the library react-native-markdown-display to render markdown pages with non-interactive content, e.g. text, images and son on. Now I want to add interactive markdown content with custom rules, e.g. a map view, which needs hooks to work properly. Because the pages can change by user input and each page has different (interactive) content, I run in the problem of “Rendered more hooks than during the previous render”.

What would be the best strategy to tackle this problem?

While playing around I found out that if I create two react components Markdown and Markdown1, which basically are the same components just with a different name. I can give each component different markdown content and conditionally render one or the other, without error. Would this be a good way to solve this problem? If yes how does react differentiate react components? How could I dynamically create Markdown components for different interactive content, which react could differentiate?

Would creating a screen in react-navigation dynamically for every markdown page be a good solution?

2

Answers


  1. I also dragged into same problem, but I resolved by split components

    When the error happen code like this:

    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
    

    Error not coming after spliting components like this

    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
    
    Login or Signup to reply.
  2. Conditional/dynamic rendering

    You can not call hooks conditionally. Calling hooks in loops, although I think it could be legal, is also at least shady and should be avoided. React Hooks must be called in the exact same order in every component render. Call them at the top level of a component (i.e. don’t nest it inside of anything more than the component ifself).

    More on topic of "Rendered more hooks than during the previous render" error

    I did ask if it’s the useState hook which causes the error because I suppose you did something like below:

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

    Changing state of components

    How could I dynamically create Markdown components for different interactive content

    I don’t know anything about react-native-markdown-display, but I checked out their documentation and it seems that if you want to change what content the Markdown component displays you just provide a different data to it. Let’s say you’ve got two constants holding markdown and two buttons which let you change which one is used:

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

    Instead of constants these could be files, data fetched from a server, user input – sky is the limit. What matters is what setMarkdown is called with, which in turn determines what sits in the state and then is passed to the Markdown component.

    Rendering lists

    If you need to display a different set of elements based on some arbitrary data there is a number of ways you can achieve it.

    You hold data in the state, like this:

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

    then you map the data to elements, e.g. like this:

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

    I don’t know where your markdown comes from so I will just provide a general way of adding some to the list:

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

    If you hold an object or an array in the state, se sure to create a new one each time you want to update it since React compares references to tell if a change was made and does not examine contents of them.

    A classic example is a Todo List. I encourage you to check out some examples. Here is a full example of Todo list on codesandbox.

    Component tree

    how does react differentiate react components?

    It creates a virtual DOM tree. Elements of this tree correspond to elements displayed on the screen.

    Also, JSX is transpiled to React.createElement calls:

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

    You can see that 'h1' was passed to createElement so this is another piece by which React differenciates components – by the function (or tag) which is used to create the DOM tree.

    In the example above I used a map to create an arbitrary set of elements. There is a caveat to that.

    JSX elements directly inside a map() call always need keys!

    (React docs on rendering lists)

    The key is also used to differenciate elements. React holds data for each element and the key is necessary to assign state to the element which owns it. I used index as key which is fine as long as you don’t perform any manipulation on the list other than adding to the end. If, for example, you would remove an element from the beginning of the list and the indices would shift, the states would not shift with them and second element would get state from the first, third from second and so on. The key needs to be unique. IDs and hashes are good as keys because they are usually unique.

    Disclaimer

    You didn’t provide and code so I had to guess some things and I’m not certain if anyhing from what I wrote above is going to be any helpful. Feel free to ask furher questions.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search