rebo / seed-quickstart-hooks

Example of how react style hooks might integrate with Seed.
5 stars 0 forks source link

useState and useRef and seeds implementation of use_state #2

Closed rebo closed 4 years ago

rebo commented 4 years ago

In React useState and useRef are very similar with some subtle differences.

Both functions store a state that can be retained over subsequent renders.

The main difference is that useState()'s setState is asynchronous and will enqueue a re-render. useRef is synchronous and will not force a re-render.

useRef accesses the current state via .current().

In this way the current Seed/Comp_state implementation of use_state() is more similar to Reacts useRef() than it is to useState().

I think this is the right choice due to the way Seed works. Event callbacks are all terminated in a Message which enqueues a re-render anyway. Therefore I don't see much of a requirement to follow React here. However it could trivially be added if needed.

The only impedance may be if a React developer wants to use Seed and expects use_state's set or update method to cause a re-render automatically. This could simply be explained in docs. If there are alternative views more than happy to consider them.

MartinKavik commented 4 years ago

I see useRef for the first time, but according to docs it's useful for access a child imperatively.

So let's try to rewrite their example to Seed. React:

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` points to the mounted text input element
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

Seed:

fn text_input_with_focus_button<Ms: 'static + Default>() -> Vec<Node<Ms>> {
    let input_el = use_state(ElRef<HtmlInputElement>::default);
    vec![
        input![
            el_ref(input_el.get()),
            attrs!{At::Type => "text"},
        ],
        button![
            ev(Ev::Click, move |_| { 
                input_el
                    .get()
                    .map_type::<HtmlElement>()  // we don't need to use it when we declare `input_el` as `HtmlElement` directly in `use_state` above
                    .get()   // it returns element only when the element is in the DOM; otherwise returns `None`
                    .expect("get HTMLElement from DOM")
                    .focus()   // focus element - it can fail according to `web_sys` type
                    .expect("focus element")
           }),
           "Focus the input"
        ]
    ]
}

And

However, useRef() is useful for more than the ref attribute. It’s handy for keeping any mutable value around similar to how you’d use instance fields in classes.

It's basically Seed's use_state if I'm not mistaken.

rebo commented 4 years ago

It's basically Seed's use_state if I'm not mistaken.

Yes it is exactly is that. I don't think we need the re-rendering capability and async dispatch of useState from React.

edit:: that said having it is definitely possible. And it would mean programatically updating a view (for instance from a fetch request) would be more straightforward.

rebo commented 4 years ago

Closing due to #5 being decided as api.