Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions challenges/data/01.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ I think you may have a question at this point.

## 👾 Before we start the exercise

- [ ] Google Chrome needs to be installed on your machine.
- [ ] Google Chrome (or Microsoft Edge) needs to be installed on your machine: React Native DevTools runs on top of it.

## 👨‍🚀 Exercise 01

Debugging is a crucial aspect of the development workflow in React Native. To learn more about debugging in React Native, refer to the [Debugging section of the React Native documentation](https://reactnative.dev/docs/debugging).

Like websites, you can use Chrome DevTools to debug the `JavaScript` code of your app. You can launch it with `j` in the terminal.
Like websites, you can use [React Native DevTools](https://reactnative.dev/docs/react-native-devtools) —based on Chrome DevTools— to debug the `JavaScript` code of your app. You can launch it with `j` in the terminal.

```bash
› Press j │ open debugger
Expand All @@ -41,17 +41,21 @@ Try to use them both in your app. `debugger` is super useful to inspect the stat

### Debugging React with React Developer Tools

The React Developer Tools extension for Chrome and Firefox lets you inspect a React component tree with your browser’s developer tools.
React Developer Tools are built into React Native DevTools. Open the **⚛️ Components** tab to inspect your React component tree, and the **⚛️ Profiler** tab to record renders.

![javascript source using Hermes](https://raw.githubusercontent.com/flexbox/react-native-workshop/main/challenges/data/react-dev-tools.gif)

- [ ] Use `Shift + m` to "Start React devtools"
- [ ] Press `j` in the terminal and open the **⚛️ Components** tab.
- [ ] Inspect your React component tree and change the Text from your button.

**🔭 Hint:** `Shift + m` opens the list of [Expo dev tools plugins](https://docs.expo.dev/debugging/devtools-plugins/) installed in your project (React Navigation, TanStack Query…).

### Testing your app with bad network

> ⚠️ The Expo debugger **cannot** throttle or simulate a slow network connection — the old React Native Debugger "Network" throttling is gone.

You can still inspect every request (headers, payload, timing) in the **Network** panel of React Native DevTools.

To test offline / poor connectivity, toggle **Airplane mode** on your device or simulator, or use your OS network link conditioner.

### Expo debugger shortcuts
Expand All @@ -61,6 +65,7 @@ To test offline / poor connectivity, toggle **Airplane mode** on your device or
| Open debugger | `j` |
| Reload the app | `r` |
| Toggle dev menu | `m` |
| Dev tools plugins | `Shift + m` |

## 👽 Bonus

Expand Down
20 changes: 14 additions & 6 deletions challenges/data/02.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ We are going to [use `TanStack Query`, here is a video explaining why](https://w
Here's how it works:

1. Write your data fetching functions (_here it's `getDataFromApi`_).
1. Wrap them in a named query with `queryKey` as an array of `string` (_here it's `["dataFromApi"]`_).
1. Wrap them in a named query with `queryKey` as an array of `string` (_here it's `["dataFromApi"]`_) and `queryFn`.
1. `react-query` handles caching, deduping, re-fetching, and loading states.

```javascript
Expand All @@ -37,6 +37,9 @@ Here's how it works:
async function getDataFromApi() {
try {
const response = await fetch(`https://api.example.com/collection/`);
if (!response.ok) {
throw new Error(`HTTP error ${response.status}`);
}
const json = await response.json();
return json;
} catch (error) {
Expand All @@ -48,9 +51,12 @@ async function getDataFromApi() {
* Renders a component that fetches data from an API and displays it.
*/
function Component() {
const { isLoading, isError, data } = useQuery(["dataFromApi"], getDataFromApi);
const { isPending, isError, data } = useQuery({
queryKey: ["dataFromApi"],
queryFn: getDataFromApi,
});

if (isLoading) {
if (isPending) {
return <Text>Loading…</Text>;
}
if (isError) {
Expand All @@ -61,6 +67,8 @@ function Component() {
}
```

**🔭 Hint:** We are using TanStack Query v5: `useQuery` takes a **single object** with `queryKey` and `queryFn`, and `isPending` is `true` until the first data arrives.

## 👨‍🚀 Exercise 2

That's the screen we will create: a list of data
Expand All @@ -72,7 +80,7 @@ That's the screen we will create: a list of data
- [ ] Install the library

```console
npm install @tanstack/react-query
npx expo install @tanstack/react-query
```

- [ ] Add a `QueryClientProvider` and wrap the application entry point.
Expand Down Expand Up @@ -130,7 +138,7 @@ async function fetchStarshipsData() {
}

export function useStarships() {
return useQuery({ queryKey: ["starships"], queryFn: fetchStarshipsData} );
return useQuery({ queryKey: ["starships"], queryFn: fetchStarshipsData });
}
```

Expand All @@ -153,4 +161,4 @@ If you want to display some images on your `Card`, you can use this placeholder

- [ ] Add [`expo-dev-tools`](https://docs.expo.dev/debugging/devtools-plugins/#expo-dev-tools-plugins) to your project
![expo-dev-tools example](https://raw.githubusercontent.com/flexbox/react-native-workshop/main/challenges/data/expo-dev-tools.gif)
- [ ] Add [`@dev-plugins/react-query`](https://docs.expo.dev/debugging/devtools-plugins/#react-query)
- [ ] Add [`@dev-plugins/react-query`](https://docs.expo.dev/debugging/devtools-plugins/#react-query) to inspect your queries and the cache.
143 changes: 71 additions & 72 deletions challenges/ecosystem/02.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
- [ ] ❗ Commit your work if it's not already done.
- [ ] Push your code to GitHub.

You are going to create a `eslint.config.js` file, here is a [preview of the one used on the spacecraft codebase](https://raw.githubusercontent.com/flexbox/react-native-bootcamp/main/hackathon/spacecraft/.eslintrc.js). We will create more or less the same with small steps.
You are going to create a `eslint.config.js` file (ESLint 9 "flat config"), here is a [preview of the one used on the spacecraft codebase](https://raw.githubusercontent.com/flexbox/react-native-bootcamp/main/hackathon/spacecraft/eslint.config.js). We will create more or less the same with small steps.

## 👨‍🚀 Exercise 2

Expand Down Expand Up @@ -69,24 +69,26 @@ You will have a `eslint.config.js` file created at the root of your project.

"scripts": {
...
"lint": "eslint --ignore-path .gitignore --ext .js,.ts,.tsx ."
"lint": "eslint ."
...
}
```

Now you can run the `lint` script to teel if you have errors
**🔭 Hint:** Files to lint and files to ignore are declared directly in `eslint.config.js`.

Now you can run the `lint` script to tell if you have errors

```console
npm run lint
# or
yarn lint
```

Running lint will show you errors in your code, here is an example:
Running lint will show you errors in your code, here is an example:

![yarn lint](https://raw.githubusercontent.com/flexbox/react-native-workshop/main/challenges/ecosystem/eslint-cli.gif)

You don't need to manullay fix your errors. We can run a script for that with:
You don't need to manually fix your errors. We can run a script for that with:

```console
npm run lint -- --fix
Expand All @@ -110,81 +112,84 @@ npm install --save-dev prettier eslint-config-prettier eslint-plugin-prettier
yarn add --dev prettier eslint-config-prettier eslint-plugin-prettier
```

- [ ] Update your `eslint.config.js` file to add the following rules:
- [ ] Update your `eslint.config.js` file to add the Prettier recommended config:

```javascript
// eslint.config.js

const { defineConfig } = require("eslint/config");
const expoConfig = require("eslint-config-expo/flat");
const prettier = require("eslint-plugin-prettier");
const prettierConfig = require("eslint-config-prettier");
const prettierRecommended = require("eslint-plugin-prettier/recommended");
const { defineConfig } = require("eslint/config");

module.exports = defineConfig([
expoConfig,
prettierConfig,
{
plugins: {
prettier,
},
rules: {
"prettier/prettier": "warn",
},
ignores: ["node_modules/**", ".expo/**", "dist/**", "ios/**", "android/**"],
},
expoConfig,
prettierRecommended, // enables `eslint-plugin-prettier` + `eslint-config-prettier`
]);
```

### ESLint and prettier rules for React Native

We are making progress, but we are not done yet. We need to add rules for React Native. In the meantime, let's install some plugins to format our code with prettier and sort imports automatically.
We are making progress, but we are not done yet. We need to add rules for TypeScript and React.

- [ ] Install the following plugins

```console
npm install --save-dev eslint-plugin-react @typescript-eslint/parser
npm install --save-dev @eslint/js@^9 @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-plugin-react globals
# or
yarn add --dev eslint-plugin-react @typescript-eslint/parser
yarn add --dev @eslint/js@^9 @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-plugin-react globals
```

**🔭 Hint:** Keep `@eslint/js` on the same major version as `eslint` (v9 here). The latest `@eslint/js` targets ESLint 10 and npm will fail with an `ERESOLVE` error.

- [ ] Update your `eslint.config.js` file to add the following rules:

```javascript
// eslint.config.js

const { defineConfig } = require("eslint/config");
const js = require("@eslint/js");
const tseslint = require("@typescript-eslint/eslint-plugin");
const expoConfig = require("eslint-config-expo/flat");
const prettier = require("eslint-plugin-prettier");
const prettierConfig = require("eslint-config-prettier");
const reactPlugin = require("eslint-plugin-react");
const typescriptParser = require("@typescript-eslint/parser");
const prettierRecommended = require("eslint-plugin-prettier/recommended");
const react = require("eslint-plugin-react");
const { defineConfig } = require("eslint/config");
const globals = require("globals");

module.exports = defineConfig([
{
ignores: ["node_modules/**", ".expo/**", "dist/**", "ios/**", "android/**"],
},
expoConfig,
prettierConfig,
js.configs.recommended, // ESLint rules
tseslint.configs["flat/recommended"], // TypeScript rules
react.configs.flat.recommended, // React rules
react.configs.flat["jsx-runtime"], // No need to `import React` in every file
prettierRecommended, // Prettier rules
{
languageOptions: {
parser: typescriptParser, // Specifies the ESLint parser
},
plugins: {
prettier,
react: reactPlugin, // add React plugin
globals: {
...globals.node,
},
},
rules: {
"prettier/prettier": [ // Prettier rules
"@typescript-eslint/no-require-imports": "off", // allow `require()` for static assets
"@typescript-eslint/no-unused-vars": "warn", // detect unused variables
"prettier/prettier": [
"warn",
{
usePrettierrc: true,
},
],
"@typescript-eslint/no-unused-vars": "warn", // detect unused variables
},
},
]);
```

- [ ] Create a `.prettierrc` file at the root of your project with the following content:

```javascript
```json
// .prettierrc

{
Expand All @@ -193,11 +198,12 @@ module.exports = defineConfig([
"singleAttributePerLine": true,
"trailingComma": "all",
"tabWidth": 2,
"singleQuote": false,
"endOfLine": "crlf" // for windows users only
"singleQuote": false
}
```

**🔭 Hint:** Windows users can add `"endOfLine": "crlf"`. Don't paste the `// .prettierrc` comment: the file must be valid JSON.

### Updating ESLint rules

We want to add some rules to our ESLint configuration.
Expand All @@ -213,73 +219,66 @@ yarn add --dev eslint-plugin-react-native
```javascript
// eslint.config.js

const reactNativePlugin = require("eslint-plugin-react-native");
const reactNative = require("eslint-plugin-react-native");

// Add to plugins:
// Add to your last config object:
{
plugins: {
...
"react-native": reactNativePlugin,
"react-native": reactNative,
},
rules: {
...
"react-native/no-color-literals": 2, // enforce color literals are not used
"react-native/no-unused-styles": 2, // detect unused StyleSheet rules
"react-native/no-raw-text": 0, // detect raw text outside of Text component
"react-native/no-unused-styles": 2, // detect unused StyleSheet rules
"react-native/sort-styles": 2, // enforce style definitions are sorted
},
}
```

- [ ] Lint your code and push your changes to GitHub.

We now want to sort our imports automatically. We can use the `simple-import-sort` plugin.
We now want to sort our imports (and objects, interfaces…) automatically. We can use the [`perfectionist`](https://perfectionist.dev/) plugin.

```console
npm install --save-dev eslint-plugin-simple-import-sort
npm install --save-dev eslint-plugin-perfectionist
# or
yarn add --dev eslint-plugin-simple-import-sort
yarn add --dev eslint-plugin-perfectionist
```

- [ ] Update your `eslint.config.js` file to add the following import rules:
- [ ] Update your `eslint.config.js` file to add the following sorting rules:

```javascript
// eslint.config.js

const simpleImportSort = require("eslint-plugin-simple-import-sort");
const perfectionist = require("eslint-plugin-perfectionist");

// Add to plugins:
{
plugins: {
...
"simple-import-sort": simpleImportSort,
},
rules: {
module.exports = defineConfig([
...
prettierRecommended,
perfectionist.configs["recommended-natural"], // sort everything with a natural order
{
...
"simple-import-sort/exports": "warn", // enforce sorting exports within module
"simple-import-sort/imports": [
"warn",
{
groups: [
// Side effect imports.
["^\\u0000"],
// Packages `react` related packages come first.
["^react", "^@?\\w"],
// Environment variables
["^(@env)(/.*|$)"],
// Parent imports. Put `..` last.
["^\\.\\.(?!/?$)", "^\\.\\./?$"],
// Other relative imports. Put same-folder imports and `.` last.
["^\\./(?=.*/)(?!/?$)", "^\\.(?!/?$)", "^\\./?$"],
],
rules: {
...
"perfectionist/sort-imports": "error", // https://perfectionist.dev/rules/sort-imports
"perfectionist/sort-interfaces": "error",
"perfectionist/sort-modules": "off",
},
settings: {
perfectionist: {
partitionByComment: true,
type: "line-length",
},
],
},
},
}
]);
```

- [ ] Lint your code and push your changes to GitHub.

**🔭 Hint:** `perfectionist` also sorts object keys, so `useQuery({ queryKey, queryFn })` becomes `useQuery({ queryFn, queryKey })`. It works, but if you prefer TanStack Query's order, copy the `perfectionist/sort-objects` exception from [the spacecraft config](https://raw.githubusercontent.com/flexbox/react-native-bootcamp/main/hackathon/spacecraft/eslint.config.js).

We have no control on the api data we are pulling from SWAPI. Sometimes we want to update our rules to reflect differences.

In our case `cargo_capacity`, `cost_in_credits` are not using `camelCase` and we want to disable this rule.
Expand Down Expand Up @@ -324,4 +323,4 @@ This will make sure everyone in your team is using the same settings.

### Avoid data layer issues

- [ ] Install [TanStack Query ESLint plugin](https://tanstack.com/query/latest/docs/eslint/eslint-plugin-query).
- [ ] Install [TanStack Query ESLint plugin](https://tanstack.com/query/latest/docs/eslint/eslint-plugin-query) and add `pluginQuery.configs["flat/recommended"]` to your `eslint.config.js`.
Loading
Loading