0

Thanks for taking time to read this.

I am new to React and I am struggling a bit to render dynamically components. Object received from API can be arbitrary nested to x number of levels and component names that need to be injected are received from BE.

I am getting response from API that looks something like this :

{
  name: 'div'
  components: [
    componentOne: {
      name: 'div'
      components: [
        {
          name: 'p',
          components: [...deeply nested]
        },
        {
          name: 'h1',
          components: [...deeply nested]
        }
      ]
    }
    componentTwo: {
      name: 'nav',
      components: [...]
    }
  ] 
}
2
  • 3
    Hi, I feel there is no clear question here. Can you describe further what you want to do, what you tried and what was your issue when trying? Commented Mar 5, 2019 at 9:39
  • Hi @remix, basically goal is to render components based on API response. So from the example posted I would need to render smtgh like this: <div> <p></p> <h1></h1> <div/> <nav></nav> The main problem is that I can't know how deep nesting will be, because user has ability to build his UI in CMS and to add as much components he wants in any structure he wants. So recursion was an option, but I am having problem to figure out the way when injecting components dynamically as children of components that were dynamically injected. (I hope i did not confuse you even more) Commented Mar 5, 2019 at 9:53

1 Answer 1

2

You will have to handle different kind of node (at least text) but it is just a matter of walking down a tree (the API response) and build a React node tree according to it:

so your node definition looks like this (this is pseudo grammar and not meant to be in your code):

    node: {
        type: 'element' | 'text' => the type will be needed,
        name: string => a tag name (if it is a React element),
        children: node[]
    }

So an actual response from your api could be:

    {
        type: 'element',
        name: 'div',
        children: [
            {
                type: 'element',
                name: 'div',
                children: [
                    {
                        type: 'element',
                        name: 'h1',
                        children: [
                            { type: 'text', value: 'Title 1' }
                        ]
                    },
                    {
                        type: 'element',
                        name: 'p',
                        children: [
                            { type: 'text', value: 'This is a paragraph' }
                        ]
                    }
                ]
            },
            {
                type: 'element',
                name: 'nav',
                children: []
            }
        ]

    }

We will pass this response parsed as an object to a generateTreeNode method:

    /**
     * @param {object} apiResponse
     * @returns {ReactNode}
     */
    function generateTreeNode(apiResponse) {
        switch (apiResponse.type) {
            case 'element':
                return React.createElement(apiResponse.name, { children: apiResponse.children.map(child => generateTreeNode(child)) });
            case 'text':
                return apiResponse.value;
            default: // no default
        }
    }

Note the recursive call will ensure the whole apiResponse tree is walked.

That's it. This is untested but should work quite nicely: the return of the generateTreeNode function can be used as the return value of a render method.

The main ressources you should understand to use it are what is a React Node and creating a react node.

Also, feel free (and you should) to extend your api response structure and walking to handle:

  • custom components (that you will have to import somehow)
  • additional elements props (such a style or classNames)
  • etc...

Note also that this code assumes that the root of your response is one node (remember how React do not let you return several nodes at once in the render method, array and fragments aside, that's why)

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.