Monday, May 21, 2018

Typescript definition for process.env

// autocomplete for process.env.
declare global
{
    namespace NodeJS
    {
        // tslint:disable interface-name
        interface ProcessEnv
        {
            NODE_ENV?: 'production' | 'development';
            FACEBOOK_CLIENT_ID: string;
            FACEBOOK_CLIENT_SECRET: string;
            GOOGLE_CLIENT_ID: string;
            GOOGLE_CLIENT_SECRET: string;
            JWT_SIGNING_KEY: string;
        }
    }
}

// https://www.typescriptlang.org/docs/handbook/declaration-files/templates/global-modifying-module-d-ts.html
export {};


Happy coding!

Saturday, May 19, 2018

Redux Reducer Perfection with Immer




All of these achieves the same thing. The last one is the perfect one.

export function counterReducer(
    state: ICounterMore = {count: 0, message: 'hello'},
    action: ICounterAction
) // : ICounterMore, automatically inferred from return produce
{
    return produce(state, draft => {
        switch (action.type) {
            case CounterKind.INCREMENT:
                ++draft.count;
                break;
            case CounterKind.DECREMENT:
                --draft.count;
                draft.message = 'hey';
                break;
        }
    });
}


export const counterReducer = (
    state: ICounterMore = {count: 0, message: 'hello'},
    action: ICounterAction
) // : ICounterMore, automatically inferred from return type of produce
    => produce(state, draft =>
    {
        switch (action.type) {
            case CounterKind.INCREMENT:
                ++draft.count;
                break;
            case CounterKind.DECREMENT:
                --draft.count;
                draft.message = 'hey';
                break;
        }
    });


export const counterReducer = produce((
    draft: ICounterMore = {count: 0, message: 'hello'},
    action: ICounterAction
) // : ICounterMore, automatically inferred from return draft
=> {
    switch (action.type) {
        case CounterKind.INCREMENT:
            ++draft.count;
            break;
        case CounterKind.DECREMENT:
            --draft.count;
            draft.message = 'hey';
            break;
    }
    return draft;
});

Friday, May 18, 2018

Mutating React state

This won't work. React can't detect mutation when done this way:
interface IComponentState
{
    numberList: number[];
}

export class HomeComponent extends React.Component<ComponentProps, IComponentState>
{
    private addNumber(): void
    {
        this.state.numberList.push(Math.random());
    }
}

This works, but would have to produce immutable explicitly:
private addNumber(): void
{
    const newArray = [...this.state.numberList, Math.random()];
    this.setState({numberList: newArray});    
}


Producing immutable the convenient way, use immer:
private addNumber(): void
{
    // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18365#issuecomment-390110697

    this.setState(produce<IComponentState>(draft => {
        draft.numberList.push(Math.random());
    }));
}


Deleting an element from an array:
{this.state.numberList.map((n, i) =>
    <div key={i}>
        {n}
        <button
            // tslint:disable jsx-no-lambda
            onClick={() => {
                this.state.numberList.splice(i, 1);
            }}
        >
            Non-working delete
        </button>

        <button
            // tslint:disable jsx-no-lambda
            onClick={() => {
                this.setState({
                    numberList: [
                        ...this.state.numberList.slice(0, i),
                        ...this.state.numberList.slice(i + 1)
                    ]
                });
            }}
        >
            Delete using explicit immutable
        </button>

        <button
            // tslint:disable jsx-no-lambda
            onClick={() => {
                this.setState(produce<IComponentState>(draft => {
                    draft.numberList.splice(i, 1);
                }));
            }}
        >
            Delete the easier way
        </button>
    </div>
)}



Happy Coding!

Monday, May 14, 2018

Mutating Redux state

Mutating Redux state directly does not work.

export interface ICounterMore
{
    count: number;
    message: string;
}

export function counterReducer(
    state: ICounterMore = {count: 0, message: 'hello'},
    action: ICounterAction
): ICounterMore
{
    switch (action.type) {
        case CounterKind.INCREMENT:
            state.count = state.count + 1;
            return state;
        case CounterKind.DECREMENT:
            state.count = state.count - 1;
            state.message = 'hey';
            return state;
        default:
            return state;
    }
}


Must return new state that does not mutate the existing state.

export function counterReducer(
    state: ICounterMore = {count: 0, message: 'hello'},
    action: ICounterAction
): ICounterMore
{
    switch (action.type) {
        case CounterKind.INCREMENT:
            return {
                ...state,
                count: state.count + 1
            };
        case CounterKind.DECREMENT:
            return {
                ...state,
                count: state.count - 1,
                message: 'hey'
            };
        default:
            return state;
    }
}


Mutating Redux state "directly" works, using immer.

immer creates immutable state from the mutated state from draft, and then it return the immutable state to Redux.
Proof that immutable state is the state that is still being passed around, Redux's time travel still work.


export function counterReducer(
    state: ICounterMore = {count: 0, message: 'hello'},
    action: ICounterAction
): ICounterMore
{
    return produce(state, draft => {
        switch (action.type) {
            case CounterKind.INCREMENT:
                ++draft.count;
                break;
            case CounterKind.DECREMENT:
                --draft.count;
                draft.message = 'hey';
                break;
        }
    });
}

Happy Coding!

Sunday, May 13, 2018

Destructuring to existing variables

Can't be done like this:

function getColor(): { a: number, b: string }
{
    return { a: 0xff0000, b: "Red" };
}

let color: number;
let colorString: string;

{ a: color, b: colorString } = getColor();

window.alert(color);
window.alert(colorString);

Must be done like this, use parenthesis. As for why: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment

function getColor(): { a: number, b: string }
{
    return { a: 0xff0000, b: "Red" };
}

let color: number;
let colorString: string;

({ a: color, b: colorString } = getColor());

window.alert(color);
window.alert(colorString);


Happy Coding!

Wednesday, May 2, 2018

Material UI SelectField for react-final-form

import React from "react";

import { FormControl, FormHelperText, InputLabel, Select } from "material-ui";

export const SelectField = ({
  input: { name, onChange, value, ...restInput },
  meta,
  children,
  label,
  fullWidth,
  margin,
  InputLabelProps,
  ...rest
}) => (
  <FormControl fullWidth={fullWidth} margin={margin}>
    {label && <InputLabel error={meta.error && meta.touched} {...InputLabelProps}>{label}</InputLabel>}
    <Select
      {...rest}
      name={name}
      error={meta.error && meta.touched}
      inputProps={restInput}
      value={value}
      onChange={onChange}
    >
      {children}
    </Select>
    {meta.error &&
      meta.touched && (
        <FormHelperText error={true}>{meta.error}</FormHelperText>
      )}
  </FormControl>
);


Demo code: https://codesandbox.io/s/kkp59xr047