When nesting routes, recent versions of React Router encourage you to declare child routes directly in the component they will render into. Although it is a perfectly valid way of doing things, it gets a bit more complicated if you are defining all your routes in a separate configuration file to begin with. Documentation includes an example of such a config, but it does not cover some pretty essential use cases, such as redirects, or routes using a render function rather than a static component declaration. Let's dive in and see how to do those!
First, let's picture a basic app looking something like the following:
import React from "react";
import { Switch } from "react-router-dom";
import Home from "./components/Home";
import Hello from "./components/Hello";
import World from "./components/World";
Now, imagine we want our World component to actually be rendered within the Hello component, which would better reflect the URL it is accessed at and allow it to share part of its newfound parent's markup, but we want to keep all routes declarations in our config, like so:
To make this work, we need the routes object to be passed down to our Hello component, which we can achieve by superseding the standard Route component:
import React from "react";
import { Route } from "react-router-dom";
import React from "react";
import { Switch, Route } from "react-router-dom";
import RecursiveRoute from "./RecursiveRoute";
export default function Hello({ routes }) {
return (
<div>
<div class="sidebar"><!-- ... --></div>
<Switch>
{Object.entries(routes).map(([key, route]) => <RecursiveRoute key={key} {...route} />)}
<Route>I only appear if no child route matched</Route>
</Switch>
</div>
);
}
Routes can now be nested on an infinite number of levels in our config!
So far, we're on par with the example from the React Router docs; but what if instead of showing default content when none of our child routes match, we wanted to redirect to one of them?
In my previous article, I was exploring the possibility to read a Node.js version constraint from the package.json file of every directory upon entering them, and switch Node.js versions accordingly, relying on nvm's default version if a specific one was not required.
The script I hacked together does the job in most cases, but suffers from a few limitations:
it only looks for package.json in the (new) current directory, rather than the closest one in the tree
it is pretty bad at dealing with file ranges, just parsing the first correct version number it could find in them, which turns out to be their lower end
I therefore dug a bit further and found out that n, nvm's main competitor, can perfectly handle those two aspects natively! Just run n auto anywhere and it will look for the closest version constraint in the tree and interpret it correctly, reading from package.json but also from other files - even .nvmrc!
Running it systematically has one major downside, though: n actually reinstalls Node.js upon every version switch, and even if it uses its local cache for versions it has already downloaded, it turns out to be pretty slow; and unfortunately, there does not seem to be an option to cancel the switch if the current version already matches the expected one. Well, in that case, why not write something of our own to do exactly that?
In order to properly handle ranges, we need a list of all available Node.js versions; fortunately, this is pretty easy to get by running n ls-remote --all, the result of which we will store in a file that then gets passed as a parameter to our script.
As I was browsing search results for that exact sentence, I stumbled upon this post which proposes a zsh script automating the switching of Node.js versions when entering a directory using nvm, based on the presence of a .nvmrc file.
It works pretty well, but where I work we already specify Node version constraints in our package.json files (under the engines key) and weren't keen on duplicating that information, since said constraints mostly consist of a strict version anyway. I therefore adapted the original script to read from there instead:
autoload -U add-zsh-hook
switch-node-version() {
if [[ -f package.json ]] && grep -q '"node"' package.json; then
nvm use `cat package.json | sed -nE 's/"node": "[^0-9]*([0-9\.]*)[^"]*"/\1/p'`
elif [[ $(nvm version) != $(nvm version default) ]]; then
nvm use default
fi
}
When using a range (e.g. ^11.9, >=10 <14, etc.), special characters are dropped (which would result in nvm use 10 with the latter example). This brings one caveat, which is that the script doesn't play nice with overly specific ranges, such as ^10.0.0: it would indeed try to nvm use 10.0.0, and that particular version might not be available on one's machine. In such cases, I would advise shortening the range to ^10 (which better conveys the idea anyway in my opinion).
You may one day find yourself unlucky enough to work on a React application embedding an iframe. If that happens, chances are you might want to force it to refresh based on some change in the overlying component, that is to say have it rerender. But how would you do it?
An easy and not too shabby way is to abuse the key attribute, which serves as an identifier to tell a React component whether a given child element needs to be rerendered, and is typically used when building a list within a loop. How convenient! And even more so if you hide away all those shenanigans in a custom hook:
import { useState, useEffect } from "react";
export default function useChecksum(value) {
const [checksum, setChecksum] = useState(0);
The "checksum" here is but a dumb counter, which does a perfect job at yielding unique values over time - you can go with something more convoluted if you so desire. Anyhow, here is our hook in action:
import React from "react";
import useChecksum from "../hooks/useChecksum";
As opposed to most contemporary web-oriented JavaScript ecosystems, the game development frameworkPhaser does not come with a bundling solution out of the box, which would allow the use of cutting-edge syntax, or writing code across multiple source files but still referencing only one in the HTML page hosting the project. Fortunately, adding one in the mix is fairly easy and works just as well as one would expect.
There is, however, one tiny caveat; loading an asset (e.g. an image) implies providing its relative path as a string, delegating the underlying logic to Phaser itself:
In the context of a built project, the above just might fail, since our PNG file will be out of reach from its scope. In the case of Parcel, the way to go is pretty straightforward, and consists of bundling these assets with the code and using its internal mechanism to get a proper reference to their actual location:
import Phaser from "phaser";
import tiles from "../../assets/map/spritesheet.png";