epam-ai-conductor 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +46 -0
- package/bin/workshop.js +6 -0
- package/dist/auth-check.d.ts +1 -0
- package/dist/auth-check.js +45 -0
- package/dist/auth-check.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +156 -0
- package/dist/cli.js.map +1 -0
- package/dist/content/content/module-1/task/module-1_home-task_components.md +257 -0
- package/dist/content/content/module-1/task/module-1_home-task_good_practices.md +69 -0
- package/dist/content/content/module-1/task/module-1_home-task_task.md +253 -0
- package/dist/content/content/module-1/tests/Button.test.tsx +22 -0
- package/dist/content/content/module-1/tests/CourseCard.test.tsx +64 -0
- package/dist/content/content/module-1/tests/CourseInfo.test.tsx +73 -0
- package/dist/content/content/module-1/tests/Courses.test.tsx +87 -0
- package/dist/content/content/module-1/tests/Header.test.tsx +23 -0
- package/dist/content/content/module-1/tests/Input.test.tsx +36 -0
- package/dist/content/content/module-1/tests/helpers.test.ts +13 -0
- package/dist/content/content/module-1/theory/module-1_elements-render.md +66 -0
- package/dist/content/content/module-1/theory/module-1_hooks_useEffect.md +178 -0
- package/dist/content/content/module-1/theory/module-1_hooks_useState.md +131 -0
- package/dist/content/content/module-1/theory/module-1_react.md +64 -0
- package/dist/content/content/module-1/theory/module-1_spa.md +74 -0
- package/dist/content/content/module-2/task/module-2_home-task_components.md +313 -0
- package/dist/content/content/module-2/task/module-2_home-task_good_practices.md +35 -0
- package/dist/content/content/module-2/task/module-2_home-task_task.md +210 -0
- package/dist/content/content/module-2/tests/App.test.tsx +54 -0
- package/dist/content/content/module-2/tests/Login.test.tsx +73 -0
- package/dist/content/content/module-2/tests/Registration.test.tsx +70 -0
- package/dist/content/content/module-2/tests/SearchBar.test.tsx +39 -0
- package/dist/content/content/module-2/theory/module-2_custom-hooks.md +201 -0
- package/dist/content/content/module-2/theory/module-2_hooks.md +117 -0
- package/dist/content/content/module-2/theory/module-2_react-router.md +328 -0
- package/dist/content/content/module-3/task/module-3_home-task_components.md +94 -0
- package/dist/content/content/module-3/task/module-3_home-task_good_practices.md +26 -0
- package/dist/content/content/module-3/task/module-3_home-task_task.md +170 -0
- package/dist/content/content/module-3/tests/App.test.tsx +54 -0
- package/dist/content/content/module-3/tests/Courses.test.tsx +87 -0
- package/dist/content/content/module-3/tests/CreateAuthor.test.tsx +44 -0
- package/dist/content/content/module-3/tests/CreateCourse.test.tsx +122 -0
- package/dist/content/content/module-3/theory/module-3_redux-hooks.md +194 -0
- package/dist/content/content/module-3/theory/module-3_state-actions-reducers.md +445 -0
- package/dist/content/content/module-4/task/module-4_home-task_components.md +187 -0
- package/dist/content/content/module-4/task/module-4_home-task_task.md +139 -0
- package/dist/content/content/module-4/tests/App.test.tsx +54 -0
- package/dist/content/content/module-4/tests/Courses.test.tsx +87 -0
- package/dist/content/content/module-4/tests/CreateCourse.test.tsx +122 -0
- package/dist/content/content/module-4/tests/Login.test.tsx +73 -0
- package/dist/content/content/module-4/theory/module-4_async-redux.md +99 -0
- package/dist/content/content/module-4/theory/module-4_private-routes.md +55 -0
- package/dist/content/content/module-5/task/module-5_home-task_instruction.md +68 -0
- package/dist/content/content/module-5/task/module-5_home-task_task.md +154 -0
- package/dist/content/content/module-5/tests/App.test.tsx +54 -0
- package/dist/content/content/module-5/tests/CourseCard.test.tsx +64 -0
- package/dist/content/content/module-5/tests/Courses.test.tsx +87 -0
- package/dist/content/content/module-5/tests/Header.test.tsx +23 -0
- package/dist/content/content/module-5/theory/module-5_react-testing-library_example.md +379 -0
- package/dist/content/content/module-5/theory/module-5_redux-writing-tests.md +246 -0
- package/dist/content/module-1/task/module-1_home-task_components.md +257 -0
- package/dist/content/module-1/task/module-1_home-task_good_practices.md +69 -0
- package/dist/content/module-1/task/module-1_home-task_task.md +253 -0
- package/dist/content/module-1/tests/Button.test.tsx +22 -0
- package/dist/content/module-1/tests/CourseCard.test.tsx +64 -0
- package/dist/content/module-1/tests/CourseInfo.test.tsx +73 -0
- package/dist/content/module-1/tests/Courses.test.tsx +87 -0
- package/dist/content/module-1/tests/Header.test.tsx +23 -0
- package/dist/content/module-1/tests/Input.test.tsx +36 -0
- package/dist/content/module-1/tests/helpers.test.ts +13 -0
- package/dist/content/module-1/theory/module-1_elements-render.md +66 -0
- package/dist/content/module-1/theory/module-1_hooks_useEffect.md +178 -0
- package/dist/content/module-1/theory/module-1_hooks_useState.md +131 -0
- package/dist/content/module-1/theory/module-1_react.md +64 -0
- package/dist/content/module-1/theory/module-1_spa.md +74 -0
- package/dist/content/module-2/task/module-2_home-task_components.md +313 -0
- package/dist/content/module-2/task/module-2_home-task_good_practices.md +35 -0
- package/dist/content/module-2/task/module-2_home-task_task.md +210 -0
- package/dist/content/module-2/tests/App.test.tsx +54 -0
- package/dist/content/module-2/tests/Login.test.tsx +73 -0
- package/dist/content/module-2/tests/Registration.test.tsx +70 -0
- package/dist/content/module-2/tests/SearchBar.test.tsx +39 -0
- package/dist/content/module-2/theory/module-2_custom-hooks.md +201 -0
- package/dist/content/module-2/theory/module-2_hooks.md +117 -0
- package/dist/content/module-2/theory/module-2_react-router.md +328 -0
- package/dist/content/module-3/task/module-3_home-task_components.md +94 -0
- package/dist/content/module-3/task/module-3_home-task_good_practices.md +26 -0
- package/dist/content/module-3/task/module-3_home-task_task.md +170 -0
- package/dist/content/module-3/tests/App.test.tsx +54 -0
- package/dist/content/module-3/tests/Courses.test.tsx +87 -0
- package/dist/content/module-3/tests/CreateAuthor.test.tsx +44 -0
- package/dist/content/module-3/tests/CreateCourse.test.tsx +122 -0
- package/dist/content/module-3/theory/module-3_redux-hooks.md +194 -0
- package/dist/content/module-3/theory/module-3_state-actions-reducers.md +445 -0
- package/dist/content/module-4/task/module-4_home-task_components.md +187 -0
- package/dist/content/module-4/task/module-4_home-task_task.md +139 -0
- package/dist/content/module-4/tests/App.test.tsx +54 -0
- package/dist/content/module-4/tests/Courses.test.tsx +87 -0
- package/dist/content/module-4/tests/CreateCourse.test.tsx +122 -0
- package/dist/content/module-4/tests/Login.test.tsx +73 -0
- package/dist/content/module-4/theory/module-4_async-redux.md +99 -0
- package/dist/content/module-4/theory/module-4_private-routes.md +55 -0
- package/dist/content/module-5/task/module-5_home-task_instruction.md +68 -0
- package/dist/content/module-5/task/module-5_home-task_task.md +154 -0
- package/dist/content/module-5/tests/App.test.tsx +54 -0
- package/dist/content/module-5/tests/CourseCard.test.tsx +64 -0
- package/dist/content/module-5/tests/Courses.test.tsx +87 -0
- package/dist/content/module-5/tests/Header.test.tsx +23 -0
- package/dist/content/module-5/theory/module-5_react-testing-library_example.md +379 -0
- package/dist/content/module-5/theory/module-5_redux-writing-tests.md +246 -0
- package/dist/content-loader.d.ts +7 -0
- package/dist/content-loader.js +26 -0
- package/dist/content-loader.js.map +1 -0
- package/dist/context-builder.d.ts +2 -0
- package/dist/context-builder.js +116 -0
- package/dist/context-builder.js.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { render, screen, fireEvent } from "@testing-library/react";
|
|
3
|
+
import { MemoryRouter } from "react-router-dom";
|
|
4
|
+
import { Registration } from "../components/Registration";
|
|
5
|
+
import * as services from "../../../services";
|
|
6
|
+
|
|
7
|
+
const mockedUsedNavigate = jest.fn();
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
jest
|
|
11
|
+
.spyOn(services, "createUser")
|
|
12
|
+
.mockImplementation(jest.fn(() => Promise.resolve()) as jest.Mock);
|
|
13
|
+
|
|
14
|
+
jest.spyOn(global, "fetch").mockImplementation(
|
|
15
|
+
jest.fn(() =>
|
|
16
|
+
Promise.resolve({
|
|
17
|
+
ok: true,
|
|
18
|
+
json: () => Promise.resolve(),
|
|
19
|
+
})
|
|
20
|
+
) as jest.Mock
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
jest.mock("react-router-dom", () => ({
|
|
24
|
+
...jest.requireActual("react-router-dom"),
|
|
25
|
+
useNavigate: () => mockedUsedNavigate,
|
|
26
|
+
}));
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe("Registration", () => {
|
|
30
|
+
test("renders the registration form", () => {
|
|
31
|
+
render(
|
|
32
|
+
<MemoryRouter>
|
|
33
|
+
<Registration />
|
|
34
|
+
</MemoryRouter>
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
expect(screen.getByLabelText("name")).toBeInTheDocument();
|
|
38
|
+
expect(screen.getByLabelText("email")).toBeInTheDocument();
|
|
39
|
+
expect(screen.getByLabelText("password")).toBeInTheDocument();
|
|
40
|
+
expect(screen.getByRole("button")).toBeInTheDocument();
|
|
41
|
+
expect(screen.getByRole("link")).toBeInTheDocument();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("submits the form", async () => {
|
|
45
|
+
render(
|
|
46
|
+
<MemoryRouter>
|
|
47
|
+
<Registration />
|
|
48
|
+
</MemoryRouter>
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
fireEvent.change(screen.getByLabelText("name"), {
|
|
52
|
+
target: { value: "John Doe" },
|
|
53
|
+
});
|
|
54
|
+
fireEvent.change(screen.getByLabelText("email"), {
|
|
55
|
+
target: { value: "john.doe@example.com" },
|
|
56
|
+
});
|
|
57
|
+
fireEvent.change(screen.getByLabelText("password"), {
|
|
58
|
+
target: { value: "password123" },
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
fireEvent.click(screen.getByRole("button", { name: "registration" }));
|
|
62
|
+
|
|
63
|
+
expect(services.createUser).toHaveBeenCalledTimes(1);
|
|
64
|
+
await expect(services.createUser).toHaveBeenCalledWith({
|
|
65
|
+
name: "John Doe",
|
|
66
|
+
email: "john.doe@example.com",
|
|
67
|
+
password: "password123",
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { render, screen, fireEvent } from "@testing-library/react";
|
|
3
|
+
import { SearchBar } from "../components/Courses/components/SearchBar/SearchBar";
|
|
4
|
+
|
|
5
|
+
test("SearchBar renders correctly", () => {
|
|
6
|
+
const onAddClick = jest.fn();
|
|
7
|
+
render(<SearchBar onAddClick={onAddClick} />);
|
|
8
|
+
|
|
9
|
+
const inputElement = screen.getByRole("textbox");
|
|
10
|
+
const searchButtonElement = screen.getByText("Search");
|
|
11
|
+
const addButtonElement = screen.getByText("Add new courses");
|
|
12
|
+
|
|
13
|
+
expect(inputElement).toBeInTheDocument();
|
|
14
|
+
expect(searchButtonElement).toBeInTheDocument();
|
|
15
|
+
expect(addButtonElement).toBeInTheDocument();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("SearchBar handles input change correctly", () => {
|
|
19
|
+
const onAddClick = jest.fn();
|
|
20
|
+
render(<SearchBar onAddClick={onAddClick} />);
|
|
21
|
+
|
|
22
|
+
const inputElement = screen.getByRole("textbox") as HTMLInputElement;
|
|
23
|
+
|
|
24
|
+
fireEvent.change(inputElement, {
|
|
25
|
+
target: { value: "test" },
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
expect(inputElement.value).toBe("test");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("SearchBar calls onAddClick correctly", () => {
|
|
32
|
+
const onAddClick = jest.fn();
|
|
33
|
+
render(<SearchBar onAddClick={onAddClick} />);
|
|
34
|
+
|
|
35
|
+
const addButtonElement = screen.getByText("Add new courses");
|
|
36
|
+
fireEvent.click(addButtonElement);
|
|
37
|
+
|
|
38
|
+
expect(onAddClick).toHaveBeenCalledTimes(1);
|
|
39
|
+
});
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 4
|
|
3
|
+
sidebar_label: 'Custom Hooks'
|
|
4
|
+
title: 'Extracting a Custom Hook'
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## What is a Custom Hook?
|
|
8
|
+
|
|
9
|
+
In order not to duplicate code in JavaScript we create functions, which give us an opportunity to
|
|
10
|
+
make our code reusable.
|
|
11
|
+
|
|
12
|
+
Sometimes we may want to use the same logic (for instance, use some set of
|
|
13
|
+
`Hooks`) in different components in React. Usually we don't want to repeat the logic for each component,
|
|
14
|
+
because there may be quite a lot of components in the application. As `Hooks` are also functions we can
|
|
15
|
+
extract repeated logic to a `Custom Hook`.
|
|
16
|
+
|
|
17
|
+
## Usage of Custom Hook
|
|
18
|
+
|
|
19
|
+
Let's create two similar components to understand better what `Custom Hook` is and how it works.
|
|
20
|
+
|
|
21
|
+
```jsx
|
|
22
|
+
// src/App.jsx
|
|
23
|
+
|
|
24
|
+
import FirstComponent from "./FirstComponent";
|
|
25
|
+
import SecondComponent from "./SecondComponent";
|
|
26
|
+
|
|
27
|
+
function App() {
|
|
28
|
+
return (
|
|
29
|
+
<>
|
|
30
|
+
<FirstComponent />
|
|
31
|
+
<SecondComponent />
|
|
32
|
+
</>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default App;
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```jsx
|
|
40
|
+
// src/FirstComponent.jsx
|
|
41
|
+
|
|
42
|
+
import { useEffect, useState } from "react";
|
|
43
|
+
|
|
44
|
+
const url = 'https://api.github.com/users';
|
|
45
|
+
|
|
46
|
+
function FirstComponent() {
|
|
47
|
+
const [data, setData] = useState([]);
|
|
48
|
+
|
|
49
|
+
const getData = async () => {
|
|
50
|
+
const response = await fetch(url);
|
|
51
|
+
const data = await response.json();
|
|
52
|
+
setData(data);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
getData();
|
|
57
|
+
}, []);
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<>
|
|
61
|
+
<div>First Component</div>
|
|
62
|
+
// rendering data after checking if array is not empty
|
|
63
|
+
{data.length > 0 && data.map(element => {
|
|
64
|
+
return <div key={element.id}>{element.login}</div>;
|
|
65
|
+
})}
|
|
66
|
+
</>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export default FirstComponent;
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
```jsx
|
|
74
|
+
// src/SecondComponent.jsx
|
|
75
|
+
|
|
76
|
+
import { useEffect, useState } from "react";
|
|
77
|
+
|
|
78
|
+
const url = 'https://api.github.com/events';
|
|
79
|
+
|
|
80
|
+
function SecondComponent() {
|
|
81
|
+
const [data, setData] = useState([]);
|
|
82
|
+
|
|
83
|
+
const getData = async () => {
|
|
84
|
+
const response = await fetch(url);
|
|
85
|
+
const data = await response.json();
|
|
86
|
+
setData(data);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
getData();
|
|
91
|
+
}, []);
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<>
|
|
95
|
+
<div>Second Component</div>
|
|
96
|
+
// rendering data after checking if array is not empty
|
|
97
|
+
{data.length > 0 && data.map(element => {
|
|
98
|
+
return <div key={element.id}>{element.type}</div>;
|
|
99
|
+
})}
|
|
100
|
+
</>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export default SecondComponent;
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
As you can see, these components are almost the same: both have local state with data and both make
|
|
108
|
+
request to API. The only difference is URL. So now when similarity is obvious let's create a
|
|
109
|
+
`Custom Hook` `useFetch` with common logic.
|
|
110
|
+
|
|
111
|
+
:::warning
|
|
112
|
+
Custom Hook's name should start with `use`.
|
|
113
|
+
:::
|
|
114
|
+
|
|
115
|
+
```jsx
|
|
116
|
+
// src/useFetch.jsx
|
|
117
|
+
|
|
118
|
+
import { useEffect, useState } from "react";
|
|
119
|
+
|
|
120
|
+
function useFetch(url) {
|
|
121
|
+
const [data, setData] = useState([]);
|
|
122
|
+
|
|
123
|
+
const getData = async () => {
|
|
124
|
+
const response = await fetch(url);
|
|
125
|
+
const data = await response.json();
|
|
126
|
+
setData(data);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
useEffect(() => {
|
|
130
|
+
getData();
|
|
131
|
+
}, [url]);
|
|
132
|
+
|
|
133
|
+
return data;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export default useFetch;
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
As you can see inside the `Custom Hook` we moved our `useState` and `useEffect hooks`.
|
|
140
|
+
It works the same as this code worked inside components, but the difference is:
|
|
141
|
+
* it takes `url` as a parameter
|
|
142
|
+
* `useEffect` will execute every time when URL changes (dependency array is the second parameter of `useEffect`)
|
|
143
|
+
* returns `data` that we need.
|
|
144
|
+
|
|
145
|
+
Now we can use it inside our components.
|
|
146
|
+
|
|
147
|
+
```jsx
|
|
148
|
+
// src/FirstComponent.jsx
|
|
149
|
+
|
|
150
|
+
import useFetch from "./useFetch";
|
|
151
|
+
|
|
152
|
+
const url = 'https://api.github.com/users';
|
|
153
|
+
|
|
154
|
+
function FirstComponent() {
|
|
155
|
+
const data = useFetch(url);
|
|
156
|
+
|
|
157
|
+
return (
|
|
158
|
+
<>
|
|
159
|
+
<div>First Component</div>
|
|
160
|
+
{data.length > 0 && data.map(element => {
|
|
161
|
+
return <div key={element.id}>{element.login}</div>;
|
|
162
|
+
})}
|
|
163
|
+
</>
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export default FirstComponent;
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
```jsx
|
|
171
|
+
// src/SecondComponent.jsx
|
|
172
|
+
|
|
173
|
+
import useFetch from "./useFetch";
|
|
174
|
+
|
|
175
|
+
const url = 'https://api.github.com/events';
|
|
176
|
+
|
|
177
|
+
function SecondComponent() {
|
|
178
|
+
const data = useFetch(url);
|
|
179
|
+
|
|
180
|
+
return (
|
|
181
|
+
<>
|
|
182
|
+
<div>Second Component</div>
|
|
183
|
+
{data.map(element => {
|
|
184
|
+
return <div key={element.id}>{element.type}</div>;
|
|
185
|
+
})}
|
|
186
|
+
</>
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export default SecondComponent;
|
|
191
|
+
```
|
|
192
|
+
As you can see we use `Custom Hook` as a simple JS function that starts with 'use' and returns data.
|
|
193
|
+
So now there is no need to duplicate our code for every component.
|
|
194
|
+
|
|
195
|
+
## Conclusion
|
|
196
|
+
|
|
197
|
+
Congratulations! Now you've learned how `Custom Hooks` work.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
## Materials
|
|
201
|
+
1. [React documentation. Custom Hooks](https://react.dev/learn/reusing-logic-with-custom-hooks)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 3
|
|
3
|
+
sidebar_label: 'React Router Hooks'
|
|
4
|
+
title: 'React Router Hooks'
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## useLocation()
|
|
8
|
+
This hook returns the current location object. This can be useful if you'd like to perform some side effect whenever the current location changes.
|
|
9
|
+
|
|
10
|
+
```jsx
|
|
11
|
+
import React, { useEffect } from 'react';
|
|
12
|
+
import { useLocation } from 'react-router-dom';
|
|
13
|
+
|
|
14
|
+
function App() {
|
|
15
|
+
const location = useLocation();
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
// this block will run once location is changed
|
|
19
|
+
}, [location]);
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
// ...
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## useNavigate()
|
|
29
|
+
The `useNavigate` hook returns a function that lets you navigate programmatically, for example after a form is submitted.
|
|
30
|
+
|
|
31
|
+
```jsx
|
|
32
|
+
import { useNavigate } from "react-router-dom";
|
|
33
|
+
|
|
34
|
+
function SignupForm() {
|
|
35
|
+
const navigate = useNavigate();
|
|
36
|
+
|
|
37
|
+
async function handleSubmit(event) {
|
|
38
|
+
event.preventDefault();
|
|
39
|
+
|
|
40
|
+
await submitForm(event.target);
|
|
41
|
+
|
|
42
|
+
navigate("../success", { replace: true });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return <form onSubmit={handleSubmit}>{/* ... */}</form>;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
The navigate function has two signatures:
|
|
50
|
+
|
|
51
|
+
* Either pass a `to` value (same type as `<Link to>`) with an optional second `{ replace, state }` arg or
|
|
52
|
+
|
|
53
|
+
* Pass the delta you want to go in the history stack. For example, `navigate(-1)` is equivalent to hitting the back button.
|
|
54
|
+
|
|
55
|
+
## useParams()
|
|
56
|
+
The `useParams` hook returns an object of key/value pairs of the dynamic params from the current
|
|
57
|
+
URL that were matched by the `<Route path >`.
|
|
58
|
+
Child routes inherit all params from their parent routes.
|
|
59
|
+
|
|
60
|
+
```jsx
|
|
61
|
+
import * as React from 'react';
|
|
62
|
+
import { Routes, Route, useParams } from 'react-router-dom';
|
|
63
|
+
|
|
64
|
+
function ProfilePage() {
|
|
65
|
+
// Get the userId param from the URL.
|
|
66
|
+
const { userId } = useParams();
|
|
67
|
+
// ...
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function App() {
|
|
71
|
+
return (
|
|
72
|
+
<Routes>
|
|
73
|
+
<Route path="users">
|
|
74
|
+
<Route path=":userId" element={<ProfilePage />} />
|
|
75
|
+
<Route path="me" element={...} />
|
|
76
|
+
</Route>
|
|
77
|
+
</Routes>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## useRoutes()
|
|
84
|
+
|
|
85
|
+
The useRoutes hook is the functional equivalent of `<Routes>`, but it uses JavaScript objects instead of `<Route>` elements to define your routes. These objects have the same properties as normal `<Route>` elements, but they don't require JSX.
|
|
86
|
+
|
|
87
|
+
The return value of `useRoutes` is either a valid React element you can use to render the route tree, or null if nothing matched.
|
|
88
|
+
|
|
89
|
+
```jsx
|
|
90
|
+
import * as React from "react";
|
|
91
|
+
import { useRoutes } from "react-router-dom";
|
|
92
|
+
|
|
93
|
+
function App() {
|
|
94
|
+
let element = useRoutes([
|
|
95
|
+
{
|
|
96
|
+
path: "/",
|
|
97
|
+
element: <App />,
|
|
98
|
+
children: [
|
|
99
|
+
{
|
|
100
|
+
path: "first-page",
|
|
101
|
+
element: <FirstPage />,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
path: "second-page",
|
|
105
|
+
element: <SecondPage />,
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
]);
|
|
110
|
+
|
|
111
|
+
return element;
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
:::info
|
|
116
|
+
You can find more React Router hooks in the official [documentation](https://reactrouter.com/en/6.6.2)
|
|
117
|
+
:::
|