/images/art2.png

HTML/Document Trick

Understanding offsetWidth, clientWidth, scrollWidth

Note
All these property will round the value to an integer. If you need a fractional value, use element.getBoundingClientRect().

Get Scrollbar width

Reference: https://www.30secondsofcode.org/js/s/get-scrollbar-width/

Get window scroll bar width

const getScrollbarWidth = () =>
    window.innerWidth - document.documentElement.clientWidth;

getScrollbarWidth();

Get a element scroll bar width

const getScrollbarWidth = (el) => {
    const leftBorder = parseInt(
        getComputedStyle(el).getPropertyValue('border-left-width'),
        10
    );
    const rightBorder = parseInt(
        getComputedStyle(el).getPropertyValue('border-right-width'),
        10
    );

    return el.offsetWidth - el.clientWidth - leftBorder - rightBorder;
};

getScrollbarWidth(el);

Element: getBoundingClientRect()

el.getBoundingClientRect() will return: left, top, right, bottom, x, y, width, and height.

Typescript Trick

generic types optional

To make a generic type optional, you have to assign the void as the default value.

const fetchData = <T = void>(url: string): T => {
    const res: T = fetch(url);
    return res;
};

https://garbagevalue.com/blog/optional-generic-typescript#quick-solutions-make-generic-type-optional

string[ ] & [ string, …string[ ] ]

The main difference is that type [string, ...string[]] at least have one element. [] will alert error. string[] could be empty. [] is ok.

ffmpeg

Reference

official

https://ffmpeg.org

blog

https://fireship.io/lessons/ffmpeg-useful-techniques/

node js

https://www.npmjs.com/package/fluent-ffmpeg

example

concatenate

ffmpeg -f concat -i vids.txt -c copy out.mp4

vids.txt:

file 'name1.mov'
file 'name2.mov'
file 'name3.mov'

Type convention

ffmpeg -i in.mp4 out.mov

ffmpeg -i in.mp4 out.gif

Scale

It very common to reduce size of output file. Change scale usually the common and efficient way to do so.

Design Pattern

Design pattern

Factory pattern

const createDataBaseClass = (dbName: DBOption) => {
    switch (dbName) {
        case 'InMemo':
            return InMemoryDataBase;
        case 'SQL':
            return SQL_DB;

        // ...
        // you can add anything else
        default:
            break;
    }
};

Singleton pattern

export const createDataBase = <T extends BaseRecord>() => {
    const db = new InMemoryDataBase<T>();
    return db;
};

const pokemonDB = createDataBase<Pokemon>();

pokemonDB.set({
    id: 'Bulbasaur',
    attack: 59,
    defense: 10,
});

console.log(pokemonDB.get('Bulbasaur'));

// A CPP way to do
const createDataBase2 = <T extends BaseRecord>() => {
    class InMemoryDataBase2 implements DataBase<T> {
        private db: Record<string, T> = {};
        static instance: InMemoryDataBase2 = new InMemoryDataBase2();

        private constructor() {} // private constructor is necessary

        public set(newValue: T): void {
            this.db[newValue.id] = newValue;
        }

        public get(id: string): T | undefined {
            return this.db[id];
        }
    }

    return InMemoryDataBase2;
};
const PokemonDB2 = createDataBase2<Pokemon>();
PokemonDB2.instance.set({
    id: 'Bulbasaur',
    attack: 59,
    defense: 10,
});

Observer (pub/sub) pattern

// ++++++++++  Observer  ++++++++++
const createObserver = <EventType>(): {
    subscribe: (listener: listenerType<EventType>) => () => void; // take listener and return an unsubscribe function
    publish: (event: EventType) => void;
} => {
    let listeners: listenerType<EventType>[] = [];
    return {
        subscribe: (listener: listenerType<EventType>): (() => void) => {
            listeners.push(listener);
            return () => {
                listeners = listeners.filter((l) => l !== listener);
            };
        },
        publish: (event: EventType) => {
            listeners.forEach((l) => l(event));
        },
    };
};

//  +++++++++++++++++++++++++++++

interface BeforeAddValueEvent<T> {
    newValue: T;
    value: T;
}
interface AfterAddValueEvent<T> {
    value: T;
}

