29 February 2024 (updated: 29 February 2024)
Chapters
Are you waiting for your interview and looking for possible questions? Or maybe just want to learn something new? Check our list and keep your ear to the ground.
It is an extension of JavaScript syntax with the ability to insert tags (HTML-like). It is used to define the UI elements for mobile applications in a more readable way.
JSX is transpiled into regular JavaScript function calls using a tool like Babel before being executed by the JavaScript engine.
You have to define a JavaScript or TypeScript function or class that returns JSX code representing the UI structure of the component.
Yes. You can take advantage of the state in both types of components. In class components, you can define state with this, and in functional components you have to use hook useState.
You cannot directly change the value of the state. You have to use special functions like this.setState (for class components) and useState hook (for functional components)
Feature | State | Props |
Communication | Internal (inside component) | From parent to child |
Mutable | Can be modified, (component can change value) | Cannot be modified (child component cannot modify value) |
Initialization | Initialized in component | Passed from the parent component |
Usage example | Internal data and UI state | External inputs |
Trigger re-render | Changes in state trigger re-render | Changes in props do not trigger re-render. When a parent component updates the values it passes as props, React will re-render the child component with the new props. This is not because the child component detected a change in props. React automatically re-renders a component when its props or state change. |
Component re-renders when:
Yes, we can. Is it good? Not really. It is called props drilling.
When props change, component rerender. So when you want to pass props to a nested component, all parents of this component will rerender.
For example, you have a cookie list and want to show if a cookie is your favorite and add cookie to your favorites. Here there are “only” two props to pass 3 levels down, but let’s imagine that there are many more props needed. When something changes you have to update all components and add missing props to them.
Passing props down the components tree - props drilling
Instead of doing that you can useContext to manage information needed by some independent components.
What to do when you have to pass values between components that are not in a direct relationship? To avoid populating props down the components tree (props drilling), you can use useContext hook. It allows you to define a mechanism for transmitting and consuming data between independent elements.
You can create a context for these components, for example, CookieContext. Then wrap the components in a context and pass to it the values that should be available for these components. Now you can get context values directly from child components. No need to pass them as props through every component, from parents to nested children.
Example from the question “Can we populate props down the components tree?” but with context usage
Here is a short example of context use:
UseEffect allows you to handle side effects like data download or creating subscriptions. By default, it is executed on the first render and each subsequent update of the component.
useEffect(() => {});
You can limit the number of calls by adding a dependency array.
useEffect(() => {}, [test]);
Now useEffect will run on the first render and whenever the test value changes. Leaving the dependency array empty will cause it to run useEffect only on the first render.
Each effect can return a function that specifies how to clean up after it. Thanks to this, we can first subscribe to notifications and then unsubscribe in nearly the same place.
You can use the useMemo hook. Memoization is a mechanism for components optimization. Thanks to this technique you can prevent your component from unnecessary re-renders or recalculations.
useMemo will allow you to recalculate value only when something in your dependency array changes, so it should not be recalculated during every render. The previous contents of the dependency array will be compared with the new contents. If changes are detected, the content of useMemo will be invoked. Remember that if a value (in a dependency array) is not a primitive (eg. is it object or array), the value comparison won’t work. In the example below we will recalculate cookies value only when cookies data changes.
The function is re-created with each render. You can avoid that by using the useCallback hook. It works similar to useMemo hook. Function returned by useCallback will only be recreated when something in your dependency array changes.
This is a state management library. It gives the possibility to manage the whole application state in a predictable way. There is a single source of truth for the state of the application and thanks to that it is much easier to share state between components. What’s more, the state is read-only, you can update it only by using actions. Redux works based on context.
Redux consists of several “elements”:
State management with redux
The lifecycle of react native components consists of several phases:
Class components | Functional components | |
Initialization | constructor(props) - Initializes the component | --- |
Mounting | componentWillMount() render() - renders UI componentDidMount() - invoked after a component is mounted, this is the place where you can fetch data, subscribe notifications, etc |
useEffect(() => {}, []) - when you put an empty dependencies array, useEffect will run only once on the initial render. This is equivalent to componentDidMount and componentDidUpdate |
Updating | componentWillReceiveProps() shouldComponentUpdate() - return info ic component should be updated or not componentWillUpdate() render() - renders updated UI componentDidUpdate() - invoked after component is updated |
It is also done by using the useEffect hook. By adding a dependency array to useEffect hook you can add logic that should be invoked when some of the dependencies change. By returning a function from useEffect you can albo do some cleanup |
Unmounting | componentWillUnmount() - invoked before the component is unmounted/destroyed. Perfect place for cleanup, for example removing subscriptions | The cleanup mechanism should be placed as a return from useEffect hook |
Class and functional component lifecycle
In older versions of React Native bridge was used to allow communication between JavaScript code and native code. To communicate with native functionality (e.g. access to a camera or GPS), React Native uses native modules. These native modules are implemented in native languages (Java, Objective-C, Swift) and provide a JavaScript API that React Native components can use. When a React Native component needs to communicate with native code or vice versa, a “bridge” is called. “Bridge” translates fragments of code written in JavaScript into Java for Android apps and Objective-C for iOS and enables the use of modules native to a given platform.
New architecture abandons the bridge in favor of a different communication: the JavaScript interface (JSI). The JSI interface gives us the possibility to hold references to C++ objects inside JavaScript objects.
With references, we can directly call methods on those objects.
The JSI interface idea
Fabric is a new rendering system connected with React Native new architecture (with JSI interface). The main purpose of this renderer is to unify more render logic in C++. The core is shared between platforms. It improves interoperability with host platforms (Android, iOS, macoOS, Windows) and makes it easier to adopt React Native on new platforms.
There is no solution added to react-native by default.
There is AsyncStorage, a persisted key-value storage system, but values that are stored there are unencrypted, so they are insecure. This is something like LocalStorage for web apps.
Fortunately, we can take advantage of solutions offered by each platform:
If you want to use solutions If you want to use the solutions above, you either have to connect to the appropriate native module yourself or use a library like react-native-keychain.
First what comes to mind is probably console.log… What’s more, you can use Flipper or any other tool. In Flipper, you can check logs from the app, inspect the layout, see network requests, and many more.
But how about using breakpoints? You can insert a breakpoint in a line of code. Then you just start your app in debug mode and it will stop in the line where you put your breakpoint. Now you can check your variables and debug your app step by step in a more comfortable way.
You can use the Fetch API that is available out-of-the-box in React Native. Another possibility is to use 3rd party libraries like Axios.
In both solutions, you make an asynchronous request to the api and then wait for a result. You can use async/await syntax or promises to handle responses.
It is a mechanism that allows you to pass a function as a prop of a component and this function is responsible for rendering some content of this component.
This is a really great solution when you want to give parent component flexibility in determining the UI structure of child components. Instead of passing a bunch of props, you pass a single function that returns a ReactNode. You can also pass some data to this rendering function.
Feature | Hot reloading | Live reloading |
What is does | Updates code in real-time without losing state. It injects or replaces only changed code | Reloads the whole app and reset state. |
Use case | Applying style changes, updating components etc. | Used when seeing the entire app with updated code is required |
State preservation | Aims to store current app state | Reset state, unsaved changes will be lost |
Where is used | Common in React Native | Common in web development (with tools like webpack) |
Key prop is used in arrays to give each element a unique identifier. It is a special attribute that helps React efficiently update the UI by recognizing which items have been added, removed or modified.
Key value should be unique within the scope of the component or list. Using list indices as keys is not a good solution, it can lead to unexpected behavior when the list changes. The best way is to use unique and stable identifiers for keys when rendering lists.
HOC is a High Order Component. This is a pattern that involves creating a function that takes a component and returns a new component with additional functionality or props. For example with the HOC concept, you can add a lifecycle logger to your components.
Yes, you can add native modules and get access to native functionalities from JavaScript code. Basically, you have to:
As stated in documentation Native Modules and Native Components are part of legacy architecture. They will be deprecated when New Architecture will be stable. Instead of current Native Modules, Turbo Native Modules will be used.
Depending on how big the differences are between the application's behavior on different platforms, there are 2 options.
Using Platform module:
Using platform-specific extensions:
Feature | React Native CLI | Expo |
Environment configuration | More complex setup Tools like Android Studio and Xcode are needed Dependencies like Node and Watchman are needed |
Nearly no setup Just create project and scan QR code |
Project configuration | More control Possibility to integrate native modules and dependencies Full access to all available native modules |
Less control Limited access to native modules |
Third-party libraries | Can use any third-party libraries that are compatible with React Native | Limited to libraries that are compatible with Expo's |
Build and deployment | Requires manual configuration and handling of builds | Simplified build and deployment process, managed by Expo servers |
Community | Large community and ecosystem | Has its own community and ecosystem |
Entry threshold | More initial setup and configurations are needed. It can be sometimes a bit difficult | Quick and easy setup |
Running application | Standalone apps or emulators/real devices are used for testing | You can scan QR code from terminal and run app on your device |