meliorence / react-native-render-html

iOS/Android pure javascript react-native component that renders your HTML into 100% native views
https://meliorence.github.io/react-native-render-html/
BSD 2-Clause "Simplified" License
3.47k stars 587 forks source link

Slow navigate to a screen contains 12k words (including table) #652

Open QuangBinhDinh opened 9 months ago

QuangBinhDinh commented 9 months ago

Decision Table

Good Faith Declaration

Description

I have a very long policy text which contains over 12k words (around 78k chars) on my website and I want to render it on the mobile app. When I try to navigate to the screen contains it, it took 5-6 seconds delay to navigate (video below)

https://github.com/meliorence/react-native-render-html/assets/43164997/77c56acd-fc62-4880-83f3-3083a143b5fb

Note that I also use @native-html/table-plugin to render table, but even without using it, the navigation is still laggy

Here is my implement code

import React, { memo } from 'react';
import { lightColor } from '@styles/color';
import { SCREEN_WIDTH } from '@util/index';
import RenderHTML, { RenderersProps, MixedStyleRecord, MixedStyleDeclaration } from 'react-native-render-html';
import { normalize } from '@rneui/themed';
import { useNavigateFromWebLink } from '@util/renderHTML';
import WebView from 'react-native-webview';
import TableRenderer, { cssRulesFromSpecs, defaultTableStylesSpecs, tableModel } from '@native-html/table-plugin';
import { base64Font } from '@util/base64';

const baseStyle: MixedStyleDeclaration = {
    fontFamily: 'Poppins-Regular',
    color: '#444444',
    fontSize: normalize(14),
};

const tagsStyles: MixedStyleRecord = {
    b: { fontFamily: 'Poppins-SemiBold', fontWeight: '500' },
    strong: { fontFamily: 'Poppins-Medium', fontWeight: '500' },
    a: { color: lightColor.secondary },
};

const cssRules =
    cssRulesFromSpecs({
        ...defaultTableStylesSpecs,
        linkColor: lightColor.secondary,
        fontFamily: 'Poppins-Regular',
        trOddBackground: 'white',
        fontSizePx: 14,
    }) +
    `
td {
  text-align: left;
  padding: 10px
}
table {
    width: ${SCREEN_WIDTH - 36}px !important;
    height: auto !important;
    border: 1px solid #ccc;
}
@font-face {
    font-family: 'Poppins-Regular';
    font-style: normal;
    font-weight: 500;
    src: url(data:font/ttf;base64,${base64Font});
}
`;
interface IProps {
    html: string;
    showTable?: boolean;
}

const RenderPrintervalHtml = ({ html, showTable }: IProps) => {
    const { navigateFromLink } = useNavigateFromWebLink();
    const renderersProps: Partial<RenderersProps> = {
        img: { enableExperimentalPercentWidth: true },
        a: {
            onPress: async (_, href) => {
                //console.log('Link clicked', href);
                navigateFromLink(href);
            },
        },
        table: {
            cssRules,
            computeContainerHeight() {
                return null;
            },
        },
    };

    const newHtml = showTable
        ? html.replace(/<table.+?>/, `<table border="1">`)
        : html.replace(/<table\b[^>]*>[\s\S]*?<\/table>/g, '');

    return (
        <RenderHTML
            {...(showTable && {
                customHTMLElementModels: { table: tableModel },
                WebView,
                renderers: { table: TableRenderer },
            })}
            contentWidth={SCREEN_WIDTH}
            renderersProps={renderersProps}
            baseStyle={baseStyle}
            tagsStyles={tagsStyles}
            enableExperimentalMarginCollapsing
            source={{ html: newHtml }}
            systemFonts={['Poppins-Regular', 'Poppins-Medium', 'Poppins-Bold', 'Poppins-SemiBold']}
        />
    );
};

export default memo(RenderPrintervalHtml);

Any solution to deal with this scenario would be appreciated !

React Native Information

react-native version: 0.72.4

RNRH Version

react-native-render-html version: 6.3.4 @native-html/table-plugin version : 5.3.1

Tested Platforms

Reproduction Platforms

Minimal, Reproducible Example

I try to create an exact version of this scenario but seem I can't (Snack install @react-navigation/native failed) . Also my RenderHTML is using custom fonts and base64 font (you can see in the code) so I don't know how to do it with Snack. But I already created a simple version of this, you can check it here: https://snack.expo.dev/@quangbinh1999/rnrhtml-template. The "very long html" is fetched from my server and you can find it in html variable. I hope this does some help.

Additional Notes

No response

SashaGo3 commented 4 months ago

Having similar issue, that is more related to react-navigation pre-rendering

I handled it by showing loader.

import { useFocusEffect } from '@react-navigation/native'

export const Screen = () => {
  const [isReady, setIsReady] = React.useState(false)

  useFocusEffect(
    useCallback(() => {
      setTimeout(() => setIsReady(true), 1000)

      return () => setIsReady(false)
    }, []),
  )

  if (!isReady) {
    return <Loader />
  }

  return <HeavyComponent /> 

}