Background
After releasing React v16.8, now we have hooks to use in React Native.
I am doing some simple tests to see the render times and the performance between
Hooked functional components and class components. Here is my sample:
@Components/Button.js
import React, { memo } from 'react';
import { TouchableOpacity, Text } from 'react-native';
const Button = memo(({ title, onPress }) => {
console.log("Button render"); // check render times
return (
<TouchableOpacity onPress={onPress} disabled={disabled}>
<Text>{title}</Text>
</TouchableOpacity>
);
});
export default Button;
@Contexts/User.js
import React, { createContext, useState } from 'react';
import User from '@Models/User';
export const UserContext = createContext({});
export const UserContextProvider = ({ children }) => {
let [ user, setUser ] = useState(null);
const login = (loginUser) => {
if (loginUser instanceof User) { setUser(loginUser); }
};
const logout = () => {
setUser(null);
};
return (
<UserContext.Provider value={{value: user, login: login, logout: logout}}>
{children}
</UserContext.Provider>
);
};
export function withUserContext(Component) {
return function UserContextComponent(props) {
return (
<UserContext.Consumer>
{(contexts) => <Component {...props} {...contexts} />}
</UserContext.Consumer>
);
}
}
Cases
We have two cases below for constructing screen components:
@Screens/Login.js
Case 1: Functional Component with Hooks
import React, { memo, useContext, useState } from 'react';
import { View, Text } from 'react-native';
import Button from '@Components/Button';
import { UserContext } from '@Contexts/User';
const LoginScreen = memo(({ navigation }) => {
const appUser = useContext(UserContext);
const [foo, setFoo] = useState(false);
const userLogin = async () => {
let response = await fetch('blahblahblah');
if (response.is_success) {
appUser.login(user);
} else {
// fail on login, error handling
}
};
const toggleFoo = () => {
setFoo(!foo);
console.log("current foo", foo);
};
console.log("render Login Screen"); // check render times
return (
<View>
<Text>Login Screen</Text>
<Button onPress={userLogin} title="Login" />
<Button onPress={toggleFoo} title="Toggle Foo" />
</View>
);
});
export default LoginScreen;
Case 2: Component wrapped with HOC
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import Button from '@Components/Button';
import { withUserContext } from '@Contexts/User';
import UserService from '@Services/User';
class LoginScreen extends Component {
state = { foo: false };
userLogin = async () => {
let response = await UserService.login();
if (response.is_success) {
login(user); // function from UserContext
} else {
// fail on login, error handling
}
};
toggleFoo = () => {
const { foo } = this.state;
this.setState({ foo: !foo });
console.log("current foo", foo);
};
render() {
console.log("render Login Screen"); // check render times
return (
<View>
<Text>Login Screen</Text>
<Button onPress={userLogin} title="Login" />
<Button onPress={toggleDisable} title="Toggle" />
</View>
);
}
}
Results
Both cases have identical render times at the beginning:
render Login Screen
Button render
Button render
But while I press the "Toggle" button, the state changed and here is the result:
Case 1: Functional Component with Hooks
render Login Screen
Button render
Button render
Case 2: Component wrapped with HOC
render Login Screen
Questions
Although the Button Component isn't a large bunch of codes, considering the re-render times between two cases, Case 2 should have a better performance than Case 1.
However, considering the code readability, I definitely love using hooks more than using HOC. (Especially the function: appUser.login() and login())
So here's the question. Is there any solution that I can keep the benefits of both size, decreasing the re-render times while using the hooks? Thank you.