GUIPSS

👉 Browsing Articles Tagged : react - Go back to the home page

Create-React-App (CRA) generates javascript and css files with names that contains a hash that varies everytime we build them. That is fine when you use the provided index.html file but sometime your use case does not allows that. Here is two (not perfect, but good enough) ways to load those files in another html file than the one provided by default.

The first would be to load the css and javascript files in your page directly from the assets-manifest.json file that is genereted by CRA, for example here with jQuery (in ES5) (but that could be done without it also) :

<div id="root"></div>

<script type="text/javascript">
    var BASE_URL = '/';

    function loadCss (cssFiles) {
        cssFiles.forEach(function (css) {
            $('<link>')
                .appendTo('body')
                .attr({
                    type: 'text/css',
                    rel: 'stylesheet',
                    href: css,
                });
        });
    }

    function loadNextScript (scripts) {
        if (!scripts.length) { return; }

        var script = scripts.shift();
        $.ajax(script, {
            dataType: 'script',
            success: function () { loadNextScript(scripts) },
        })
    }

    $.ajax(BASE_URL + 'asset-manifest.json', {
        dataType: "json",
        success : function (data, textStatus, jqXHR) {

            if (data && data.entrypoints && data.entrypoints.length > 0) {
                var scripts =
                    data.entrypoints
                        .filter(function (resource) {
                            return resource.match(/.+\.js$/)
                        })
                        .map(function (script) {
                            return BASE_URL + script;
                        });
                var cssFiles =
                    data.entrypoints
                        .filter(function (ressource) {
                            return ressource.match(/.+\.css$/)
                        })
                        .map(function (file) {
                            return BASE_URL + file;
                        });

                loadCss(cssFiles);
                loadNextScript(scripts);
            }
        },
        error: function () {
            console.log('unable to load react app, manifest file not accessible or corrupted');
        }
    })

</script>

I guess you could also use the asset-manifest.json file to generate the bundle during the build process and end up with just one javascript file. You can also not use the manifest file and bundle the files beforehand by putting some scripts in your package.json file, for example you can loop on the generated files and concatenate then into a bundle file:

{
  "bundle-js": "cd build/static/js/ ; for each in runtime*.js; do cat $each; echo \"\\n\"; done > ../bundle.js ; for each in *.chunk.js; do cat $each; echo \"\\n\"; done >> ../bundle.js",
  "bundle-css": "cd build/static/css/ ; for each in *.css; do cat $each; echo \"\\n\"; done > ../bundle.css",
}

If you have to render a lot of data that changes often, using Mobx wisely could help you build a smooth and responsive ui. If you use mobx-react observer, you should try to tie the smallest component possible to the store and not the entire array.

If you have the following store, that represent a table of colors:

class DataStore {
  @observable dataMap: Map<string, string> = new Map();

  width = 50;
  height = 50;

  constructor () {
    let i = -1;

    while (i++ < this.width) {
      let j = -1;

      while (j++ < this.height) {
        this.dataMap.set(`${i}-${j}`, 'black');
      }
    }

  }

  updateRandomCell () {
    const x = Math.floor(Math.random() * this.width);
    const y = Math.floor(Math.random() * this.height);
    const key = `${x}-${y}`;

    this.dataMap.set(key, this.dataMap.get(key) === 'black' ? 'red' : 'black');
  }

}

To render this table efficiently, the best thing to do is, instead of doing one big component tht renders everything, to split the code so that every cell is its own component that connect directly to the store. That way, you don't have to do a full re-render everytime a cell data changes. Here is the example app and the two codes compared:

 
// Without Child Component (The Parent Component is Directly Connected to the Store):
const Square: React.FC<any> = observer(() => {
  return (
    <div>
      {
        Array.from({ length : store.width }).map((_, i) => {
          return (
            <div style={{ display: 'flex' }}>
              {
                Array.from({ length : store.height }).map((_, j) => {
                  return (
                    <div 
                      style={{
                        width: 3,
                        height: 3,
                        background: store.dataMap.get(`${i}-${j}`)
                      }}
                    >
                    </div>
                  )
                })
              }
            </div>
          )
        })
      }
    </div>
  )
})
// With Child Compponents Directly Connected to the Store:
const Cell: React.FC<any> = observer((props: any) => {
  return (
    <div 
      style={{
        width: 3,
        height: 3,
        background: store.dataMap.get(`${props.i}-${props.j}`)
      }}
      >
    </div>
  );
});
 
const SquareWithCells: React.FC<any> = observer(() => {
  return (
    <div>
      {
        Array.from({ length : store.width }).map((_, i) => {
          return (
            <div style={{ display: 'flex' }}>
              {
                Array.from({ length : store.height }).map((_, j) => {
                  return (
                    <Cell i={i} j={j}/>
                  )
                })
              }
            </div>
          )
        })
      }
    </div>
  )
})