I have a React component that contains a text <input> element. When the component is mounted, I want the text cursor to be set in the input field, i.e. I want the text input element to have the focus.
In a “traditional” JavaScript React component, I would get the input field's DOM element through a ref and then call its focus
method.
I've read this documentation that explains how to use refs in Reason-React: https://github.com/reasonml/reason-react/blob/master/docs/react-ref.md
Alas, the code sample contained on this page is for refs of custom component, it only mentions that it also works on React DOM elements.
So I've tried to convert the example code to a React DOM element, here is what I've tried so far:
type state = {
text: string,
inputElement: ref(option(Dom.element))
};
let valueFromEvent = (evt) : string => (
evt
|> ReactEventRe.Form.target
|> ReactDOMRe.domElementToObj
)##value;
let component = ReasonReact.reducerComponent("EditTodoField");
let setInputElement = (theRef, {ReasonReact.state}) =>
state.inputElement := Js.Nullable.to_opt(theRef);
let make = (~initialText, ~onSubmit, _) => {
...component,
initialState: () => {text: initialText, inputElement: ref(None)},
reducer: (newText, state) => ReasonReact.Update({...state, text: newText}),
render: ({state: {text}, reduce, handle}) =>
<input
value=text
_type="text"
ref=(handle(setInputElement))
placeholder="Todo description"
onChange=(reduce((evt) => valueFromEvent(evt)))
onKeyDown=(
(evt) =>
if (ReactEventRe.Keyboard.key(evt) == "Enter") {
onSubmit(text);
(reduce(() => ""))()
} else if (ReactEventRe.Keyboard.key(evt) == "Escape") {
onSubmit(initialText);
(reduce(() => ""))()
}
)
/>
};
The error message I get is this:
We've found a bug for you!
/Users/pahund/git/todo-list-reason-react/src/EditTodoField.re 21:11-35
19 ┆ value=text
20 ┆ _type="text"
21 ┆ ref=(handle(setInputElement))
22 ┆ placeholder="Todo description"
23 ┆ onChange=(reduce((evt) => valueFromEvent(evt)))
This has type:
ReasonReact.Callback.t(Js.Nullable.t(Dom.element)) (defined as
(Js.Nullable.t(Dom.element)) => unit)
But somewhere wanted:
(Js.null(Dom.element)) => unit
The incompatible parts:
Js.Nullable.t(Dom.element) (defined as Js.nullable(Dom.element))
vs
Js.null(Dom.element)
I know that the problem probably lies within how I define the type of the state at the beginning of the code, it's different for DOM elements than it is for custom components.
What would be the correct type definition here to fix the bug?
The full project can be found here on GitHub: https://github.com/pahund/todo-list-reason-react/tree/ref-problem
I think your reason-react dependency is out of date. ref
s were changed from Js.null(Dom.element)
to Js.nullable(Dom.element)
in 0.3.0. See https://github.com/reasonml/reason-react/blob/master/HISTORY.md#030
If for some reason you can't or refuse to upgrade, you can just use Js.Null.to_opt
instead though :)
(Also, if you do upgrade, you can use Js.toOption
as a nice shortcut in place of Js.Nullable.to_opt
)