Timsy Compatibility
This project was heavily-inspired by the simplicity, elegance and type-safety of timsy.
As it happens, similar usage is possible.
Make state explicit
39 lines,
1064 chars
const states = defineStates({
NOT_LOADED: () => ({}),
LOADING: () => ({}),
LOADED: (data: { whatever: true }) => ({ data }),
ERROR: (error: Error) => ({ error }),
});
type DataState = FactoryState<typeof states>;
const DataComponent: React.FC = () => {
const [state, setState] = useState<DataState>(states.NOT_LOADED());
return (
<div>
{state.match({
NOT_LOADED: () =>
(
<button
onClick={() => {
fetch("/data")
.then((response) => response.json())
.then((data) => setState(states.LOADED(data)))
.catch((error) => setState(states.ERROR(error)));
}}
>
Load Data
</button>
) as any,
LOADING: () => "Loading...",
LOADED: ({ data }) => JSON.stringify(data),
ERROR: ({ error }) => `ops, ${error.message}`,
})}
</div>
);
};
Make state predictable
70 lines,
1537 chars
type Data = { whatever: true };
const states = defineStates({
NOT_LOADED: () => ({}),
LOADING: () => ({}),
LOADED: (data: Data) => ({ data }),
ERROR: (error: Error) => ({ error }),
});
const dataMachine = withApi(
createFactoryMachine(
states,
{
NOT_LOADED: {
load: () => () => states.LOADING(),
},
LOADING: {
loadSuccess: (data: Data) => () => states.LOADED(data),
loadError: (error: Error) => () => states.ERROR(error),
},
LOADED: {},
ERROR: {},
},
"NOT_LOADED",
),
);
const DataComponent: React.FC = () => {
// soon
// const [state, events, useTransitionEffect] = useMachine(() =>
// dataMachine(states.NOT_LOADED())
// )
const state = dataMachine.getState();
const { api } = dataMachine;
// soon
//useTransition("LOADING",
const onLoad = () => {
fetch("/data")
.then((response) => response.json())
.then(api.loadSuccess)
.catch(api.loadError);
};
//)
return (
<div>
{state.match({
NOT_LOADED: () =>
(
<button
onClick={() => {
api.load();
}}
>
Load Data
</button>
) as any,
LOADING: () => "Loading...",
LOADED: ({ data }) => JSON.stringify(data),
ERROR: ({ error }) => `ops, ${error.message}`,
})}
</div>
);
};
Make promises safe
41 lines,
1075 chars
const machine = withApi(
createPromiseMachine((id: number) =>
fetch("/data").then((response) => response.json()),
),
);
const state = machine.getState();
type S = Expand<typeof state>;
const DataComponent: React.FC = () => {
const state = machine.getState();
// useTransitionEffect("RESOLVED", ({ value }) => {
// // Pass resolved data into other state stores or react
// // to transitions
// })
return (
<div>
{state.match({
Idle: () =>
(
<button
onClick={() => {
machine.api.execute(123);
}}
>
Load Data
</button>
) as any,
Pending: () => "Loading...",
Resolved: (value) => JSON.stringify(value),
Rejected: (error) => `ops, ${error.message}`,
})}
</div>
);
};
Core Api
67 lines,
1542 chars
const states = defineStates({
FOO: () => ({}),
BAR: () => ({}),
BAZ: () => ({}),
});
const runMachine = createFactoryMachine(
states,
{
FOO: {
switch: () => () => states.BAR(),
},
BAR: {
switch: () => () => states.FOO(),
},
BAZ: {
switch: () => () => states.BAZ(),
},
},
"FOO",
);
const machine = withNanoSubscribe(withApi(runMachine));
machine.api.switch();
const currentState = machine.getState();
const unsubscribe = machine.subscribe((change) => {
// Any change
const { type, from, to, params } = change;
change.match({
switch: function (...args: any[]) {
console.log("switching!");
},
});
return () => {
console.log("exiting", change);
};
});
// machine.when({ to: "FOO" }, ({ to }) => {
// // When first entering either state
// return () => {
// // When exiting to other state
// };
// });
// machine.when({ to: ["FOO", "BAR"] }, ({ to }) => {});
// machine.when({ to: "FOO", type: "switch" }, ({ to }) => {});
// machine.when({ to: ["FOO", "BAR"], type: "switch" }, (change) => {});
// machine.when({ type: "switch", to: "FOO", from: "BAR" }, (change) => {});
// machine.when(
// { to: ["FOO", "BAR"], type: "switch", from: "BAZ" },
// (change) => {},
// );
Real-world example
43 lines,
1227 chars
const states = defineStates({
IDLE: () => ({}),
DETECTING_RESIZE: (initialX: number) => ({ initialX }),
RESIZING: (x: number) => ({ x }),
});
const machine = withApi(
createFactoryMachine(
states,
{
IDLE: {
MOUSE_DOWN: (x: number) => () => states.DETECTING_RESIZE(x),
},
DETECTING_RESIZE: {
MOUSE_MOVE: (x: number) => (state: any) => {
if (Math.abs(x - state.initialX) > 3) {
return states.RESIZING(x);
}
return state;
},
// eslint-disable-next-line unicorn/consistent-function-scoping
MOUSE_UP: () => () => states.IDLE(),
// eslint-disable-next-line unicorn/consistent-function-scoping
MOUSE_UP_RESIZER: () => () => states.IDLE(),
},
RESIZING: {
MOUSE_MOVE: (x: number) => () => states.RESIZING(x),
// eslint-disable-next-line unicorn/consistent-function-scoping
MOUSE_UP: () => () => states.IDLE(),
},
},
"IDLE",
),
);
machine.api.MOUSE_DOWN(2);