Merge branch 'main' into nextjs
# Conflicts: # __mocks__/data/mock-unicorn.ts # gatsby-config.js # gatsby-node.js # package-lock.json # package.json # src/components/layout/layout.tsx # src/components/post-card/post-card.tsx # src/components/seo.tsx # src/components/user-profile-pic/user-profile-pic.tsx # src/pages/about.tsx # src/templates/blog-post/post-metadata/post-metadata.tsx # src/templates/blog-profile/blog-profile.tsx # src/templates/post-list/post-list.tsx # src/types/PostInfoListDisplay.ts # src/types/RolesInfo.ts # src/types/UnicornInfo.ts
@@ -0,0 +1,281 @@
|
|||||||
|
---
|
||||||
|
{
|
||||||
|
title: "Angular Route Guards For Authorization In A Web And Mobile Application",
|
||||||
|
description: '',
|
||||||
|
published: '2018-07-13T22:12:03.284Z',
|
||||||
|
authors: ['crutchcorn'],
|
||||||
|
tags: ['angular'],
|
||||||
|
attached: [],
|
||||||
|
license: 'cc-by-4',
|
||||||
|
originalLink: "https://www.thepolyglotdeveloper.com/2018/07/angular-route-guards-authorization-web-mobile-application/"
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
You’re about to release your new Angular web app. It’s a photo sharing site and you want to test it, so you send a link to it to your hacker sister. She’s always messing with your stuff and she found out the URL to your admin page you added to your web app. Before you know it, she’s flushed your database using a button on that admin page that you didn’t restrict access to. Not a problem when using development data - but I’m sure your users wouldn’t be any too keen on a service where they lost all of their data. Let’s fix that
|
||||||
|
|
||||||
|
## Component Checks
|
||||||
|
|
||||||
|
The most basic way to restrict a user’s access to any given page is to use logic that will run at the load time of the component and redirect the user if needed. Given [Angular’s lifecycle hooks](https://angular.io/guide/lifecycle-hooks), we can use `ngOnInit` in order to do so.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import {Component, OnInit} from '@angular/core';
|
||||||
|
import {Router} from '@angular/router';
|
||||||
|
import {MyAuthService} from '../../core/auth/auth.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'super-secret-component',
|
||||||
|
templateUrl: '<comp-here></comp-here>',
|
||||||
|
styles: []
|
||||||
|
})
|
||||||
|
export class SecretComponent implements OnInit {
|
||||||
|
|
||||||
|
constructor(private myAuthService: MyAuthService, private router: Router) { }
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.myAuthService.checkAuth().subscribe(isAllowed => {
|
||||||
|
if (!isAllowed) {
|
||||||
|
this.router.navigate(['/']);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This code sample is pretty straightforward - on loading the component, let’s go ahead and check that the user is allowed to see the page, if not - let’s move them to somewhere they are allowed to see. You could even add a snackbar to let them know that they’re trying to access something they shouldn’t, maybe move them to a login page? It’s fairly customizable.
|
||||||
|
|
||||||
|
This works perfectly fine if there’s a single route you’d like to restrict users from being able to see, but perhaps you’d like to lockdown an entire module’s routes (or just a route with child routes) or there are many different routes you’d like to protect in a similar way. Of course, you could always copy and paste the code we’ve made before, but Angular actually provides a much easier, cleaner way of doing so.
|
||||||
|
|
||||||
|
## Introducing: Route Guards
|
||||||
|
|
||||||
|
In essence, a route guard is simply a check to tell if you’re allowed to view a page or not. It can be added to any route using `canActivate` (a fairly verbose property, I’d say) with a custom interface that follows [Angular’s CanActivate API](https://angular.io/api/router/CanActivate). The most simplistic example of a router guard is as follows:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// route.guard.ts
|
||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot} from '@angular/router';
|
||||||
|
import {Observable} from 'rxjs';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RouteGuard implements CanActivate {
|
||||||
|
|
||||||
|
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// route-routes.module.ts
|
||||||
|
import {NgModule} from '@angular/core';
|
||||||
|
import {Routes, RouterModule} from '@angular/router';
|
||||||
|
import {RouteComponent} from './posts.component';
|
||||||
|
import {RouteGuard} from '../core/route.guard';
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
pathMatch: 'full',
|
||||||
|
component: RouteComponent,
|
||||||
|
canActivate: [RouteGuard]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const routeRoutedComponents = [
|
||||||
|
RouteComponent
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [RouterModule.forChild(routes)],
|
||||||
|
exports: [RouterModule]
|
||||||
|
})
|
||||||
|
export class RouteRoutingModule { }
|
||||||
|
```
|
||||||
|
|
||||||
|
As you can see from the typing of `canActivate`, Angular is fairly lenient with what you need to return in order to let a user to access the page or not - it accepts a `Promise` or `Observable` of a `boolean` or even just a `boolean` itself as a return value. This guard has limited value currently, because it always returns `true` regardless of any parameters or changes. However, if we replace the `canActivate` method with something a little more useful, we can easily add back the functionality our old `ngOnInit` had:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
|
||||||
|
return this.myAuthService.checkAuth();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Tada! We suddenly have the same logic as before! We can now remove the `ngOnInit` from the previously added route, and keep things just as secure as before! Because we can return an `Observable`, we can even use `Observable` `pipes` like so:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
return this.myAuthService.checkAuth().pipe(tap(allowed => {
|
||||||
|
if (allowed) {
|
||||||
|
this.snackBar.open("Welcome back!");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
Of course, it might not be the best bet to add this logic in a guard, but it’s still representative of what you’re capable of doing inside of a guard.
|
||||||
|
|
||||||
|
## Children Guarding
|
||||||
|
|
||||||
|
When I first learned about this, I thought it was the coolest thing in the world. I started adding it to all of my routes. Next thing I knew, I was adding it to all my routes I wanted protected in some form or another.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
[
|
||||||
|
{ path: '', pathMatch: 'full', component: RouteComponent, canActivate: [RouteGuard] },
|
||||||
|
{ path: 'list', component: RouteComponent, canActivate: [RouteGuard] },
|
||||||
|
{ path: 'detail/:id', component: RouteComponent, canActivate: [RouteGuard] }
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
This isn’t too bad alone - but when you have hundreds of routes on a large scale project, this easily becomes unmanageable. I also had times when I wanted to add additional security to a route’s children, for example a dashboard page that included some admin routes that I wanted to lock down. This is where child guards come into play.
|
||||||
|
|
||||||
|
Child guards do exactly what you think they would. They add an additional guard for children. They use a similar API as `canActivate`, and the reference to that API can be found [here](https://angular.io/api/router/CanActivateChild). So, if I were to add the following guard to my child routes:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {ActivatedRouteSnapshot, RouterStateSnapshot, CanActivateChild} from '@angular/router';
|
||||||
|
import {Observable} from 'rxjs';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ChildGuard implements CanActivateChild {
|
||||||
|
canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
|
||||||
|
console.log('This child was activated!');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
It would console log ‘This child was activated’ every time you accessed a child route. How do you apply this to your routes?
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
[
|
||||||
|
{ path: '', canActivateChild: [ChildGuard], children: [
|
||||||
|
{ path: '', pathMatch: 'full', component: RouteComponent, canActivate: [RouteGuard] },
|
||||||
|
{ path: 'list', component: RouteComponent, canActivate: [RouteGuard] },
|
||||||
|
{ path: 'detail/:id', component: RouteComponent, canActivate: [RouteGuard] }
|
||||||
|
]}
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
However, I’m sure you’re wondering what happens if you apply a `canActivate` alongside a `canActivateChild`. If you were to change the code so the `canActivate` runs a `console.log('This is the canActivate!')` and your routes were to look like this:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
[
|
||||||
|
{ path: '', canActivate: [ActivateGuard], canActivateChild: [ChildGuard], children: [
|
||||||
|
{ path: '', pathMatch: 'full', component: RouteComponent },
|
||||||
|
{ path: 'list', component: RouteComponent },
|
||||||
|
{ path: 'detail/:id', component: RouteComponent }
|
||||||
|
]}
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
And accessed the `list` route, your console would output ‘This is the canActivate!’ and then ‘This child was activated!’. Of course, this has limited application when making an empty route without a component to load (that’s not a child), but it’s massively helpful when you have a component in the parent route such as this:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
[
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
canActivate: [AuthenticationGuard],
|
||||||
|
canActivateChild: [AuthorizationGuard],
|
||||||
|
children: [
|
||||||
|
{ path: 'admin', component: AdminComponent },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// NOT SHOWN: AuthenticationGuard and AuthorizationGuard. Just pretend they're code that checks what you think they would, based on the names (remember, authentication is if the user is who they say they are [AKA logged in]; authorization is making sure they have the right access [AKA if they're admin])
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, when you access the `''` path, you’ll make sure the user is authenticated, but doesn’t care about authorization. However, when you access a child of that path (in this example, `'admin'`), it will check both authentication AND authorization.
|
||||||
|
|
||||||
|
### Route Data
|
||||||
|
|
||||||
|
But let’s say that I wanted to be able to change my child route based on information that I’ve stored about that particular route. For example, I typically layout my breadcrumbs by using a `data` property on my routes like such:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
[
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
canActivateChild: [ChildGuard],
|
||||||
|
component: RouteComponent,
|
||||||
|
data: {
|
||||||
|
title: 'Main Page'
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{ path: 'list', component: RouteComponent, data: {title: 'List Page'} },
|
||||||
|
{ path: 'detail/:id', component: RouteComponent, data: {title: 'Detail Page'} }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
If I wanted to be able to add a welcome message for each page that printed their `title` on every route you accessed, you could add that logic to a `ChildGuard` logic.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { // The return type has been simplified
|
||||||
|
console.log(childRoute.data.title);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Because the first argument to `canActivateChild` is an `ActivatedRouteSnapshot`, you can grab [any of the methods or properties from the API](https://angular.io/api/router/ActivatedRouteSnapshot) from the routes that are currently being called. However, something you’ll probably want to keep in mind is that this will occur once for every single child route being called.
|
||||||
|
|
||||||
|
## Lazy Loading
|
||||||
|
|
||||||
|
Because lazy loading using `loadChildren` is still considered a child route, all of the same rules from [Children Guarding](https://www.thepolyglotdeveloper.com/2018/07/angular-route-guards-authorization-web-mobile-application/#ChildrenGuarding) still apply. However, there are more tricks that are available for lazy loaded routes that are not otherwise available.
|
||||||
|
|
||||||
|
### Can Load
|
||||||
|
|
||||||
|
The [API for canLoad](https://angular.io/api/router/CanLoad) looks very similar to what we’ve seen before with `canActivate` and `canActivateChild`.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {Route, CanLoad} from '@angular/router';
|
||||||
|
import {Observable} from 'rxjs';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class LoadGuard implements CanLoad {
|
||||||
|
|
||||||
|
canLoad(route: Route): Observable<boolean> | Promise<boolean> | boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Much like before, if we were to add it on a lazy loaded route:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
[
|
||||||
|
{
|
||||||
|
canLoad: [LoadGuard],
|
||||||
|
path: '',
|
||||||
|
loadChildren: './feature.module#FeatureModule'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
It would prevent the route from loading if the return was `false`. However, the bigger difference between, say, `canActivateChild`, is how it interacts with the other methods shown here.
|
||||||
|
|
||||||
|
Let’s say we have some routes shown like this:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
[
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
canActivate: [AuthenticationGuard],
|
||||||
|
canActivateChild: [AuthorizationGuard],
|
||||||
|
children: [{
|
||||||
|
canLoad: [LoadGuard],
|
||||||
|
path: 'feature',
|
||||||
|
loadChildren: './feature.module#FeatureModule'
|
||||||
|
}, {
|
||||||
|
path: 'otherfeature',
|
||||||
|
component: RouteComponent
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, if you access the `''` route, only the `AuthenticationGuard` would be called. Meanwhile, if you accessed the `'otherfeature'` route, you would load the `AuthenticationGuard`, THEN call the `AuthorizationGuard`. What order would you think the guards would load for the `'feature'` route? The answer might be a little more tricky than you expect.
|
||||||
|
|
||||||
|
The answer? `canLoad` runs first. Before `AuthenticationGuard` and before `AuthorizationGuard`. It also, unlike the other two, prevents the entire loading of the route. The advantage here is that you can stop the loading of a lazy-loaded route before doing any checks you’d want to run to prevent. This would increase performance greatly in situations where you’d block the loading of a page and it would be much more secure. After `canLoad` runs, then the other two run in order as they would before
|
||||||
|
|
||||||
|
## Wrap Up
|
||||||
|
|
||||||
|
Just like anything else, an Angular Router Guard is a tool. It has many uses that are really only restricted by how you’re able to utilize that tool. You’re able to do service calls, logic changes, and more in order to restrict access to a page. However, it’s not a one-tool-fits-all solution. There will be times that a [resolver](https://angular.io/api/router/Resolve) might be able to help better, or sometimes even component logic might fit your use-case better. That being said, Guards are incredibly helpful when the time comes to use them
|
||||||
BIN
content/blog/change-host-file-android-emulator/1.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
content/blog/change-host-file-android-emulator/10.png
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
content/blog/change-host-file-android-emulator/11.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
content/blog/change-host-file-android-emulator/2.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
content/blog/change-host-file-android-emulator/3.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
content/blog/change-host-file-android-emulator/4.png
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
content/blog/change-host-file-android-emulator/5.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
content/blog/change-host-file-android-emulator/6.png
Normal file
|
After Width: | Height: | Size: 262 KiB |
BIN
content/blog/change-host-file-android-emulator/7.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
content/blog/change-host-file-android-emulator/8.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
content/blog/change-host-file-android-emulator/9.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
133
content/blog/change-host-file-android-emulator/index.md
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
---
|
||||||
|
{
|
||||||
|
title: "Change the Host File of an Android Emulator",
|
||||||
|
description: "",
|
||||||
|
published: '2019-12-27T22:12:03.284Z',
|
||||||
|
authors: ['crutchcorn'],
|
||||||
|
tags: ['android'],
|
||||||
|
attached: [],
|
||||||
|
license: 'cc-by-4',
|
||||||
|
originalLink: "https://www.thepolyglotdeveloper.com/2019/12/change-host-file-android-emulator/"
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
While working on a bug in one of my projects recently, I found an issue that I could only recreate on an Android device. However, due to some cross-origin resource sharing (CORS) issues on my server, I had to serve my development environment from a changed hostfile that had a specific subdomain of my project.
|
||||||
|
|
||||||
|
With the ability to use a remote Chrome debugger from your desktop to a mobile device, you can use an emulator and still have your full Chrome debugging capabilities. The only problem then, is how to get the host file to match your desktop environment. Following these steps will allow you to do just that!
|
||||||
|
|
||||||
|
## Pre-Requisites
|
||||||
|
|
||||||
|
In order to do this, you’ll need to install a few things first:
|
||||||
|
|
||||||
|
- [Download and install Android Studio](https://developer.android.com/studio/install)
|
||||||
|
|
||||||
|
During installation, it will ask you if you want to setup an emulator. You’ll want to install all of the related Intel Virtualization packages, as it will greatly increase your speed of the emulator.
|
||||||
|
|
||||||
|
- Download and install the android-platform-tools. This will include
|
||||||
|
|
||||||
|
```
|
||||||
|
adb
|
||||||
|
```
|
||||||
|
|
||||||
|
command directly on your path for you to utilize
|
||||||
|
|
||||||
|
- For macOS, I suggest using [the Homebrew package manager](https://brew.sh/) and running `brew cask install android-platform-tools`
|
||||||
|
- For Windows, I suggest using [the Chocolatey package manager](https://chocolatey.org/) and running `choco install adb`
|
||||||
|
- For Linux, you’ll want to check with your package manager for one of the two above-mentioned package names
|
||||||
|
|
||||||
|
## Setup Steps
|
||||||
|
|
||||||
|
Once you have Android Studio installed, we’ll need to setup an emulator. To do so, open the application:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Then, press “Configure” in the bottom right corner. Then press the “AVD Manager” in the sub-menu.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
You’ll see a popup window that will show you the list of virtual devices. *These are devices that will be used in order to run an emulator*. You may already have a virtual device setup from the initial setup of Android Studio. They include the version of the operating system you use when you boot up the device. While the virtual device that was setup out-of-the-box is fine for most operations, we’ll want to setup an older version of the emulator. This will allow us to change the host file in Android, which requires root (something the default images won’t allow).
|
||||||
|
|
||||||
|
Select **Create Virtual Device**, then select a device type. In my example, I selected **Nexus 5**, but any device definition of a relatively modern phone should work.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
As mentioned before, the default images that are provided will not allow us to replace the host files. In order to do so, *we have to download an older Android image* (and one that does not include Google Play Store). To do this, I selected the **x86_64 Android 7.1.1** (non Google API version) image to download and then selected **Next**.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
It’s worth noting that we specifically must select a non-Google version, otherwise our future commands will not work (per Google’s restrictions on Google API images).
|
||||||
|
|
||||||
|
After this step, proceed to name the Android Device. *I’d suggest you name it something without any spaces in order to run a command later that you’ll need to run*. In this case, I called the image **Nexus5**.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Handling the Emulator
|
||||||
|
|
||||||
|
Once the AVD is initially setup, open your terminal, and find your installation path of Android Studio.
|
||||||
|
|
||||||
|
- For MacOS, this should be under **~/Library/Android/sdk**
|
||||||
|
- For Windows, this *should* be **C:\Users<username>\AppData\Local\Android\sdk**
|
||||||
|
|
||||||
|
Once in that path, you want to run a specific emulator command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./emulator/emulator -writable-system -netdelay none -netspeed full -avd <AVDName>
|
||||||
|
```
|
||||||
|
|
||||||
|
For example, given that I’m on macOS and my AVD name is **Nexus5**, I ran:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
~/Library/Android/sdk/emulator/emulator -writable-system -netdelay none -netspeed full -avd Nexus5
|
||||||
|
```
|
||||||
|
|
||||||
|
This will start the emulator under specific pretenses. These pretenses will allow you to write to any file on your OS, including the host file.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Once you’re done with running the emulator, open a new tab and run the following commands (in a folder you want to have the host file within):
|
||||||
|
|
||||||
|
- `adb root`
|
||||||
|
- `adb remount`
|
||||||
|
- `adb pull /system/etc/hosts`
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Upon running these commands, you’ll find a **hosts** file. *This file is the file that tells your OS what path a given domain has.* You can, for example, map `example.com` to go to a specific IP address, similar to how DNS works for most domains.
|
||||||
|
|
||||||
|
Inside the emulator, the IP address `10.0.2.2` refers to the *host* OS. For example, if you’re running a local server on your Windows/MacOS/Linux machine on `localhost:3000`, you can access it using `10.0.2.2:3000` from the Android emulator.
|
||||||
|
|
||||||
|
Knowing these two things, you can change the host file to make `example.com` refer to the host by adding the following to the host file:
|
||||||
|
|
||||||
|
```plaintext
|
||||||
|
10.0.2.2 example.com
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Pushing the Changes
|
||||||
|
|
||||||
|
Once you’ve made the changes to the host file that you want to have changed, you’ll have to push the host file to the OS of the AVD:
|
||||||
|
|
||||||
|
- `adb push ./hosts /etc/hosts`
|
||||||
|
- `adb push ./hosts /etc/system/hosts`
|
||||||
|
- `adb push ./hosts /system/etc/hosts`
|
||||||
|
|
||||||
|
While only one of these host file locations is needed to replaced, it’s easier to run all three than only run one to see if it worked
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
In order for the changes to the host file to take effect, you’ll have to restart the emulator. In order to do so, you’ll want to press and hold the power button off to the right of the emulator. Then, press “Restart”.
|
||||||
|
|
||||||
|
If you close the emulator and re-open it using the command above, it may work, but I’ve found instances where it seems to reset the host file, making you go through the whole process again
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Seeing the Results
|
||||||
|
|
||||||
|
Finally, you’re able to [sideload the Chrome APK’s x86 variant](https://www.apkmirror.com/apk/google-inc/chrome) in order to load the example codebase.
|
||||||
|
|
||||||
|
So, for example, using the above hostfile as an example, we can visit `example.com:8000` when running the development mode for [the project’s GitHub code](https://github.com/unicorn-utterances/unicorn-utterances/), you’re able to see a preview of the [Unicorn Utterances](https://unicorn-utterances.com) website from a different domain.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
And that’s it! You now know how to modify and update the host file for an emulated Android device. I hope this has helped with your development usage!
|
||||||
89
content/blog/how-to-upgrade-to-react-18/index.md
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
---
|
||||||
|
{
|
||||||
|
title: "How to Upgrade to React 18",
|
||||||
|
description: "",
|
||||||
|
published: '2022-01-07T22:12:03.284Z',
|
||||||
|
authors: ['crutchcorn'],
|
||||||
|
tags: ['react', 'webdev'],
|
||||||
|
attached: [],
|
||||||
|
license: 'coderpad',
|
||||||
|
originalLink: 'https://coderpad.io/blog/how-to-upgrade-to-react-18/'
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
React 18 is the latest in a long line of major releases of React. With it you gain access to: [new features for Suspense](https://reactjs.org/docs/concurrent-mode-suspense.html), new [useId](https://github.com/reactwg/react-18/discussions/111), [useSyncExternalStore](https://github.com/reactwg/react-18/discussions/86), and [useDeferredValue](https://github.com/reactwg/react-18/discussions/100) hooks, as well as the new [startTransition](https://github.com/reactwg/react-18/discussions/100) API.
|
||||||
|
|
||||||
|
While React 18 is not yet a stable release, testing out your application can be useful.
|
||||||
|
|
||||||
|
Like with previous major releases of React, React 18 is a fairly easy migration for most apps.
|
||||||
|
|
||||||
|
While [Strict Mode has received some changes](https://github.com/reactwg/react-18/discussions/19) that may impact your app, and [automatic batching](https://github.com/reactwg/react-18/discussions/21) may introduce some new edge cases, they only impact apps that don’t [follow the Rules of React properly](https://reactjs.org/docs/hooks-rules.html).
|
||||||
|
|
||||||
|
|
||||||
|
Outside of those considerations, let’s upgrade!
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
First, start by installing React 18:
|
||||||
|
|
||||||
|
```
|
||||||
|
npm i react@18.0.0-rc.0 react-dom@18.0.0-rc.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Or, if you use `yarn`:
|
||||||
|
|
||||||
|
```
|
||||||
|
yarn add react@18.0.0-rc.0 react-dom@18.0.0-rc.0
|
||||||
|
```
|
||||||
|
|
||||||
|
If you’re using Create React App, you may also want to [upgrade to the newest v5](https://github.com/facebook/create-react-app/releases/tag/v5.0.0) as well using:
|
||||||
|
|
||||||
|
```
|
||||||
|
npm i react-scripts@5
|
||||||
|
```
|
||||||
|
|
||||||
|
Or
|
||||||
|
|
||||||
|
```
|
||||||
|
yarn add react-scripts@5
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, make sure to upgrade any dependencies that might rely on React.
|
||||||
|
|
||||||
|
For example, upgrade [React Redux to v8](https://github.com/reduxjs/react-redux/releases/tag/v8.0.0-beta.2) or [SWR to 1.1.0](https://github.com/vercel/swr/releases/tag/1.1.0)
|
||||||
|
|
||||||
|
## Update `render` method
|
||||||
|
|
||||||
|
After you install React 18, you may receive an error when your app is running:
|
||||||
|
|
||||||
|
> Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more:[ https://reactjs.org/link/switch-to-createroot](https://reactjs.org/link/switch-to-createroot)
|
||||||
|
|
||||||
|
This is because previously, in React 17 and before, you’d have a file - usually called `index.js` or `index.ts` - that included the following code:
|
||||||
|
|
||||||
|
```
|
||||||
|
const rootElement = document.getElementById("root");
|
||||||
|
ReactDOM.render(<App />, rootElement);
|
||||||
|
```
|
||||||
|
|
||||||
|
While this code will continue to function for this release, it will not allow you to leverage most of the new features of React 18. Further, it’ll be removed in a future release of React.
|
||||||
|
|
||||||
|
To fix this issue, replace this code with the following:
|
||||||
|
|
||||||
|
```
|
||||||
|
const rootElement = document.getElementById("root");
|
||||||
|
ReactDOM.createRoot(rootElement).render(
|
||||||
|
<App />
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
When finished, you should be able to verify the version of React you’re using with `{React.version}`
|
||||||
|
|
||||||
|
<iframe src="https://app.coderpad.io/sandbox?question_id=200107" width="640" height="480" loading="lazy"></iframe>
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
As promised, the update to React 18 is fairly straightforward! Most applications should be able to upgrade without too many problems.
|
||||||
|
|
||||||
|
If you run into issues during your migration and you’re using `StrictMode`, try temporarily removing it to see if you run into any issues. [React 18 introduced some changes that may impact some apps.](https://github.com/reactwg/react-18/discussions/19)
|
||||||
|
|
||||||
|
We hope you enjoy the new [React concurrent features](https://github.com/reactwg/react-18/discussions/4) and happy hacking!
|
||||||