teslamotors / informed

A lightweight framework and utility for building powerful forms in React applications
https://teslamotors.github.io/informed
MIT License
956 stars 173 forks source link

An idea for hooks without nesting... #141

Closed mikestopcontinues closed 5 years ago

mikestopcontinues commented 5 years ago

With new hooks support, accessing formState and formApi still requires you nest a custom component within your form. That's really just reimplementing HOC behavior instead of helping to flatten react's logic. Informed is so great because it's so flat. But wouldn't it be great if you could do something like this?

function CustomForm() {
  const formApi = useFormApi();

  return (
    <Form api={formApi}>
      <Select onBlur={() => formApi.submitForm()}/>
    </Form>
  );
}

At present, the closest you can do is the following, but it feels like this behavior should really be part of Informed, not a custom ref that isn't even guaranteed to be there on first load.

function CustomForm() {
  const formApi = useRef();

  return (
    <Form getApi={(api) => {
      formApi.current = api;
    }}>
      <Select onBlur={() => formApi.current.submitForm()}/>
    </Form>
  );
}
joepuzzo commented 5 years ago

Hmm i have not thought about this before. Its def a cool idea.. you would however need to hook more things up yourself. That being said i do like the look of it and i will add it to my todo to look into.

joepuzzo commented 5 years ago

Maybe i add a hook called useForm? i think that would be easy to implement.

mikestopcontinues commented 5 years ago

I'm glad you like it! I've only taken small peeks at the source, but it seems like you could do something like:

function useForm(opts) {
  const [form] = useState(new FormController(opts));
  return form;
}

class Form {
  constructor(props) {
    this.controller = props.form || new FormController();
  }
}

It looks like the challenge would be how you handle the opts. They seem to belong being stated on the element. But honestly, even if they stayed where they were, I think it would be an awesome step forward.

I'd also be happy to submit a PR, though I'm a bit hesitant because it seems like there are some architectural choices to this one.

mielecmichal commented 5 years ago

I also like this idea, but recently I have achieved something similar by following pattern. In my opinion it gives us exactly what we are trying to achieve.

 const [formState, setFormState] = useState({});

    return (
        <Form onChange={setFormState}>
            <h1>{JSON.stringify(formState)}</h1>
            <label>Name:<Text field="name"/></label>
            <button type="submit">Submit</button>
        </Form>)

It is my first time I use informed. Do you see any serious issues with presented pattern?

mikestopcontinues commented 5 years ago

Hey @mielecmichal,

setState immediately triggers a re-render. In the case of formState, that'll work most of the time. Unless you do something like—useEffect(() => changeInput(), [formState])—to trigger infinite renders. But in the case of useState() for formApi, I ran into a similar loop. It's possible I could have avoided it with useCallback() with <Form getApi={callback}/>, but I can't remember what I tested when I chose to go with useRef(). But if what you're doing works, that's all that matters!

joepuzzo commented 5 years ago

This is done and in latest release!!!!! Need to add docs but there is now a useForm hook and also a FormProvider component!!

mikestopcontinues commented 5 years ago

Woo! Thanks!

wahlstedt commented 5 years ago

Please add documentation on how to use FormProvider & useForm. Thanks.