JAM
JAM

Reputation: 770

React Native reuse function that sets state

I am new to React Native. In my app, the same function to check if user exists in the database is used in 3 different files.

checkUserExists = (userId) => {
        var that = this;
        database.ref('users').child(userId).once('value').then(function(result){
        const exists = (result.val() !== null);

        if (exists) {
            var data = result.val();
            that.setState({
                username: data.username,
                name: data.name
            })
        }
    })
}

To reduce amount of code, I want to put this function into a component so that it can be reused. So I created a new file with this function:

export function checkUserExists (userId) {
    database.ref('users').child(userId).once('value').then(function(result){
        const exists = (result.val() !== null);

        if (exists) {
            var data = result.val();
            setState({
                username: data.username,
                name: data.name
            })
        }
    })
}

But this is met with [Unhandled promise rejection: ReferenceError: Can't find variable: setState]

What is the proper way to handle this? Do I have to use Redux? I can't find a clear answer on SO so far.

Update: I also tried

export default class Authentication extends Component {
    checkUserExists = (userId) => {
        database.ref('users').child(userId).once('value').then(function(result){
            const exists = (result.val() !== null);

            if (exists) {
                var data = result.val();
                setState({
                    username: data.username,
                    name: data.name
                })
            }
        })
    }
}

and I tried to call it like

Authentication.checkUserExists(user.id);

but gets: TypeError: _authentication.default.checkUserExists is not a function

Upvotes: 3

Views: 578

Answers (2)

jmargolisvt
jmargolisvt

Reputation: 6088

You need to call this.setState from within a component. You can't just call if from anywhere.

Your helper function is not bound to your class. Notice in the original var that = this is set up so that that.setState will work from within the then block. It's likely that checkUserExists is bound using https://github.com/tc39/proposal-class-fields in your original example. Now you've got a regular (not bound to the class) function and it has no access to this nor any of its methods, including setState.

To be honest though, this is an XY problem, imho. If you want to extract a helper function, you should have it return the data you need, not attempt to pass around references to the class whose state you want to update. You should instead just return the data to the component --and you have to do this because your database call is async anyway-- and use that data within the component to call its own setState method.

https://reactjs.org/docs/react-component.html#setstate

Upvotes: 0

Shubham Khatri
Shubham Khatri

Reputation: 281874

Two things you can do to make it work

  1. Change the .then function to an arrow function or bind it
  2. Call the checkUserExists function where you execute it with the class context
 export function checkUserExists (userId) {
        database.ref('users').child(userId).once('value').then((result) => {
            const exists = (result.val() !== null);

            if (exists) {
                var data = result.val();
                this.setState({
                    username: data.username,
                    name: data.name
                })
            }
        })
    }

Now where you wanna use checkUserExists, you would use it with .call like

checkUserExists.call(this, userId);

One downside of this solution though is that it might be difficult to debug your app as the state is being set from code that is outside of the component

To overcome the downside you can define your method like

export async function checkUserExists (userId) {
      try {
        const result = await database.ref('users').child(userId).once('value');
        const exists = (result.val() !== null);
        if (exists) {
            return data;
        }
        return false;
      } catch(e) {
         console.log(e);

      }
  }

and in the component use it like

someFunc = async () => {
   const data = await checkUserExists(userId);
   if (data) {
        this.setState({
            username: data.username,
            name: data.name
        })
   }
}

Upvotes: 1

Related Questions