A random image for effect.

Best Practices for Capturing User Feedback in React Native

Nothing beats getting real feedback from your users — but how should you go about capturing that feedback? In this tutorial we’ll discuss when you should prompt them, how you should prompt them, and what tools you can use to capture that feedback.

Spencer Carli
Mar 21, 2019 · 8 min read

This tutorial was originally published on React Native School. Give it a visit if you want to see 75+ React Native tutorials!

I’ve got a pet peeve — when apps ask for feedback/a review at bad times/in bad ways:

  • In the middle of an important action (like checking out)
  • They hijack my screen and don’t let me do anything unless I do their feedback thing

Good Times & Ways to Ask for Feedback

Before we get into the hands on part, lets just drop a few times/places you could ask for feedback and get good responses:

  1. When they take a similar action many times. Say your app gives them a random joke. Say they’ve pressed the “New Joke” button 15 times. That would be a good opportunity to ask why they aren’t rolling on the floor gasping for air from your quality jokes you jacked from the latest Netflix stand up special.
  2. When they’re vigorously shaking their phone out of frustration. Maybe ask if they’re having issues? I mean how often do you see people shaking their phone out of joy?

Navigation Setup for Capturing User Feedback

What’s this look like in practice? Let’s look at it.

The Example App

First we’ll set up our example app. It’s a revolutionary new app called Clargue that will, on demand, generate a random color for you. Here’s the code we’re starting with.

import React from 'react';
import { Text, View, Button, AsyncStorage } from 'react-native';
import randomColor from 'random-color';
import {
createStackNavigator,
createAppContainer,
createSwitchNavigator,
} from 'react-navigation';

class RandomColor extends React.Component {
state = {
backgroundColor: randomColor().hexString(),
};

newColor = () => {
this.setState(state => ({
backgroundColor: randomColor().hexString(),
}));
};

signOut = () => {
AsyncStorage.removeItem('isOnboarded');
this.props.navigation.navigate('Onboarding');
};

render() {
return (
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: this.state.backgroundColor,
}}
>
<View
style={{
backgroundColor: 'rgba(255, 255, 255, 0.5)',
borderRadius: 10,
paddingHorizontal: 10,
paddingVertical: 5,
}}
>
<Text
style={{
fontSize: 20,
color: '#000',
}}
>
{this.state.backgroundColor}
</Text>
</View>
<Button title="New Color" onPress={this.newColor} />
<Button title="Sign Out" onPress={this.signOut} />
</View>
);
}
}

class Entry extends React.Component {
componentDidMount() {
AsyncStorage.getItem('isOnboarded').then(isOnboarded => {
if (isOnboarded == 'true') {
this.props.navigation.navigate('Main');
} else {
this.props.navigation.navigate('Onboarding');
}
});
}

render() {
return null;
}
}

const Onboard1 = ({ navigation }) => (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{ fontSize: 30, marginBottom: 10 }}>Welcome to Clargue</Text>
<Text>The Biggest Innovation in</Text>
<Text>Random Color Generation</Text>
<Button
title="Learn More"
onPress={() => navigation.navigate('Onboard2')}
/>
</View>
);

const Onboard2 = ({ navigation }) => (
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 40,
}}
>
<Text style={{ marginBottom: 30 }}>
We use big data and machine learning to get money from investors...
</Text>
<Text>and a basic algorithm from Github to select your colors.</Text>
<Button
title="Get Started"
onPress={() => {
AsyncStorage.setItem('isOnboarded', 'true');
navigation.navigate('Main');
}}
/>
</View>
);

const Navigator = createSwitchNavigator({
Entry: {
screen: Entry,
},
Onboarding: createStackNavigator(
{
Onboard1: {
screen: Onboard1,
},
Onboard2: {
screen: Onboard2,
},
},
{
headerMode: 'none',
}
),
Main: {
screen: RandomColor,
},
});

export default createAppContainer(Navigator);

The Feedback Screen

You can present the request for feedback/bug report/review in a variety of ways. I think the easiest is going to be by adding a global modal screen that you can access and present from anywhere in your app. For our app I’ll do this by creating a new screen (which in a real situation would likely have a form of some sort) and then adding a stack navigator as our new root navigator.

// ...

const Navigator = createSwitchNavigator({
Entry: {
screen: Entry,
},
Onboarding: createStackNavigator(
{
Onboard1: {
screen: Onboard1,
},
Onboard2: {
screen: Onboard2,
},
},
{
headerMode: 'none',
}
),
Main: {
screen: RandomColor,
},
});

const FeedbackForm = () => (
<View style={{ flex: 1, justifyContent: 'center', paddingHorizontal: 40 }}>
<Text>
How are you liking Clargue? We'll use your feedback to file an issue on
the open source library - but don't worry! We'll say it's a priority
because our business depends on a fix!
</Text>
</View>
);

const Modals = createStackNavigator(
{
Navigator: {
screen: Navigator,
},
Feedback: {
screen: createStackNavigator({
FeedbackForm: {
screen: FeedbackForm,
navigationOptions: ({ navigation }) => ({
headerTitle: 'Feedback',
}),
},
}),
},
},
{
headerMode: 'none',
mode: 'modal',
}
);

export default createAppContainer(Modals);

Presenting Feedback Form after a Given Number of Actions

