Skip to content

turtlebe/react-rxjs

Repository files navigation

Truck OS UI

Getting Started

A.K.A - How to develop/localhost setup :)

The basics

Package Manager: pnpm

Make sure you have node and pnpm installed. Also, read the local workstation setup for being able to access TruckOs's private gitlab registries.

You can install pnpm as follows:

  • If on mac with homebrew:
    brew install pnpm
  • via npm, installing it globally
    npm install -g pnpm

Storybook testing:

Make sure you have system deps required by Playwright

You can install pnpm as follows:

  • Run this as a one-time script
    npx playwright install-deps

IDE Recommended Setup

We do lint/etc and have the setup so that style/etc is all automatic. Hence, it is strongly recommended you have plugins installed/enabled that automatically fix style for you so you don't get commit rejects/etc.

VSCode
  • Add vscode extensions:
    • ESLint
    • Prettier
    • MDX
    • Color Highlight (optional)

Jetbrains IDEA based (Webstorm)

Development Commands

First off, make sure you have all node_modules installed

pnpm install

Then we have the following:

  • storybook: Run Storybook to check components
    pnpm run dev
  • test-storybook: Start Storybook tests in the background.
    npx test-storybook --watchAll --coverage
  • test: Run all unit tests via jest
    pnpm run test
  • test:coverage: Runs tests with jest and generated test coverage repots. A report is printed to stdout while other formats (e.g., json, html) are written to a coverage directory
    pnpm run test:coverage
  • test:coverage:ci: Same as test:coverage it also passes --ci flag to Jest, ensures correct snapshot testing behaviour in a CI environment - see docs
  • dev: launch the app running locally with a dev server and hot module reloading
    pnpm run dev
  • extract-i18n: Extract phrases for translation. See the section on Internationalisation
    pnpm run extract-i18n
  • api-types:gen: If you change the api files, then run this to regenerate bindings/merge together/etc.
    pnpm run api-types:gen
  • tos-cdk:clean:all-local: If it's your first checkout or properties that are from config have been changed/added, make sure you update them in the environment definitions, and then run this so that the config file for local is regenerated from templates and placed locally in your public dir.
    pnpm run tos-cdk:clean:all-local
  • aws:test:get-s3-url: If you need a valid S3 pre-signed URL for testing, run this command. You'll need a .env file with
    pnpm run aws:test:get-s3-url
    You'll need an .env file that looks as follows (only public information here - get the secret from Fernando or someone else who has it ;).
    AWS_ACCESS_KEY_ID=AKIA2EJ4VQTM7BGDS6CJ
    AWS_SECRET_ACCESS_KEY=### ASK FOR THE KEY #####
    AWS_S3_BUCKET_NAME=truckos-dev-data-documentsbucket9ec9deb9-1dht08po0ke8p

Important features documentation

Internationalisation

For the command to extract phrases for translation from the code, look at the development commands section

The command will run the parser to extract any missing phrases from the translation files.

It explicitly looks for the t('xxxxx') structure in the code. This means that for validation schemas we have a dummy t function to wrap the strings so they are picked up. This is because the validation strings are translated at the point of being displayed on the component rather than via the schema.

Note that the entire translation file is refreshed when this command is run. Existing translations are kept but unused translations are removed. You can change the i18next-parser.config.js file to have createOldCatalogs: true if you wish to have the old version kept in case of accidental removal (although git commit history should mean this isn't needed).

RxJS

  • Have a read to the LearnRxJS primer, it's a good one.
  • Take a look at React-RxJS - we use it to create react hooks from observables :)

Some things to be aware of

Naming conventions
  • Observables are postfixed with $.
    • Hence, if you have an observable for a company object, it should be named company$.
  • Component files and directories are capitalised (e.g. BackButton.tsx)
  • Other script files are snake-case (e.g. object-utils.ts)
Important differences between similar methods:
method 1 method 2 difference
switchMap mergeMap They take the same parameters and both return observables, but the switchMap will make sure that if there's any subscriber currently waiting on a result, it will get unsubscribed. That means that subscribers end up only getting the latest result vs. in mergemap where they get all results of all the changes to the observable .
Subscribers to long lived observables outside of a component that can be mounted/unmounted

If you create a subscriber to an observable in a component that can be mounted/unmounted depending on state, you have to make sure that you unsubscribe as well. If you don't, a couple of things happen:

  • Even if you unmount the component, the subscriber lives on. That means that if you trigger any actions (like redirecting) based on the subscriber, even if your component is unmounted, the actions will be triggered (imagine being redirected for no apparent from a page you were looking at to a completely different one just because somehow the observable changed).
  • When you mount, a new subscriber is created and off we go again. aka: How to create a memory leak ;).

Hence - make sure you do the following:

  1. Make sure the subscription uses useRef (part of react). It allows the value to change but doesn't trigger a re-render (as opposed to useState which does).
  2. use useEffect to unsubscribe on unmounting. You take advantage of the functionality that allows to execute code on mounting and unmounting (read more here)

So putting those two together, the pattern would be something like:

import { useEffect, useRef } from 'react';
import { Subscription } from 'rxjs';

const subscription = useRef<Subscription | undefined>(undefined);
const observable$ = props.obervable; //comes from somewhere.
//...
useEffect(
  () => {
    //code here runs on mount
    subscription.current = observable$.subscribe(onChangesFunction);
    return () => {
      //Code here runs on unmount
      subscription.current?.unsubscribe();
    };
  },
  [
    /* it's important that there are no dependencies */
  ]
);
//...

About

React, Typescript, RxJS, Material UI, Auth0

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages