Skip to content

Instantly share code, notes, and snippets.

@swyxio
Last active May 4, 2023 02:15

Revisions

  1. swyxio revised this gist Jan 12, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion createCtx-useReducer.tsx
    Original file line number Diff line number Diff line change
    @@ -57,7 +57,7 @@ function Counter() {
    <button onClick={() => dispatch({ type: 'increment' })}>+</button>
    <button onClick={() => dispatch({ type: 'add', payload: 5 })}>+5</button>
    <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    <button onClick={() => dispatch({ type: 'minus', payload: 5 })}>+5</button>
    <button onClick={() => dispatch({ type: 'minus', payload: 5 })}>-5</button>
    </div>
    )
    }
  2. swyxio revised this gist Aug 26, 2019. 1 changed file with 47 additions and 26 deletions.
    73 changes: 47 additions & 26 deletions createCtx-useReducer.tsx
    Original file line number Diff line number Diff line change
    @@ -1,42 +1,63 @@
    // this is a really unrealistic example where action type is same as state type. woudl love some help fixing this
    type ReducerType<State> = (state: State, action: State) => State

    export function createCtx<A>(defaultValue: A, reducer: ReducerType<A>) {
    type DispatchType = React.Dispatch<typeof defaultValue>
    const defaultDispatch: DispatchType = () => defaultValue
    const ctx = React.createContext({ state: defaultValue, dispatch: defaultDispatch })
    export function createCtx<StateType, ActionType>(
    reducer: React.Reducer<StateType, ActionType>,
    initialState: StateType,
    ) {
    const defaultDispatch: React.Dispatch<ActionType> = () => initialState // we never actually use this
    const ctx = React.createContext({
    state: initialState,
    dispatch: defaultDispatch, // just to mock out the dispatch type and make it not optioanl
    })
    function Provider(props: React.PropsWithChildren<{}>) {
    const [state, dispatch] = React.useReducer(reducer, defaultValue)
    const [state, dispatch] = React.useReducer<React.Reducer<StateType, ActionType>>(reducer, initialState)
    return <ctx.Provider value={{ state, dispatch }} {...props} />
    }
    return [ctx, Provider] as const
    }
    // usage
    enum States {
    Green,
    Red,
    Yellow
    }
    function reducer(state: States) {
    if (state === States.Green) return States.Yellow
    if (state === States.Yellow) return States.Red
    return States.Green
    const initialState = { count: 0 }
    type AppState = typeof initialState
    type Action =
    | { type: 'increment' }
    | { type: 'add'; payload: number }
    | { type: 'minus'; payload: number }
    | { type: 'decrement' }

    function reducer(state: AppState, action: Action): AppState {
    switch (action.type) {
    case 'increment':
    return { count: state.count + 1 }
    case 'decrement':
    return { count: state.count - 1 }
    case 'add':
    return { count: state.count + action.payload }
    case 'minus':
    return { count: state.count - action.payload }
    default:
    throw new Error()
    }
    }
    const [ctx, TextProvider] = createCtx(States.Green, reducer)
    export const TextContext = ctx
    const [ctx, CountProvider] = createCtx(reducer, initialState)
    export const CountContext = ctx

    // top level example usage
    export function App() {
    return (
    <TextProvider>
    <Component />
    </TextProvider>
    <CountProvider>
    <Counter />
    </CountProvider>
    )
    }
    export function Component() {
    const { state, dispatch } = React.useContext(ctx)

    // example usage inside a component
    function Counter() {
    const { state, dispatch } = React.useContext(CountContext)
    return (
    <div>
    {state}
    <button onClick={() => dispatch(state)}>Toggle</button>
    Count: {state.count}
    <button onClick={() => dispatch({ type: 'increment' })}>+</button>
    <button onClick={() => dispatch({ type: 'add', payload: 5 })}>+5</button>
    <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    <button onClick={() => dispatch({ type: 'minus', payload: 5 })}>+5</button>
    </div>
    )
    }
  3. swyxio revised this gist Aug 26, 2019. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion createCtx-useReducer.tsx
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,6 @@
    type ReducerType<State> = (state: State, action: State) => State
    // this is a really unrealistic example where action type is same as state type. woudl love some help fixing this
    type ReducerType<State> = (state: State, action: State) => State

    export function createCtx<A>(defaultValue: A, reducer: ReducerType<A>) {
    type DispatchType = React.Dispatch<typeof defaultValue>
    const defaultDispatch: DispatchType = () => defaultValue
  4. swyxio revised this gist Aug 26, 2019. No changes.
  5. swyxio revised this gist Apr 28, 2019. 3 changed files with 3 additions and 3 deletions.
    2 changes: 1 addition & 1 deletion noNullCheck.tsx → createCtx-noNullCheck.tsx
    Original file line number Diff line number Diff line change
    @@ -7,7 +7,7 @@ function createCtx<A>() {
    if (!c) throw new Error("useCtx must be inside a Provider with a value")
    return c
    }
    return [useCtx, ctx.Provider] as [() => A, typeof ctx.Provider]
    return [useCtx, ctx.Provider] as const
    }
    // usage - no need to specify value upfront!
    export const [useCtx, SettingProvider] = createCtx<string>()
    2 changes: 1 addition & 1 deletion useReducer.tsx → createCtx-useReducer.tsx
    Original file line number Diff line number Diff line change
    @@ -7,7 +7,7 @@ export function createCtx<A>(defaultValue: A, reducer: ReducerType<A>) {
    const [state, dispatch] = React.useReducer(reducer, defaultValue)
    return <ctx.Provider value={{ state, dispatch }} {...props} />
    }
    return [ctx, Provider] as [typeof ctx, typeof Provider]
    return [ctx, Provider] as const
    }
    // usage
    enum States {
    2 changes: 1 addition & 1 deletion useState.tsx → createCtx-useState.tsx
    Original file line number Diff line number Diff line change
    @@ -6,7 +6,7 @@ export function createCtx<A>(defaultValue: A) {
    const [state, update] = React.useState(defaultValue)
    return <ctx.Provider value={{ state, update }} {...props} />
    }
    return [ctx, Provider] as [typeof ctx, typeof Provider]
    return [ctx, Provider] as const
    }
    // usage
    const [ctx, TextProvider] = createCtx("someText")
  6. swyxio revised this gist Apr 27, 2019. 2 changed files with 4 additions and 4 deletions.
    4 changes: 2 additions & 2 deletions useReducer.tsx
    Original file line number Diff line number Diff line change
    @@ -3,9 +3,9 @@ export function createCtx<A>(defaultValue: A, reducer: ReducerType<A>) {
    type DispatchType = React.Dispatch<typeof defaultValue>
    const defaultDispatch: DispatchType = () => defaultValue
    const ctx = React.createContext({ state: defaultValue, dispatch: defaultDispatch })
    function Provider({ children }: { children: React.ReactNode }) {
    function Provider(props: React.PropsWithChildren<{}>) {
    const [state, dispatch] = React.useReducer(reducer, defaultValue)
    return <ctx.Provider value={{ state, dispatch }}>{children}</ctx.Provider>
    return <ctx.Provider value={{ state, dispatch }} {...props} />
    }
    return [ctx, Provider] as [typeof ctx, typeof Provider]
    }
    4 changes: 2 additions & 2 deletions useState.tsx
    Original file line number Diff line number Diff line change
    @@ -2,9 +2,9 @@ export function createCtx<A>(defaultValue: A) {
    type UpdateType = React.Dispatch<React.SetStateAction<typeof defaultValue>>
    const defaultUpdate: UpdateType = () => defaultValue
    const ctx = React.createContext({ state: defaultValue, update: defaultUpdate })
    function Provider({ children }: { children: React.ReactNode }) {
    function Provider(props: React.PropsWithChildren<{}>) {
    const [state, update] = React.useState(defaultValue)
    return <ctx.Provider value={{ state, update }}>{children}</ctx.Provider>
    return <ctx.Provider value={{ state, update }} {...props} />
    }
    return [ctx, Provider] as [typeof ctx, typeof Provider]
    }
  7. swyxio revised this gist Apr 27, 2019. No changes.
  8. swyxio revised this gist Apr 27, 2019. 3 changed files with 31 additions and 0 deletions.
    31 changes: 31 additions & 0 deletions noNullCheck.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,31 @@
    // create context with no upfront defaultValue
    // without having to do undefined check all the time
    function createCtx<A>() {
    const ctx = React.createContext<A | undefined>(undefined)
    function useCtx() {
    const c = React.useContext(ctx)
    if (!c) throw new Error("useCtx must be inside a Provider with a value")
    return c
    }
    return [useCtx, ctx.Provider] as [() => A, typeof ctx.Provider]
    }
    // usage - no need to specify value upfront!
    export const [useCtx, SettingProvider] = createCtx<string>()
    export function App() {
    // get a value from a hook, must be in a component
    const key = useLocalStorage('key')
    return (
    <SettingProvider value={key}>
    <Component />
    </SettingProvider>
    )
    }
    export function Component() {
    const key = useCtx() // can still use without null check!
    return <div>{key}</div>
    }


    function useLocalStorage(a: string) {
    return 'secretKey' + a
    }
    File renamed without changes.
    File renamed without changes.
  9. swyxio created this gist Apr 27, 2019.
    40 changes: 40 additions & 0 deletions useReducer.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,40 @@
    type ReducerType<State> = (state: State, action: State) => State
    export function createCtx<A>(defaultValue: A, reducer: ReducerType<A>) {
    type DispatchType = React.Dispatch<typeof defaultValue>
    const defaultDispatch: DispatchType = () => defaultValue
    const ctx = React.createContext({ state: defaultValue, dispatch: defaultDispatch })
    function Provider({ children }: { children: React.ReactNode }) {
    const [state, dispatch] = React.useReducer(reducer, defaultValue)
    return <ctx.Provider value={{ state, dispatch }}>{children}</ctx.Provider>
    }
    return [ctx, Provider] as [typeof ctx, typeof Provider]
    }
    // usage
    enum States {
    Green,
    Red,
    Yellow
    }
    function reducer(state: States) {
    if (state === States.Green) return States.Yellow
    if (state === States.Yellow) return States.Red
    return States.Green
    }
    const [ctx, TextProvider] = createCtx(States.Green, reducer)
    export const TextContext = ctx
    export function App() {
    return (
    <TextProvider>
    <Component />
    </TextProvider>
    )
    }
    export function Component() {
    const { state, dispatch } = React.useContext(ctx)
    return (
    <div>
    {state}
    <button onClick={() => dispatch(state)}>Toggle</button>
    </div>
    )
    }
    29 changes: 29 additions & 0 deletions usestate.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,29 @@
    export function createCtx<A>(defaultValue: A) {
    type UpdateType = React.Dispatch<React.SetStateAction<typeof defaultValue>>
    const defaultUpdate: UpdateType = () => defaultValue
    const ctx = React.createContext({ state: defaultValue, update: defaultUpdate })
    function Provider({ children }: { children: React.ReactNode }) {
    const [state, update] = React.useState(defaultValue)
    return <ctx.Provider value={{ state, update }}>{children}</ctx.Provider>
    }
    return [ctx, Provider] as [typeof ctx, typeof Provider]
    }
    // usage
    const [ctx, TextProvider] = createCtx("someText")
    export const TextContext = ctx
    export function App() {
    return (
    <TextProvider>
    <Component />
    </TextProvider>
    )
    }
    export function Component() {
    const { state, update } = React.useContext(ctx)
    return (
    <label>
    {state}
    <input type="text" onChange={e => update(e.target.value)} />
    </label>
    )
    }