question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

`useField()` hook

See original GitHub issue

Feature Request

It’d be nice to have a useField() hook for the existing react-final-form. This would give basically the same performance as <Field>, but with data returned from the hook instead of passed to the render prop.

useField(props: FieldProps): FieldRenderProps

The idea would be to get the form store via context, then subscribe to it within a useEffect(), and update a useState() each time it changes. Basically, implementation would look exactly like the hooks version of <Field>.

This would allow for fields to be created without the render prop, which would be nice when creating styled field components. For example:

function Field(props)
  let { input, meta } = useField(props)
  return (
    <StyldeField>
      <StyledLabel>Bio</StyledLabel>
      <StyledTextarea {...input} />
      {meta.touched && meta.error && <StyledError>{meta.error}</StyledError>}
    </StyledField>
  )
)

And the corresponding <Field> based example from the README:


function StyledField(props) {
  return (
    <Field
      {...props}
      render={({ input, meta }) => (
        <StyledFieldWrapper>
          <StyledLabel>Bio</StyledLabel>
          <StyledTextarea {...input} />
          {meta.touched && meta.error && <StyledError>{meta.error}</StyledError>}
        </StyledFieldWrapper>
      )}
    />
  )
}

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
wtgtybhertgeghgtwtgcommented, Mar 4, 2019

With hooks, if you are using useField(), your entire form must rerender on every keypress

I played around with a version of useField using the current context setup of react-final-form and haven’t found that to be the case. It isn’t very polished, but is there something I’m missing?

// useField.js
import {fieldSubscriptionItems} from 'final-form';
import {useContext, useEffect, useRef, useState} from 'react';
import {ReactFinalFormContext} from 'react-final-form';

export const all = fieldSubscriptionItems.reduce((result, key) => {
  result[key] = true;
  return result;
}, {});

function eventValue(event) {
  if (!event || !event.target) {
    return event;
  }

  if (['checkbox', 'radio'].includes(event.target.type)) {
    return event.target.checked;
  }

  return event.target.value;
}

export default function useField(name, subscription = all) {
  const autoFocus = useRef(false);
  const [
    {blur, change, focus, name: metaName, value = '', ...meta},
    setState,
  ] = useState({});
  const form = useContext(ReactFinalFormContext);
  useEffect(() => {
    form.registerField(
      name,
      newState => {
        if (autoFocus.current) {
          autoFocus.current = false;
          setTimeout(() => newState.focus());
        }
        setState(newState);
      },
      subscription,
    );
  }, [
    form,
    name,
    ...fieldSubscriptionItems.map(key => Boolean(subscription[key])),
  ]);
  return {
    input: {
      onBlur: () => blur(),
      onChange: event => change(eventValue(event)),
      onFocus: () => {
        if (focus) {
          focus();
        } else {
          autoFocus.current = true;
        }
      },
      name,
      value,
    },
    meta,
  };
}
// App.js
import React from 'react';
import {Form} from 'react-final-form';
import useField from './useField';

const MyField = React.memo((props) => {
  const {name, placeholder} = props;
  console.log(`Rendering "${name}"!`);
  const {input, meta} = useField(name, {value: true});
  return (
    <>
      <input {...input} placeholder={placeholder} />
      {meta.touched && meta.error && (
        <span>{meta.error}</span>
      )}
    </>
  );
});

export default function MyForm() {
  const onSubmit = whatever => console.log(whatever);
  return (
    <Form
      onSubmit={onSubmit}
      render={({handleSubmit, invalid, pristine}) => {
        console.log('Rendering form!');
        return (
          <form onSubmit={handleSubmit}>
            <div>
              <label>First Name</label>
              <MyField name="firstName" placeholder="First Name" />
            </div>
            <div>
              <label>Last Name</label>
              <MyField name="lastName" placeholder="Last Name" />
            </div>

            <button type="submit" disabled={pristine || invalid}>
              Submit
            </button>
          </form>
        );
      }}
      subscription={{submitting: true, pristine: true}}
    />
  );
}
1reaction
erikrascommented, Mar 4, 2019

Thanks for this issue, @jamesknelson. It’s very helpful to have other people attack a problem you’ve attempted to solve and to evaluate the differences.

So yes, RFFH is the package that uses hooks to use FF. What Render Props™ give you that hooks cannot – and never will be able to (somewhat validated) – is the ability to let your components (e.g. Field) control when they rerender. With hooks, if you are using useField(), your entire form must rerender on every keypress, so the whole “optimization through subscriptions” architecture of FF is thrown to the wind. The good news is, however, that 96% of forms don’t need that level of optimizations, so hooks are great.

The most amazing thing about the hooks announcement with relation to Final Form is the validation of keeping form state in a separate container, outside of React. Within hours of the hooks announcement, I was able to whip up a version of RFF with almost no effort.

Read more comments on GitHub >

github_iconTop Results From Across the Web

useField() - Formik
useField is a custom React hook that will automagically help you hook up inputs to Formik. You can and should use it to...
Read more >
The useField Hook - Formik for Beginners - Code Daily
Intro. The Field component pre-dates the release of hooks. Hooks help remove much of the complexity and helps integrate Formik into more complex...
Read more >
Final Form Docs – `useField()`
The useField() hook takes two parameters: ... i.e. the component will only rerender if the field state subscribed to via useField() changes.
Read more >
useField() hook - Formiz
When using the useField hook, you need to pass your component props to the hook. Then the hook gives you access to the...
Read more >
How to properly use useField hook from Formik in typescript
I used the FieldHookConfig as the type for the props because the useField is expecting a string or the FieldHookConfig base on the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found