class InMemoryDBWithObserver<T extends BaseRecord> extends InMemoryDataBase<T> {
    public set(newValue: T): void {
        this.BeforeAddValueObserver.publish({
            newValue,
            value: this.db[newValue.id],
        });
        this.db[newValue.id] = newValue;
        this.AfterAddValueObserver.publish({
            value: newValue,
        });
    }

    public get(id: string): T | undefined {
        return this.db[id];
    }
    // observer
    private BeforeAddValueObserver = createObserver<BeforeAddValueEvent<T>>();
    private AfterAddValueObserver = createObserver<AfterAddValueEvent<T>>();

    onBeforeAddValue(
        listener: listenerType<BeforeAddValueEvent<T>>
    ): () => void {
        return this.BeforeAddValueObserver.subscribe(listener);
    }

    onAfterAddValue(listener: listenerType<AfterAddValueEvent<T>>): () => void {
        return this.AfterAddValueObserver.subscribe(listener);
    }

    // visiter pattern
    visit(visitor: (item: T) => void): void {
        Object.values(this.db).forEach(visitor);
    }

    // strategy pattern
    getBest(strategy: (item: T) => number): T {
        let findRes: { max: number; res: T | null } = {
            max: -Infinity,
            res: null,
        };

        return Object.values(this.db).reduce((prev, cur) => {
            let score = strategy(cur);
            if (prev.max < score) return { max: score, res: cur };
            return prev;
        }, findRes).res;
    }
}

const pokemonDB = new InMemoryDBWithObserver<Pokemon>();

pokemonDB.onBeforeAddValue((event) => {
    console.log('Before add value');
    console.log(event);
});

const unsubscribe = pokemonDB.onAfterAddValue((event) => {
    console.log('After ADD A Value');
    console.log(event);
});
pokemonDB.onAfterAddValue((event) => {
    console.log('-----------');
});

pokemonDB.set({
    id: 'Bulbasaur',
    attack: 59,
    defense: 10,
});
pokemonDB.set({
    id: 'Bulbasaur',
    attack: 20,
    defense: 30,
});

unsubscribe();

pokemonDB.set({
    id: 'Spinpsaur',
    attack: 159,
    defense: 110,
});

console.log('Visit pattern:');
pokemonDB.visit((item) => console.log(item));

console.log('strategy pattern:');
console.log(pokemonDB.getBest((item) => item.defense));

Adaptor pattern

Typescript overview

type

Union type & Literal type

const add = (
    a: number | string,
    b: number | string,
    type?: 'number' | 'string'
): number | string => {
    if (type === 'string') {
        return a.toString() + b.toString();
    } else return +a + +b;
};

console.log(add(1, 2));

Array

type Book = {
    id: string;
    name: string;
};

let books: Book[] = [];

unknown

let test1: unknown;
let test2: string;

test1 = 'xyz'; // ok
// test2 = test1; // error
function f1(a: any) {
    a.b(); // OK
}
function f2(a: unknown) {
    a.b(); //error
    // Object is of type 'unknown'.
}

Type a Function

type listenerType<EventType> = (event: EventType) => void;

Assign a plain Object

type Primitive = bigint | boolean | null | number | string | symbol | undefined;

type PlainObject = Record<string, Primitive>;
const obj1: PlainObject = { a: 1 }; //✅
const obj2: PlainObject = { a: 1 }; //❌
const obj3: PlainObject = new myClass(); //❌

Assign a nested plain Object

type Primitive = bigint | boolean | null | number | string | symbol | undefined;

type JSONValue = Primitive | JSONObject | JSONArray;
interface JSONObject {
    [key: string]: JSONValue;
}
interface JSONArray extends Array<JSONValue> {}

const obj1: PlainObject = { a: 1 }; //✅
const obj2: PlainObject = { a: { b: { c: 3 } } }; //✅
const obj3: PlainObject = new myClass(); //❌

Type Template arrow function example

export const useFetchAPI = <T extends unknown>(
    url: string,
    method: 'POST' | 'GET',
    body?: string | JSONObject
): [string, T | null] => {
    const [fetchStatus, setFetchStatus] = useState('error');
    const [fetchResult, setFetchResult] = useState<T | null>(null);

    useEffect(() => {
        const apiMockFetch: () => Promise<{
            status: string;
            requestId: string;
            result: T | null;
        }> = () => {
            return new Promise((resolve) => {
                setTimeout(() => {
                    resolve(someData);
                }, 1000);
            });
        };

        const fetchData = async () => {
            const { status, result } = await apiMockFetch();
            setFetchStatus(status);
            if (result !== undefined) {
                setFetchResult(result);
            }
        };
        if (fetchStatus !== 'success') fetchData();
    }, [url, method, body, fetchStatus]);
    return [fetchStatus, fetchResult];
};

Type & interface

type Book = {
    id: string;
    name: string;
};

interface Book {
    id: string;
    name: string;
}

React Logic Reuse Example

React logic extraction

Check this post

Example code

This example demonstrate one single feature using four different feature to archive code split

Code running there: –>Link<–

import { useState, useEffect } from React;

const Styles = {
    redBorder: {
        border: '1px solid #f00',
    },
};

const MouseDisplay = ({ x, y }) => {
    return (
        <div>
            Mouse at x: {x} ; y: {y}
        </div>
    );
};
const MouseDisplay2 = ({ x, y }) => {
    return (
        <div style={{ color: 'teal' }}>
            Mouse at x: {x} ; y: {y}
        </div>
    );
};

// Normal
export const MouseInfoAndDisplay = () => {
    const [x, setX] = useState(0);
    const [y, setY] = useState(0);
    const handleMove = (e) => {
        setX(e.clientX);
        setY(e.clientY);
    };
    return (
        <div style={Styles.redBorder} onMouseMove={handleMove}>
            <MouseDisplay x={x} y={y} />
        </div>
    );
};

// HOC
const withMouseInfo = (Component) => {
    return (props) => {
        const [x, setX] = useState(0);
        const [y, setY] = useState(0);
        const handleMove = (e) => {
            setX(e.clientX);
            setY(e.clientY);
        };
        return (
            <div style={Styles.redBorder} onMouseMove={handleMove}>
                <Component {...props} x={x} y={y} />
            </div>
        );
    };
};

export const HOCMouseDisplay = withMouseInfo(MouseDisplay);
export const HOCMouseDisplay2 = withMouseInfo(MouseDisplay2);

// Render Props

const MouseRenderProps = ({ render }) => {
    const [x, setX] = useState(0);
    const [y, setY] = useState(0);
    const handleMove = (e) => {
        setX(e.clientX);
        setY(e.clientY);
    };

    return (
        <div style={Styles.redBorder} onMouseMove={handleMove}>
            {render(x, y)}
        </div>
    );
};

export const Mouse = () => {
    return (
        <div>
            <MouseRenderProps render={(x, y) => <MouseDisplay x={x} y={y} />} />
            <MouseRenderProps
                render={(x, y) => <MouseDisplay2 x={x} y={y} />}
            />
        </div>
    );
};

// Customize HOOK

const useMouseState = () => {
    const [x, setX] = useState(0);
    const [y, setY] = useState(0);
    const [node, setNode] = useState(null);
    const handleMove = (e) => {
        setX(e.clientX);
        setY(e.clientY);
    };
    useEffect(() => {
        if (node !== null) {
            node.addEventListener('mousemove', handleMove);
        }
    }, [node]);
    return [x, y, setNode];
};

export const MouseUsingHook = () => {
    const [x1, y1, ref1] = useMouseState();
    const [x2, y2, ref2] = useMouseState();

    return (
        <div>
            <div ref={ref1} style={Styles.redBorder}>
                <MouseDisplay x={x1} y={y1} />
            </div>
            <div ref={ref2} style={Styles.redBorder}>
                <MouseDisplay2 x={x2} y={y2} />
            </div>
        </div>
    );
};


const App = () => {
    return (
        <div
            style={{
                display: 'flex',
                flexDirection: 'column',
                height: '100vh',
                justifyContent: 'space-around',
            }}
        >
            <div>
                Normal: <MouseInfoAndDisplay />
            </div>
            <div>
                HOC: <HOCMouseDisplay /> <HOCMouseDisplay2/>
            </div>
            <div>
                Render Props: <Mouse />
            </div>
            <div>
                Hook: <MouseUsingHook />
            </div>
        </div>
    );
};

Useful Post Archive

React

–>Look this post<–

WEB basic

Please Stop Using Local Storage

https://www.rdegges.com/2018/please-stop-using-local-storage/

JWT authentication: When and how to use it

https://blog.logrocket.com/jwt-authentication-best-practices/#:~:text=A%20JWT%20needs%20to%20be,storage%20(or%20session%20storage).

Graphql

Dispatch This: Using Apollo Client 3 as a State Management Solution

https://www.apollographql.com/blog/dispatch-this-using-apollo-client-3-as-a-state-management-solution/

Apollo Client update cache when delete an item from list

https://github.com/apollographql/apollo-client/issues/6451#issuecomment-775242381

Several things for Graphql Security

https://ithelp.ithome.com.tw/articles/10208008

should I put useQuery inside a useEffect and should I store returned data in state?

https://github.com/trojanowski/react-apollo-hooks/issues/158

Webpack Overview

webpack is a static module bundler for modern JavaScript applications.

Document: https://webpack.js.org/concepts/

Installation

npm i -D webpack webpack-cli

webpack core concept

Entry

An entry point indicates which module webpack should use to begin building out its internal dependency graph. webpack will figure out which other modules and libraries that entry point depends on (directly and indirectly).

Jest Overview

Jest is a JavaScript Testing Framework

Document: https://jestjs.io/docs/en/getting-started

How to use Jest

function.js

const axios = require('axios');

const functions = {
    add: (num1, num2) => num1 + num2,
    isNull: () => null,
    checkValue: (x) => x,
    createUser: () => {
        const user = { firstName: 'Brad' };
        user['lastName'] = 'Traversy';
        return user;
    },
    fetchUser: () =>
        axios
            .get('https://jsonplaceholder.typicode.com/users/1')
            .then((res) => res.data)
            .catch((err) => 'error'),
};

module.exports = functions;

function.test.js

const functions = require('./functions');

// beforeEach(() => initDatabase());
// afterEach(() => closeDatabase());

// beforeAll(() => initDatabase());
// afterAll(() => closeDatabase());

// const initDatabase = () => console.log('Database Initialized...');
// const closeDatabase = () => console.log('Database Closed...');
const nameCheck = () => console.log('Checking Name....');

describe('Checking Names', () => {
    beforeEach(() => nameCheck());

    test('User is Jeff', () => {
        const user = 'Jeff';
        expect(user).toBe('Jeff');
    });

    test('User is Karen', () => {
        const user = 'Karen';
        expect(user).toBe('Karen');
    });
});

// toBe
test('Adds 2 + 2 to equal 4', () => {
    expect(functions.add(2, 2)).toBe(4);
});

// not
test('Adds 2 + 2 to NOT equal 5', () => {
    expect(functions.add(2, 2)).not.toBe(5);
});

// CHECK FOR TRUTHY & FALSY VALUES
// toBeNull matches only null
// toBeUndefined matches only undefined
// toBeDefined is the opposite of toBeUndefined
// toBeTruthy matches anything that an if statement treats as true
// toBeFalsy matches anything that an if statement treats as false

// toBeNull
test('Should be null', () => {
    expect(functions.isNull()).toBeNull();
});

// toBeFalsy
test('Should be falsy', () => {
    expect(functions.checkValue(undefined)).toBeFalsy();
});

// toEqual
test('User should be Brad Traversy object', () => {
    expect(functions.createUser()).toEqual({
        firstName: 'Brad',
        lastName: 'Traversy',
    });
});

// Less than and greater than
test('Should be under 1600', () => {
    const load1 = 800;
    const load2 = 800;
    expect(load1 + load2).toBeLessThanOrEqual(1600);
});

// Regex
test('There is no I in team', () => {
    expect('team').not.toMatch(/I/i);
});

// Arrays
test('Admin should be in usernames', () => {
    usernames = ['john', 'karen', 'admin'];
    expect(usernames).toContain('admin');
});

// Working with async data

// Promise
// test('User fetched name should be Leanne Graham', () => {
//   expect.assertions(1);
//   return functions.fetchUser().then(data => {
//     expect(data.name).toEqual('Leanne Graham');
//   });
// });

// Async Await
test('User fetched name should be Leanne Graham', async () => {
    expect.assertions(1);
    const data = await functions.fetchUser();
    expect(data.name).toEqual('Leanne Graham');
});

Matchers

Doc: https://jestjs.io/docs/en/using-matchers and https://jestjs.io/docs/en/expect