Waldo sessions now support scripting! – Learn more
App Development

Refs in React Native Made Easy: A Direct Manipulation Guide

Nabendu Biswas
Nabendu Biswas
Refs in React Native Made Easy: A Direct Manipulation Guide
April 12, 2022
11
min read

Refs are used to interact with the DOM directly. It's not advisable in ReactJS to interact with the DOM, but sometimes it's required because some features aren't available in the ReactJS approach. Here, refs come to the rescue. Using refs, we can get access to some features of the DOM that aren't available in ReactJS.

In this post, we'll first learn about DOM. Then, we'll look at how to use this.refs in class-based components. After that, we'll see how to use refs with the useRef hook.

Lastly, I'll show some examples with setNativeProps, which is another way to use refs in React Native.

waldo pull quote

DOM and React

DOM stands for document object model. It's the programming interface through which we can update an HTML document. This programming is always done in JavaScript programming language. Suppose we have a simple webpage like this


<!DOCTYPE html>
<html lang="en">
<head>
   <title>HTML Basics</title>
</head>
<body>
   <div class="box-1">
      <h1>CSS Crash Course</h1>
     <h3>Best place to learn CSS</h3>
   </div>
</body>
</html>

and we want to select the div with the box-1 class. We'll do this in JavaScript by typing


document.querySelector('.box-1')

When we interact with the DOM through JavaScript, it's a slow process. And for this reason, ReactJS was built on a different programming approach. This approach is known as the virtual DOM. Behind the scenes, ReactJS also updates the DOM using the above type of logic. But that isn't visible to the developer.

In almost 95% of cases, we can work with the ReactJS programming approach. But in some cases, we need to write JavaScript a kind of code.

A popular example is the focus method, which is only available through DOM programming. Suppose we have a form in which there is an input field and we want the cursor to blink there the moment the user opens the webpage or mobile app.

This is only achievable through the DOM.

The Setup

In this project, we'll use React Native CLI and an iOS simulator. We need to start by setting up XCode on a Mac system. The setup steps are described in React Native's documentation.

Create a new project with the command npx react-native init <project-name> in the terminal. Here, <project-name> can be any name you want. We'll use the command below to create a new project with the name RNrefsDemo.


npx react-native init RNrefsDemo

If the installation is sucessful, you should see the following:

react native installation

Running the Project

Next, move to the project directory and run the command npx react-native start. You should see the screen below:

running the project

In a new terminal, run the command npx react-native run-ios to start the project on iOS simulator.

ios demo

Refs in Class Component

Now, we'll look at how to use refs in a class-based component.

Create a new folder called components in the root directory and add a file called RefsClass.js inside it. Then, remove everything in the App.js file and add the code below to it. Here, we have a SafeAreaView, and inside it we have a View component. We're showing our RefsClass component inside it.

Also, we've given the container the CSS style with flex. It'll show everything properly.


import { StyleSheet, View, SafeAreaView } from 'react-native'
import React from 'react'
import RefsClass from './components/RefsClass'
const App = () => {
return (    
    <SafeAreaView style={{ flex: 1 }}>
    <View style={styles.container}>
      <RefsClass />
    </View>
    </SafeAreaView>  
   )
}

   const styles = StyleSheet.create({  
          container: { flex: 1, padding: 10 }
    })
export default App

Now, in the RefsClass.js file, we'll first see the normal way with a state to show a TextInput and get data from the user. Here, we're using the concept of a controlled component, which is common both in ReactJS and React Native.

We'll need to declare a new state variable in the constructor. After that, in TextInput, we'll use the value to show the user entered the input. In the onChangeText, we'll change the text using setState. Here, we're re-rendering the component every time the user types in it.


import { Text, StyleSheet, TextInput } from 'react-native'
import React, { Component } from 'react'
class RefsClass extends Component {
    constructor(props) {
        super(props)
        this.state = { email: '' }
    }
    render() {
        const { email } = this.state;
        return (
            <>
                <Text style={styles.titleText}>Refs in Class component</Text>
                <TextInput
                    value={email}
                    placeholder="Enter your email"
                    style={styles.inputText}
                    onChangeText={email => this.setState({ email })}
                />
            </>
        )
    }
}
const styles = StyleSheet.create({
    titleText: { fontSize: 22, fontWeight: 'bold', textAlign: 'center', paddingVertical: 20,
     color: '#2009' },
    inputText: { textAlign: 'center', height: 40, width: '100%', borderWidth: 1,
     borderColor: 'midnightblue', marginBottom: 20 }
})
export default RefsClass

