gg logo

gg

Documentation

Changelog

Blog

Another way to update state

→ Click here to see the state of the app after completing this step.

→ Click here to see the changes we'll make in this step in GitHub.


Looking back at what we did to make the state for the first number work, it ended up with a lot of duplication. We had to define basically the same set-state-handler separately for each number from one to nine, because we can't access variables outside the scope of the reducer function.

Apart from duplication this also bloats the size of the JavaScript that gg includes in the final output, because each set-state-handler results in a separate JavaScript function.

However, there is a way to reduce our code to having only one set-state-handler! Let's see how we can make this work.

We start by adding a single set-state-handler that will handle clicks on all button of our calculator. For now, we will just return the current value of the state.

3const firstNumber = gg.state(""); 4 +5const setFirstNumber = gg.setState(firstNumber, (value) => {+6 return value;+7}); 8

What we want to do is pass this handler to all buttons. We need to find a way to figure out, which button was clicked inside our reducer function. We can use the second argument that gg passes when calling our reducer function. This will be the DOM event that is dispatched when clicking the button.

4 -const setFirstNumber = gg.setState(firstNumber, (value) => {+5const setFirstNumber = gg.setState(firstNumber, (value, event) => {+6 if (!(event.target instanceof HTMLButtonElement)) {+7 return value;+8 }+9 const buttonText = event.target.innerText; 10 return value; 11});

The first if statement is just to make TypeScript happy, because event.target is not always a HTML element. After that we can find out which button was clicked by looking at the text of the button. You could also add id attribute or data-* attribute to your elements which you can then read client-side inside your reducer functions.

Now that we know which button click we have to handle, let's reimplement the functionality for all digits and the decimal point.

9 const buttonText = event.target.innerText;+10 if (["1", "2", "3", "4", "5", "6", "7", "8", "9"].includes(buttonText)) {+11 return value + buttonText;+12 }+13 if (buttonText === "0" && value) {+14 return value + buttonText;+15 }+16 if (buttonText === "." && !value.includes(".")) {+17 return value + buttonText;+18 } 19 return value;

Now we can use the new set-state-handler in the Button component instead of the ones passed via the custom argument.

66 return (- <button class={classes} onclick={args.onclick}>+67 <button class={classes} onclick={setFirstNumber}> 68 {args.children} 69 </button> 70 );

Now we can do some cleanup. The onclick custom attributes are not used anymore in the Button components, so we can remove them.

111 <div>- <Button onclick={gg.setState(firstNumber, (value) => value + "7")}>- 7- </Button>- <Button onclick={gg.setState(firstNumber, (value) => value + "8")}>- 8- </Button>- <Button onclick={gg.setState(firstNumber, (value) => value + "9")}>- 9- </Button>+112 <Button>7</Button>+113 <Button>8</Button>+114 <Button>9</Button> 115 <Button>{"&times;"}</Button> 116 </div> 117 <div>- <Button onclick={gg.setState(firstNumber, (value) => value + "4")}>- 4- </Button>- <Button onclick={gg.setState(firstNumber, (value) => value + "5")}>- 5- </Button>- <Button onclick={gg.setState(firstNumber, (value) => value + "6")}>- 6- </Button>+118 <Button>4</Button>+119 <Button>5</Button>+120 <Button>6</Button> 121 <Button>-</Button> 122 </div> 123 <div>- <Button onclick={gg.setState(firstNumber, (value) => value + "1")}>- 1- </Button>- <Button onclick={gg.setState(firstNumber, (value) => value + "2")}>- 2- </Button>- <Button onclick={gg.setState(firstNumber, (value) => value + "3")}>- 3- </Button>+124 <Button>1</Button>+125 <Button>2</Button>+126 <Button>3</Button> 127 <Button>+</Button> 128 </div> 129 <div>- <Button- isDoubleWidth- onclick={gg.setState(firstNumber, (value) =>- value ? value + "0" : value,- )}- >- 0- </Button>- <Button- onclick={gg.setState(firstNumber, (value) =>- value.includes(".") ? value : value + ".",- )}- >- .- </Button>+130 <Button isDoubleWidth>=</Button>+131 <Button>.</Button> 132 <Button>=</Button> 133 </div>

We can also remove the custom attribute from the Button component and the SetStateHandler import.

-import { gg, SetStateHandler } from "../deps.ts";+1import { gg } from "../deps.ts"; 56type ButtonArgs = { 57 isDoubleWidth?: boolean;- onclick?: SetStateHandler; 58};

At last, we can remove the type export again in out deps.ts file.

1export { default as gg } from "https://gg.thomasheyenbrock.com/code/<gg-token>/gg@0.1.0/mod.ts";-export type { SetStateHandler } from "https://gg.thomasheyenbrock.com/code/<gg-token>/gg@0.1.0/mod.ts";

After having reached a implementation of our state that is more maintainable, let's again work on more functionality for our calculator.


Next → State for the operator