7

I'm using Redux Form (RF) in a React Native application. Everything works fine but I can not figure out how to get the refs from the Field input to go to the next input field with Redux Form.

Without RF this solution would work just fine.

Here is my code:

class RenderInput extends Component {
   const { input, nextField, refs,
        meta: { touched, error, warning },
        input: { onChange } } = this.props
   render() {
      return (
         <Input
            returnKeyType = {'next'}
            onChangeText={onChange}
            onBlur={input.onBlur}
            onFocus={input.onFocus}
            onSubmitEditing = {(event) => {
               // refs is undefined
               refs[nextField].focus()
            }}/>
      )
   }
}

class Form extends Component {
   render() {
      return (
         <Field
            name="field1"
            focus
            withRef
            ref='field1'
            nextField = "field2"
            component={RenderInput}/>

         <Field
            name="vendor"
            withRef
            ref="field2"
            nextAction = "field3"
            component={RenderInput}/>
      )
   }
}

I'm passing on the property nextField to the component to determine the next input field when the Next key on the keyboard is clicked but I can not get the refs property inside the RenderInput component.

Any idea how to get the refs property?

Community
  • 1
  • 1
Thomas Dittmar
  • 1,764
  • 1
  • 23
  • 42
  • Just saying, assigning a ref directly is deprecated. The best thing to do, is assigning the ref to a global variable that you can reuse after: `ref={(componentRef) => this.myRef = componentRef}`. See [React Documentation](http://reactjs.cn/react/docs/more-about-refs.html) – Val Berthe May 11 '17 at 15:40
  • I posted here https://stackoverflow.com/a/56401396/6242582 how I solved this issue. – Hernán Albertario May 31 '19 at 20:33

5 Answers5

13

This solution passes props from the Form component to the RenderInput component and passes a function call back.

Here's the code:

class RenderInput extends Component {
   const { input, refField, onEnter,
        meta: { touched, error, warning },
        input: { onChange } } = this.props
   render() {
      return (
         <TextInput
            ref = {refField}
            returnKeyType = {'next'}
            onChangeText={onChange}
            onBlur={input.onBlur}
            onFocus={input.onFocus}
            onSubmitEditing={onEnter}/>
      )
   }
}

class Form extends Component {
   render() {
      return (
         <Field
            name="field1"
            focus
            withRef
            ref={(componentRef) => this.field1 = componentRef}
            refField="field1"
            component={RenderInput}
            onEnter={() => { 
               this.field2.getRenderedComponent().refs.field2.focus()
            }}/>

         <Field
            name="field2"
            withRef
            ref={(componentRef) => this.field2 = componentRef}
            refField="field2"
            component={RenderInput}/>
      )
   }
} 

So what happened here?

  1. I assign the ref to local scope with ref={(componentRef) => this.field1 = componentRef} as @Ksyqo suggested. Thanks for the hint.

  2. I pass refField="field1" to the RenderInput and assign the value to the input ref property ref = {refField}. This will add the input object to the refs property.

  3. I assigned a onEnter function in the Field

  4. I pass the function to the props of RenderInput and assign it to the onSubmitEditing={onEnter} function. Now we have bind the two functions together. That means if onSubmitEditing gets invoked the onEnter function gets invoked as well

  5. Finally, refer to the local field field2, get the rendered Component and use the refs, which we assigned in the Input field, to set the focus. this.field2.getRenderedComponent().refs.field2.focus()

I don't know if this is the most elegant solution but it works.

Thomas Dittmar
  • 1,764
  • 1
  • 23
  • 42
  • One issue I'm still having is that if I import the RenderInput class from from another file then I get the error focus is not a function. but If i define it with the same file it works. – jasan Jun 16 '17 at 09:15
  • Make sure your `export` and `import` the **RenderInput** component correctly. The solution I posted supposed to be two separate files. It definitely works on my end. – Thomas Dittmar Jun 16 '17 at 09:24
  • 2
    Also, it is very important to use a React component such as `class RenderInput extends Component{}` as **getRenderedComponent()** doesn't work on a stateless function component. – Thomas Dittmar Jun 16 '17 at 09:31
  • Weird. The RenderInput does render on to screen. its just the foucs() function that fails. – jasan Jun 16 '17 at 10:06
  • That is weird. Just `console.log(this.field2.getRenderedComponent().refs)` in the `onEnter` function of the `Field` to see whether you get the correct `componentRef` component. `refs` should be an object with all fields as props and double check the `refField` props of the `Field` – Thomas Dittmar Jun 16 '17 at 10:13
  • I figured it out: I was using FormInput component(react-native-elements) instead of TextInput in the external file. and apparently Forminput does not have the focus function – jasan Jun 16 '17 at 11:07
  • Well spotted. I forgot to mention that I was using the native-base component **Input**, which is inherited from react-native **TextInput**. I will update the answer accordingly! – Thomas Dittmar Jun 16 '17 at 11:11
  • is it possible for you to share the the gist? I also tried Input from native-base it didn't work it only works with TextInput – jasan Jun 16 '17 at 11:25
  • Update: the following is what worked for me when using Input from Native-base. ;`field2.getRenderedComponent().refs.field2._root.focus()` – jasan Jun 16 '17 at 11:51
  • 1
    May be it worked in past but it doesn't not work right now. `field2.getRenderedComponent().refs` is empty object now – Viktor Jan 24 '18 at 07:24
1

For people who are using Redux Form + React Native Elements, just follow @Thomas Dittmar answer, and add the following prop to the 'FormInput' component: textInputRef={refField}

The newest version of React Native Element has added the focus() method, so you don't have to worry about that.

Zach
  • 9
  • 2
  • I"m facing an issue where doing this breaks the field's validation (it always validates to true). Have you faced that before? – spinningarrow Sep 07 '17 at 07:08
1

withRef is deprecated, use forwardRef instead.

Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
7laria
  • 44
  • 3
0

I worked on getting a ref like this that worked for me.

           const renderComp = ({
             refName,
             meta: { touched, error },
             input,
             ...custom
           }) => (
             <MyComponent
               ref={refName}
               {...custom}
             />
           )

            <Field
              name={name}
              component={renderComp}
              ref={node =>
                isLeft ? (this.compRef1 = node) : (this.compRef2 = node)}
              refName={node =>
                 (this.myRef= node) }
              withRef
            />

now access instance functions like this.

this.myRef.anyFunc()
tariq zafar
  • 659
  • 7
  • 24
0

I had a slightly different use case, but I imagine it works for the above as well.

I used the focus action

import { focus } from 'redux-form';
dispatch(focus('signIn', 'email'));

Then in the (custom) form field that contains the TextInput, I added in the render function

<TextInput
    ref="email"
/>
formStates.filter((state) => meta[state]).map((state) => {
   if(state === 'active'){
      this.refs.email.focus()
   }
})

No worries about the component nesting/hierarchy anymore.

seppestaes
  • 31
  • 2