BluePrint
BluePrint

Reputation: 2134

Get HTML from database into React

I have the following JSON from my test API:

{
    "/page-one": "<div><h1 className='subpage'>Database page one!</h1><p className='subpage'>Showing possibility of page one</p><p className='subpage'>More text</p></div>",
    "/page-two": "<div><h1 className='subpage'>Database page two!</h1><p className='subpage'>Showing possibility of page two</p><p className='subpage'>More text</p></div>",
    "/page-two/hej": "<div><h1 className='subpage'>Database page two supbage hej!</h1><p className='subpage'>Showing possibility of page two with subpage</p><p className='subpage'>More text</p></div>"
}

I want do create a dynamig page that uses the URL pathname to determine which HTML should be used/displayed. I have tried the following:

import React from 'react';

import Utils from "../../utils";
import './not-found-controller.css';

class GenericController extends React.Component {
    constructor(props) {
        super(props);

        this.getPage = this.getPage.bind(this);
    }

    async getPage() {
        const pageList = await Utils.http.get('http://demo5369936.mockable.io/pages');
        console.log(pageList.data);
        const possiblePages = Object.keys(pageList.data);

        if (possiblePages.indexOf(window.location.pathname) !== -1) {
            return pageList.data[window.location.pathname];
        } else {
            return (
                <div>
                    <h1 className="subpage">404 - Page not fpund!</h1>
                    <p className="subpage">The page you are looking for is not found. Please contact support if you thing this is wrong!</p>
                </div>
            );
        }
    }

    render() {
        return (
            <section className="not-found-controller">
                { this.getPage }
            </section>
        );
    }
}

export default GenericController;

But this does not work. I only get the following error:

Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.
    in section (at not-found-controller.js:31)
    in NotFoundController (at app.js:166)
    in Route (at app.js:164)
    in Switch (at app.js:88)
    in div (at app.js:87)
    in App (created by Route)
    in Route (created by withRouter(App))
    in withRouter(App) (at src/index.js:18)
    in Router (created by BrowserRouter)
    in BrowserRouter (at src/index.js:17)

The API call is correct, the console.log(pageList.data); prints the JSON correctly. I tried to search for how to include HTML like this but I did not find any answer that made me understand.

Upvotes: 4

Views: 7961

Answers (2)

John Weisz
John Weisz

Reputation: 31924

3 things wrong here, but your browser's console should've told you the errors already.

First, your getPage method is async, but React rendering passes should be synchronous and execute immediately with whatever data is present. If the data is not present, the component should still render (you can return null from render to render "nothing"). Instead, fetch your data outside of a render pass and call setState when the data is available.

Second, getPage is returning a HTML string, which is not going to be parsed this way, but rendered as-is. You need to use the dangerouslySetInnerHTML prop.

Third, as jitesh pointed out, you are missing the function call brackets.

All things considered, you need something like the following (just thrown this together real quick, but you should get the idea):

constructor(props, context) {
    super(props, context);
    this.state = {
        pageData: undefined
    };
}

componentDidMount() {
    this.getPage();
}

async getPage() {
    const pageList = await Utils.http.get('http://demo5369936.mockable.io/pages');

    this.setState({
        pageData: pageList.data[window.location.pathname]
    });
}

render() {
    if (this.state.pageData) {
        return (
            <section
                className="not-found-controller"
                dangerouslySetInnerHTML={{ __html: this.state.pageData }}
            />
        );
    } else {
        // Not found or still loading...
    }
}

Upvotes: 7

SLCH000
SLCH000

Reputation: 2223

1) Use state to put loaded data to render. Like this.setState({pageHtml: pageList.data})

2) Use dangerouslySetInnerHTML to render your own html https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml

Upvotes: 3

Related Questions