In the iOS simulator, we'll get a text input box that we can type in.

input box in ios

We'll add a new TextInput in our code, but we'll use refs to access its value. First, inside the constructor, create the ref using this.inputRef and the React function of createRef().

Next, inside the render(), we can directly attach it to a TextInput by passing the ref prop and giving the value this.inputRef.

One thing to note is that the component is not re-rendered whenever we type in a field with ref. This can be beneficial in certain cases where re-rendering causes the component to slow down.

We'll also use a focus() to have the cursor blinking on this text input by giving it in the life cycle method of componentDidMount(). The focus() method is only available in the DOM and not in the virtual DOM. So, if we want to use it, we need to use refs.

using refs in dom

Now, in the iOS simulator, type in the text input created using refs.

ref text input in ios

Refs in the Functional Component

Now, let's learn about how to use refs in functional components. Functional components have gone through major changes in React, and now we have the concept of state and life cycle methods in it. We need to implement them through the in-built hooks.

First, we'll create a new file called RefsHooks.js in the components folder. Here, we'll use controlled components to get the input value from the user. Notice that we're using the useState hook, which is the equivalent of this.state of a class-based component.


import { StyleSheet, Text, TextInput } from 'react-native'
import React, { useState } from 'react' const RefsHooks = () => {

const RefsHooks = () => {
    const [email, setEmail] = useState('')
    return (
        <>
        <Text style={styles.titleText}>Refs in Hooks component</Text>
        <TextInput
            value={email}
            placeholder="Enter your email"
            style={styles.inputText}
            onChangeText={email => setEmail(email)}
        />
  </>
 )
}

const styles = StyleSheet.create({
    titleText: { fontSize: 22, fontWeight: 'bold', textAlign: 'center',
    paddingVertical: 20, color: '#2009' },
    inputText: { textAlign: 'center', height: 40, width: '100%', borderWidth: 1,
     borderColor: 'midnightblue', marginBottom: 20 }
})

export default RefsHooks

Now, we need to show this new component in the App.js file.

refs in funcitonal component

In the iOS simulator, we can see the new text input, which we can type anything in.

refs in hooks component

We'll add a new TextInput in our code. But we'll use refs to access its value. First, use the useRef hook to create a ref variable.

Next, inside the return(), we can directly attach it to a TextInput by passing the ref prop and giving the value inputRef.

We'll also use a focus() to have the cursor blinking on this text input by giving it in the useEffect().

adding new text input in code

Now, in the iOS simulator, we can type in the text input created using refs.

demo in ios

setNativeProps in Class Component

The two examples of using refs above are exactly similar to its web counterpart. React Native provides us with a special type of refs called setNativeProps. We can also use it instead of refs in React Native.

So, create a new file called SetNativeClass.js in the components folder. Now, add this file in App.js.

importing setnativeprops

Then, in the file called SetNativeClass.js, put the content below. Here, in a TextInput, we'll give a ref called textInput. After that, in onChangeText, use it with setNativeProps.


import { Text, TextInput, StyleSheet } from 'react-native'
import React, { Component } from 'react'
class SetNativeClass extends Component {
    render() {
        return (
            <>
                <Text style={styles.titleText}>setNativeProps in Class component</Text>
                <TextInput
                    ref={component => { this.textInput = component}}
                    style={styles.inputText}
                    placeholder="Enter your name"
                    onChangeText= {text => this.textInput.setNativeProps({ text })}
                />
            </>
        )
    }
}
const styles = StyleSheet.create({
    titleText: { fontSize: 22, fontWeight: 'bold', textAlign: 'center', paddingVertical: 20,
    color: '#2009' },
    inputText: { textAlign: 'center', height: 40, width: '100%', borderWidth: 1,
     borderColor: 'midnightblue', marginBottom: 20 }
})
export default SetNativeClass

Now, in the iOS simulator, we can type in the text input created using setNativeProps.

setnativeprops in class

setNativeProps in Functional Component

Now, we'll look into an example of using setNativeProps in a functional component. So, create a new file called SetNativeHooks.js in the components folder. Now, add this file in App.js.

setnativeprops in functional component code

Then, in the file called SetNativeHooks.js, put the content listed below. We need to use the useRef() hook here to declare a ref called inputRef. After that, in a TextInput, we're calling the ref inputRef. Next, in onChangeText, we're using it with setNativeProps.


import { Text, TextInput, StyleSheet } from 'react-native'
import React, { useRef } from 'react'
const SetNativeHooks = () => {
    const inputRef = useRef(null)
    return (
        <>
            <Text style={styles.titleText}>setNativeProps in Hooks component</Text>
            <TextInput
                ref={inputRef}
                style={styles.inputText}
                placeholder="Enter your name"
                onChangeText={text => inputRef.current.setNativeProps({ text })}
            />
        </>
    )
}
const styles = StyleSheet.create({
    titleText: { fontSize: 22, fontWeight: 'bold', textAlign: 'center', paddingVertical: 20, color: '#2009' },
    inputText: { textAlign: 'center', height: 40, width: '100%', borderWidth: 1, borderColor: 'midnightblue'
     , marginBottom: 20 }
})
export default SetNativeHooks

Now, in the iOS simulator, we can type in the text input created using setNativeProps.

setnativeprops in functional component ios

App Testing

We'll use Jest to test our app. First, in the __tests__ folder, remove the earlier file called App-test.js. Then, add these four files in it: RefsClass-test.js, RefsHooks-test.js, SetNativeClass-test.js, and SetNativeHooks-test.js.

Now, in the RefsClass-test.js file, add the content below. We've written a simple snapshot test to test all the components in it. Here, we'll test whether they're rendering properly on a mobile device.


import React from 'react';
import renderer from 'react-test-renderer';
import RefsClass from '../components/RefsClass'; 
describe('<RefsClass />', () => {
    const tree = renderer.create(<RefsClass />).toJSON();
    it('RefsClass Component rendered', () => {
        expect(tree).toMatchSnapshot();
    });
});

Now, in the RefsHooks-test.js file, add the content below. We'll again do the same snapshot testing.


import React from 'react';
import renderer from 'react-test-renderer';
import RefsHooks from '../components/RefsHooks';
describe('<RefsHooks />', () => {
    const tree = renderer.create(<RefsHooks />).toJSON();
    it('RefsHooks Component rendered', () => {
        expect(tree).toMatchSnapshot();
    });
});

Similarly, in the SetNativeClass-test.js file, add the content below:


import React from 'react';
import renderer from 'react-test-renderer';
import SetNativeClass from '../components/SetNativeClass'; 
describe('<SetNativeClass />', () => {
    const tree = renderer.create(<SetNativeClass />).toJSON();
    it('SetNativeClass Component rendered', () => {
        expect(tree).toMatchSnapshot();
    });
});

Lastly, in the SetNativeHooks-test.js file, add this content:


import React from 'react';
import renderer from 'react-test-renderer';
import SetNativeHooks from '../components/SetNativeHooks'; 
describe('<SetNativeHooks />', () => {
    const tree = renderer.create(<SetNativeHooks />).toJSON();
    it('SetNativeHooks Component rendered', () => {
        expect(tree).toMatchSnapshot();
    });
});

To see the output of all test cases, run the npm run test from the terminal.

app testing

As the screenshot above shows, all of our test cases ran successfully. However, adding integration testing would be required to write more complicated test cases. To do this, we can use Waldo instead of writing all test cases manually.

Waldo is a no-code testing platform that's very easy to use. We just need to upload the APK or IPA file to do so. You can create a free Waldo account here to test its features.

react pull quote

Conclusion

In this post, we've learned about refs in React Native, including using this.refs in a class-based component. We also learned how to use ref through the useRef hook. Additionally, we looked at examples with setNativeProps, including with both class-based and hook-based functional components.

At last, we tested our app through Jest. We can also test our app with Waldo's no-coding platform.

Automated E2E tests for your mobile app

Waldo provides the best-in-class runtime for all your mobile testing needs.
Get true E2E testing in minutes, not months.

Reproduce, capture, and share bugs fast!

Waldo Sessions helps mobile teams reproduce bugs, while compiling detailed bug reports in real time.