Skip to content

Timsy Compatibility

This project was heavily-inspired by the simplicity, elegance and type-safety of timsy.

As it happens, similar usage is possible.

const states = defineStates({
NOT_LOADED: () => ({}),
LOADING: () => ({}),
LOADED: (data: { whatever: true }) => ({ data }),
ERROR: (error: Error) => ({ error }),
});
type DataState = FactoryKeyedState<typeof states>;
export 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>
);
};
import {
createMachine,
defineStates,
effect,
setup,
whenState,
addEventApi,
} from "matchina";
import { useMachine } from "matchina/react";
import React from "react";
type Data = { whatever: true };
const states = defineStates({
NOT_LOADED: () => ({}),
LOADING: () => ({}),
LOADED: (data: Data) => ({ data }),
ERROR: (error: Error) => ({ error }),
});
const createDataMachine = () => {
const machine = addEventApi(
createMachine(
states,
{
NOT_LOADED: {
load: () => () => states.LOADING(),
},
LOADING: {
loadSuccess: (data: Data) => () => states.LOADED(data),
loadError: (error: Error) => () => states.ERROR(error),
},
LOADED: {},
ERROR: {},
},
"NOT_LOADED"
)
);
setup(machine)(
effect(
whenState("NOT_LOADED", () => {
fetch("/data")
.then((response) => response.json())
.then(machine.api.loadSuccess)
.catch(machine.api.loadError);
})
)
);
return machine;
};
// TODO: fix TS here:
// const createDataMatchina = () =>
// matchina(
// states,
// {
// NOT_LOADED: {
// load: () => () => states.LOADING(),
// },
// LOADING: {
// loadSuccess: (data: Data) => () => states.LOADED(data),
// loadError: (error: Error) => () => states.ERROR(error),
// },
// LOADED: {},
// ERROR: {},
// },
// "NOT_LOADED",
// )
export const DataComponent: React.FC = () => {
const dataMachine = React.useMemo(() => createDataMachine(), []);
useMachine(dataMachine);
const state = dataMachine.getState();
return (
<div>
{state.match({
NOT_LOADED: () =>
(
<button
onClick={() => {
dataMachine.api.load();
}}
>
Load Data
</button>
) as any,
LOADING: () => "Loading...",
LOADED: ({ data }) => JSON.stringify(data),
ERROR: ({ error }) => `ops, ${error.message}`,
})}
</div>
);
};
const machine = addEventApi(
createPromiseMachine((_id: number) =>
fetch("/data").then((response) => response.json())
)
);
export const DataComponent: React.FC = () => {
useMachine(machine);
const state = machine.getState();
return (
<div>
{state.match({
Idle: () =>
(
<button
onClick={() => {
machine.execute(123);
}}
>
Load Data
</button>
) as any,
Pending: () => "Loading...",
Resolved: (value) => JSON.stringify(value),
Rejected: (error) => `ops, ${error.message}`,
})}
</div>
);
};
const states = defineStates({
FOO: () => ({}),
BAR: () => ({}),
BAZ: () => ({}),
});
const runMachine = createMachine(
states,
{
FOO: {
switch: () => () => states.BAR(),
},
BAR: {
switch: () => () => states.FOO(),
},
BAZ: {
switch: () => () => states.BAZ(),
},
},
"FOO"
);
const machine = withSubscribe(addEventApi(runMachine));
machine.api.switch();
const currentState = machine.getState();
console.log("Current state:", currentState.key);
const unsubscribe = machine.subscribe
? machine.subscribe((change) => {
// Any change
const { type, from, to, params } = change;
console.log(
`Change detected: type=${type}, from=${from.key}, to=${to.key}, params=${JSON.stringify(
params
)}`
);
change.match({
switch: function (..._args: any[]) {
console.log("switching!");
unsubscribe?.();
},
});
return () => {
console.log("exiting", change);
};
})
: undefined;
// 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) => {},
// );
const states = defineStates({
IDLE: () => ({}),
DETECTING_RESIZE: (initialX: number) => ({ initialX }),
RESIZING: (x: number) => ({ x }),
});
const machine = addEventApi(
createMachine(
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);