icon

FRETS 0.2.3 - Now With More FP

I wanted to make it easier to get started with a new FRETS app, so I refactored it into one class with a more functional approach to registering the various parts of the app.

What’s new in version 0.2.3?

Primarily, a new way of instantiating applications that is more functional and less dependent on big ugly configuration objects. The idea is to still use the goodness of TypeScript generics while making it more functional and obvious how to set up a new app.

1
import { FRETS } from "frets";

The older class exports are still available, but now all you need to get started is this one FRETS class.

You kick things off by writing a very lightweight actions class for your app.

1
2
3
4
5
6
export class MyActions {
public changeName: (e: Event) => void;
public saveName: (e: Event) => void;
public startOver: (e: Event) => void;
}

Notice there are no actual implementations in this class! We can still get the help of referencing at action by name in our view, but the function will be assigned at runtime. When you write your view rendering function it still looks pretty much the same.

So, here’s how the minimum initialization procedure actually looks.

1
2
3
4
const F = new FRETS<QuizProps, WheelActions>(new QuizProps(), new WheelActions());
F.mountTo("mainapp");

There are default methods for everything, but you will want to set a real render function using registerView().
We still use the generated atomic BaseStyles class to build dom VNodes.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const renderRootView = (props: QuizProps, actions: WheelActions): VNode => {
return $$().div.border.p3.m3.h([
$$().label.pr1.h(["Your Name"]),
$$().input.h({
type: "text",
onchange: actions.changeName,
value: props.name
}),
$$().div.p2.h([
$$().button.btn.btnPrimary.h({ onclick: actions.saveName },["Save"]),
]),
]);
}
F.registerView(renderRootView);
F.mountTo("mainapp");

And of course theres no actual implementation for the actions so you will want to set those using the handy dandy registerAction() method.

1
2
3
4
F.actions.changeName = F.registerAction((e: Event, data: QuizProps) => {
data.name = (e.target as HTMLInputElement).value;
return data;
});

And that means we just need to validate our data changes and calculate any other related state variables that need to change. We should override the built in validator and calculator because they are empty passthroughs out of the box.

1
2
3
4
5
6
7
8
9
10
11
F.validator = (newProps: QuizProps, oldProps: QuizProps): QuizProps => {
if (!newProps.name) {
newProps.errors.name = true;
}
return newProps;
};
F.calculator = (newProps: QuizProps, oldProps: QuizProps): QuizProps => {
newProps.answers.reduce((acc, x) => x * 10 + acc);
return newProps
};

I’m reading the excellent book Functional-Light JavaScript: Pragmatic, Balanced FP in JavaScript. As I make it further into this book I expect I will revisit FRETS and make more updates. We shall see. I wonder if I’m willing to give up some of the TypeScript features for a more pure FP approach.