Skip to content

Instantly share code, notes, and snippets.

@g4rcez
Last active February 3, 2020 02:34
Show Gist options
  • Save g4rcez/8274b8065e9506b33315baf05eca8645 to your computer and use it in GitHub Desktop.
Save g4rcez/8274b8065e9506b33315baf05eca8645 to your computer and use it in GitHub Desktop.
A simple example with useReducer custom hook
import React, { useState, useMemo, Fragment } from "react";
// Lembrando pro cara que não pode haver reatribuição
// no estado pois ele é imutável (ou deveria ser)
type Immutable<State> = Partial<Readonly<State>>;
// Inferência dos tipos da função primária
type Infer<
State,
Fn extends (...args: never) => (state: State) => Immutable<State>
> = (...args: Parameters<Fn>) => (state: State) => Immutable<State>;
// apenas um utils para extender nos tipos
type ReducerChunk<Actions, State> = {
[key in keyof Actions]: (args: any) => (state: State) => Immutable<State>;
};
export type Dispatches<State, Actions extends ReducerChunk<Actions, State>> = {
[key in keyof Actions]: Infer<State, Actions[key]>;
};
const useReducer = <State, Actions extends ReducerChunk<Actions, State>>(
initialState: State,
actions: Actions
): [State, Dispatches<State, Actions>] => {
const [state, setState] = useState(initialState);
// memoizando as actions para evitar novos objetos
const dispatches = useMemo(
() =>
Object.entries(actions).reduce(
(acc, [name, dispatch]: [string, any]) => ({
...acc,
[name]: (...params: any) => {
const event = dispatch(...params);
setState(currentState => ({ ...currentState, ...event(...params) }));
}
}),
{} as Dispatches<State, Actions>
),
[actions]
);
return [state, dispatches];
};
type STATE = {
name: string;
age: number;
points: number;
isApproved: boolean;
};
const initialState: STATE = {
name: "",
age: 0,
points: 0,
isApproved: false
};
const App = () => {
const [state, reducers] = useReducer(initialState as STATE, {
onChangeName: (e: React.ChangeEvent<HTMLInputElement>) => {
const { value } = e.target;
// Se for passar o evento para essa próxima função
// não se esqueça de usar e.persist()
// mais informações:
// https://reactjs.org/docs/events.html#event-pooling
return (): Partial<STATE> => ({ name: value });
},
onChangeNumber: (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
return (): Partial<STATE> => ({
[name as "age" | "points"]: Number.parseFloat(value)
});
},
onChangeCheckbox: (e: React.ChangeEvent<HTMLInputElement>) => {
const { checked } = e.target;
return (): Partial<STATE> => ({ isApproved: checked });
}
});
return (
<Fragment>
<input name="name" onChange={reducers.onChangeName} value={state.name} />
<input
type="number"
name="age"
onChange={reducers.onChangeNumber}
value={state.age}
/>
<input
type="number"
name="points"
onChange={reducers.onChangeNumber}
value={state.points}
/>
<input
type="checkbox"
name="isApproved"
onChange={reducers.onChangeCheckbox}
checked={state.isApproved}
/>
</Fragment>
);
};
export default App;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment