I’m having trouble figuring out how useReducer and React updates work in general. I’ve made a minimal example. First, a place to store the data that I want to share among various components:
// Store.res
type state = {
thickness: int,
}
let initialState: state = {
thickness: 10,
}
type action =
| ChangeThickness({thickness: int})
| NoOp
let updateThickness: (state, int) => state = (s:state, t: int) => {...s, thickness: t};
let reducer = (state: state, action: action) => {
switch action {
| ChangeThickness({thickness}) => updateThickness(state, thickness)
| NoOp => state
}
}
Second, a component that uses this:
//Controls.res
@react.component
let make = (~state: Store.state, ~dispatch: Store.action => unit) => {
<main>
<form>
<label htmlFor="thickness"> {React.string("Thickness ")} </label>
<input
type_="number" id="thickness" min="0" max="10"
value={Js.Int.toString(state.thickness)}
onChange={event => {
dispatch(
Store.ChangeThickness({
thickness: int_of_string(ReactEvent.Form.target(event)["value"]),
}),
)
}}
/>
<br />
<span className="foo"> {state.thickness->string_of_int->React.string} </span>
<br />
<button
onClick={_ => {
dispatch(Store.NoOp)
}}>
{React.string("Refresh")}
</button>
</form>
</main>
}
and third, an App that loads those controls:
//App.res
@react.component
let make = () => {
let (state, dispatch) = React.useReducer(Store.reducer, Store.initialState);
(<div><Controls state dispatch/></div>);
}
Finally, the html file, index.html
:
<!DOCTYPE html>
<html lang="en">
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
The result? When I click the up-and-down arrows on the “thickness” field, the displayed value goes up and down; that part’s great. Suppose I click down until it’s, say, 6. If I then click on the Refresh
button, everything goes back to 10 (!).
While trying to create this minimal example of behaviour I wasn’t expecting, I tried removing the <form>...</form>
pair. in Controls.res
. When I do that…the behavior is what I’d expect (namely, the “6” remains “6”).
Can someone explain?
On a rather different (and more ReScript-y) note, if in Controls.res
I change the line
thickness: int_of_string(ReactEvent.Form.target(event)["value"]),
to
thickness: ReactEvent.Form.target(event)["value"],
I don’t get any actual type-error, although I expected one. Looking at the javascript code executing, I see that the thickness value changes from 10
to "9"
and so on, remaining a string as I keep clicking. This seems very weird to me.
Can someone explain this? It seems like a real violation of type-safety.