Search code examples
react-nativereact-functional-component

Why doesn't binding a prop work but using an arrow function does?


I want to create a login page where an error message is displayed when fields are empty. I use function components, state and props to keep the UI separated from the logic. So I have a UserSignInController which returns a UserSignInView which gets displayed on screen.

When I set a onPress callback for the login button, it calls onClickCheckLogin() in the controller. But setting the error message only works if I use an arrow function as a prop, if I use .bind(this) it doesn't.

This works UserSignInController.js:

import React, {useState} from 'react';
import { Linking, Alert } from 'react-native';
import UserSignInView from './UserSignInView';
import User from '../../User';

const renderSignInView = () =>
{
  const [errorMessage, setErrorMessage] = useState('');
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');

  return (
      <UserSignInView
          errorMessage = {errorMessage}
          setUsername = {setUsername}
          setPassword = {setPassword}
          //arrow function passing the state to onClickCheckLogin
          checkLogin = {() => { onClickCheckLogin(username, password, setErrorMessage)}}
          openPrivacyPolicy = {onClickOpenPrivacyPolicy}
      />
  )
};

const onClickCheckLogin = (username, password, setMessageFunction) =>
{
    if(!! username && !! password)
    {
      console.log("yeee");
    }else 
    {
      console.log('doooo');
      setMessageFunction('Username/password empty');
    } 
};

This does not work UserSignInController.js:

import React, {useState} from 'react';
import { Linking, Alert } from 'react-native';
import UserSignInView from './UserSignInView';
import User from '../../User';

const renderSignInView = () =>
{
  const [errorMessage, setErrorMessage] = useState('');
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');

  //bind function instead of arrow function
  onClickCheckLoginCallback = onClickCheckLogin.bind(this);

  return (
      <UserSignInView
          errorMessage = {errorMessage}
          setUsername = {setUsername}
          setPassword = {setPassword}
          //use the binded version
          checkLogin = {onClickCheckLoginCallback}
          openPrivacyPolicy = {onClickOpenPrivacyPolicy}
      />
  )
};

const onClickCheckLogin = () =>
{
    if(!! this.username && !! this.password)
    {
      console.log("yeee");
    }else 
    {
      console.log('doooo');
      this.setErrorMessage('Username/password empty');
    } 
};

With this I get an error TypeError: _this.setErrorMessage is not a function. (In '_this.setErrorMessage('Username/password empty')', '_this.setErrorMessage' is undefined)


Solution

  • I found the answer here. You can't access local properties of a function from the outside. That globalObject displayed with console.log is just the window object. So, binding with .bind() can't work. As an alternative, you may pass the setErrorMessage property as an argument, and also the username and password.

    I wrote on a separate answer to better distinguish from the first one