We’ll just cover one of the scenarios I outlined before: requesting feedback after a given number of actions.

// ...

class RandomColor extends React.Component {
state = {
backgroundColor: randomColor().hexString(),
clickCount: 0,
};

newColor = () => {
if (this.state.clickCount > 5) {
this.props.navigation.navigate('Feedback');
this.setState({ clickCount: 0 });
} else {
this.setState(state => ({
backgroundColor: randomColor().hexString(),
clickCount: state.clickCount + 1,
}));
}
};

// ...
}

// ...

Give the User a Way Out

Finally, don’t forget to give the user a clear way to get out of submitting feedback! Maybe that don’t have the time, don’t want to, or just don’t know WTF is going on. Make it clear to get out. Leave a good impression.

const Modals = createStackNavigator(
{
Navigator: {
screen: Navigator,
},
Feedback: {
screen: createStackNavigator({
FeedbackForm: {
screen: FeedbackForm,
navigationOptions: ({ navigation }) => ({
headerTitle: 'Feedback',
headerRight: (
<Button title="Close" onPress={() => navigation.pop()} />
),
}),
},
}),
},
},
{
headerMode: 'none',
mode: 'modal',
}
);

export default createAppContainer(Modals);

Making Life Easier: Outsourcing Feedback & Bug Report Capturing

Obviously this is just one part of capturing feedback and bug reports. You also need to setup something on the backend to capture that feedback and present it to developers/product owners. That’s more work for you and more code for you to maintain.

Links & Resources

Final Code

import React from 'react';
import { Text, View, Button, AsyncStorage } from 'react-native';
import randomColor from 'random-color';
import {
createStackNavigator,
createAppContainer,
createSwitchNavigator,
} from 'react-navigation';

class RandomColor extends React.Component {
state = {
backgroundColor: randomColor().hexString(),
clickCount: 0,
};

newColor = () => {
if (this.state.clickCount > 5) {
this.props.navigation.navigate('Feedback');
this.setState({ clickCount: 0 });
} else {
this.setState(state => ({
backgroundColor: randomColor().hexString(),
clickCount: state.clickCount + 1,
}));
}
};

signOut = () => {
AsyncStorage.removeItem('isOnboarded');
this.props.navigation.navigate('Onboarding');
};

render() {
return (
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: this.state.backgroundColor,
}}
>
<View
style={{
backgroundColor: 'rgba(255, 255, 255, 0.5)',
borderRadius: 10,
paddingHorizontal: 10,
paddingVertical: 5,
}}
>
<Text
style={{
fontSize: 20,
color: '#000',
}}
>
{this.state.backgroundColor}
</Text>
</View>
<Button title="New Color" onPress={this.newColor} />
<Button title="Sign Out" onPress={this.signOut} />
</View>
);
}
}

class Entry extends React.Component {
componentDidMount() {
AsyncStorage.getItem('isOnboarded').then(isOnboarded => {
if (isOnboarded == 'true') {
this.props.navigation.navigate('Main');
} else {
this.props.navigation.navigate('Onboarding');
}
});
}

render() {
return null;
}
}

const Onboard1 = ({ navigation }) => (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{ fontSize: 30, marginBottom: 10 }}>Welcome to Clargue</Text>
<Text>The Biggest Innovation in</Text>
<Text>Random Color Generation</Text>
<Button
title="Learn More"
onPress={() => navigation.navigate('Onboard2')}
/>
</View>
);

const Onboard2 = ({ navigation }) => (
<View
style={{
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 40,
}}
>
<Text style={{ marginBottom: 30 }}>
We use big data and machine learning to get money from investors...
</Text>
<Text>and a basic algorithm from Github to select your colors.</Text>
<Button
title="Get Started"
onPress={() => {
AsyncStorage.setItem('isOnboarded', 'true');
navigation.navigate('Main');
}}
/>
</View>
);

const Navigator = createSwitchNavigator({
Entry: {
screen: Entry,
},
Onboarding: createStackNavigator(
{
Onboard1: {
screen: Onboard1,
},
Onboard2: {
screen: Onboard2,
},
},
{
headerMode: 'none',
}
),
Main: {
screen: RandomColor,
},
});

const FeedbackForm = () => (
<View style={{ flex: 1, justifyContent: 'center', paddingHorizontal: 40 }}>
<Text>
How are you liking Clargue? We'll use your feedback to file an issue on
the open source library - but don't worry! We'll say it's a priority
because our business depends on a fix!
</Text>
</View>
);

const Modals = createStackNavigator(
{
Navigator: {
screen: Navigator,
},
Feedback: {
screen: createStackNavigator({
FeedbackForm: {
screen: FeedbackForm,
navigationOptions: ({ navigation }) => ({
headerTitle: 'Feedback',
headerRight: (
<Button title="Close" onPress={() => navigation.pop()} />
),
}),
},
}),
navigationOptions: {
gesturesEnabled: false,
},
},
},
{
headerMode: 'none',
mode: 'modal',
}
);

export default createAppContainer(Modals);

The React Native Log

All things React Native — tutorials, experiments, tips & tricks, snippets

Spencer Carli

Written by

Student. Teacher. Pizza fiend. I write about React Native, Meteor, and more. http://learn.handlebarlabs.com

The React Native Log

All things React Native — tutorials, experiments, tips & tricks, snippets

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade