11 | );
12 | }
13 |
14 | const rootElement = document.getElementById("root");
15 | ReactDOM.render(, rootElement);
16 |
--------------------------------------------------------------------------------
/01-basic/00-use-state/src/styles.css:
--------------------------------------------------------------------------------
1 | .App {
2 | font-family: sans-serif;
3 | text-align: center;
4 | }
5 |
--------------------------------------------------------------------------------
/01-basic/01-use-state-object/Readme.md:
--------------------------------------------------------------------------------
1 | # 02 Use State Object
2 |
3 | In the previous sample we learnt how to make use of _useState_ to add state
4 | to a functional component. We just added a simple field (string), but what
5 | if we want to useState on an object? What is the equivalent to class
6 | component based _SetState_? Your friend spread operator :), let's hop on that.
7 |
8 | # Steps
9 |
10 | - We will take as starting point sample _00-use-state. Copy the content of the
11 | project to a fresh folder an execute \_npm install_.
12 |
13 | ```bash
14 | npm install
15 | ```
16 |
17 | - Let's open the _demo.js_ file and add our hooks based state.
18 |
19 | _./src/demo.js_
20 |
21 | ```diff
22 | import React from "react";
23 |
24 | export const MyComponent = props => {
25 | - const [myName, setMyName] = React.useState("John Doe");
26 | + const [userInfo, setUserInfo] = React.useState({
27 | + name: 'John',
28 | + lastname: 'Doe',
29 | + });
30 |
31 | return (
32 | <>
33 | -
{myName}
34 | +
{userInfo.name} {userInfo.lastname}
35 |
36 | - setMyName(e.target.value)} />
37 | + setUserInfo({
40 | + ...userInfo,
41 | + name: e.target.value
42 | + })}
43 | + />
44 | + setUserInfo({
47 | + ...userInfo,
48 | + lastname: e.target.value
49 | + })}
50 | + />
51 | + >
52 | >
53 | );
54 | };
55 | ```
56 |
57 | - Now if you run the sample you can check that you can update both properties
58 | _name_ and _lastname_, you can easily assign an object to useState, and in order
59 | to update it you can just make use of the _spread operator_ instead of using
60 | _setState_.
61 |
62 | # About Basefactor + Lemoncode
63 |
64 | We are an innovating team of Javascript experts, passionate about turning your ideas into robust products.
65 |
66 | [Basefactor, consultancy by Lemoncode](http://www.basefactor.com) provides consultancy and coaching services.
67 |
68 | [Lemoncode](http://lemoncode.net/services/en/#en-home) provides training services.
69 |
70 | For the LATAM/Spanish audience we are running an Online Front End Master degree, more info: http://lemoncode.net/master-frontend
71 |
--------------------------------------------------------------------------------
/01-basic/01-use-state-object/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "new",
3 | "version": "1.0.0",
4 | "description": "",
5 | "keywords": [],
6 | "main": "src/index.js",
7 | "dependencies": {
8 | "react": "16.8.6",
9 | "react-dom": "16.8.6",
10 | "react-scripts": "2.1.8"
11 | },
12 | "devDependencies": {
13 | "typescript": "3.3.3"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test --env=jsdom",
19 | "eject": "react-scripts eject"
20 | },
21 | "browserslist": [
22 | ">0.2%",
23 | "not dead",
24 | "not ie <= 11",
25 | "not op_mini all"
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/01-basic/01-use-state-object/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
23 | React App
24 |
25 |
26 |
27 |
30 |
31 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/01-basic/01-use-state-object/src/demo.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export const MyComponent = props => {
4 | const [userInfo, setUserInfo] = React.useState({
5 | name: "John",
6 | lastname: "Doe"
7 | });
8 |
9 | return (
10 | <>
11 |
11 | );
12 | }
13 |
14 | const rootElement = document.getElementById("root");
15 | ReactDOM.render(, rootElement);
16 |
--------------------------------------------------------------------------------
/01-basic/01-use-state-object/src/styles.css:
--------------------------------------------------------------------------------
1 | .App {
2 | font-family: sans-serif;
3 | text-align: center;
4 | }
5 |
--------------------------------------------------------------------------------
/01-basic/02-component-did-mount/Readme.md:
--------------------------------------------------------------------------------
1 | # 02 Component Did Mount
2 |
3 | Reading from the state and updating it on a functional component is something great,
4 | but we are missing another important part of class components, what about
5 | lifecycle event handlers like _componentDidMount_? How can we listen to an event
6 | like that in a functional component? _React.useEffect_ is your friend.
7 |
8 | # Steps
9 |
10 | - We will take as starting point sample _01 use-state-object_. Copy the content of the
11 | project to a fresh folder an execute _npm install_.
12 |
13 | ```bash
14 | npm install
15 | ```
16 |
17 | - Let's open the _demo.js_ file, and overwrite it with the following content.
18 |
19 | _./src/demo.js_
20 |
21 | ```jsx
22 | import React from "react";
23 |
24 | export const MyComponent = () => {
25 | const [username, setUsername] = React.useState("");
26 |
27 | return (
28 | <>
29 |
{username}
30 | setUsername(e.target.value)} />
31 | >
32 | );
33 | };
34 | ```
35 |
36 | - If we run the sample, nothing wil be shown (name is empty), what if we want
37 | to assign some value right when the component is mounted? We can make use of
38 | _React.useEffect_ passing as a second argument an empty array (that's important
39 | if we don't pass this the code inside the _useEffect_ would be executed on
40 | mount and after every render).
41 |
42 | _./src/demo.js_
43 |
44 | ```diff
45 | import React from "react";
46 |
47 | export const MyComponent = () => {
48 | const [username, setUsername] = React.useState("");
49 |
50 | + React.useEffect(() => {
51 | + setUsername("John");
52 | + }, []);
53 |
54 | return (
55 | <>
56 |
{username}
57 | setUsername(e.target.value)} />
58 | >
59 | );
60 | };
61 | ```
62 |
63 | - Now if you run the sample you can check that _John_ is displayed as user name.
64 |
65 | * Let's go one step further, let's simulate an asynchronous call (we will do it
66 | using _setTimeout_).
67 |
68 | _./src/demo.js_
69 |
70 | ```diff
71 | import React from "react";
72 |
73 | export const MyComponent = () => {
74 | const [username, setUsername] = React.useState("");
75 |
76 | React.useEffect(() => {
77 | - setUsername("John");
78 | + // Simulating async call
79 | + setTimeout(() => {
80 | + setUsername("John");
81 | + }, 1500);
82 | }, []);
83 |
84 | return (
85 | <>
86 |
{username}
87 | setUsername(e.target.value)} />
88 | >
89 | );
90 | };
91 | ```
92 |
93 | - Now _John_ is displayed after 1,5 seconds, instead of _setTimeout_ you could
94 | use here _fetch_ or any other similar approach to make an ajax request.
95 |
96 | - What happens if we remove the _[]_ on the useEffect? The code in this effect will be
97 | triggered when the function component is instantiated and when any render is going to be triggered.
98 |
99 | # About Basefactor + Lemoncode
100 |
101 | We are an innovating team of Javascript experts, passionate about turning your ideas into robust products.
102 |
103 | [Basefactor, consultancy by Lemoncode](http://www.basefactor.com) provides consultancy and coaching services.
104 |
105 | [Lemoncode](http://lemoncode.net/services/en/#en-home) provides training services.
106 |
107 | For the LATAM/Spanish audience we are running an Online Front End Master degree, more info: http://lemoncode.net/master-frontend
108 |
--------------------------------------------------------------------------------
/01-basic/02-component-did-mount/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "new",
3 | "version": "1.0.0",
4 | "description": "",
5 | "keywords": [],
6 | "main": "src/index.js",
7 | "dependencies": {
8 | "react": "16.8.6",
9 | "react-dom": "16.8.6",
10 | "react-scripts": "2.1.8"
11 | },
12 | "devDependencies": {
13 | "typescript": "3.3.3"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test --env=jsdom",
19 | "eject": "react-scripts eject"
20 | },
21 | "browserslist": [
22 | ">0.2%",
23 | "not dead",
24 | "not ie <= 11",
25 | "not op_mini all"
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/01-basic/02-component-did-mount/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
23 | React App
24 |
25 |
26 |
27 |
30 |
31 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/01-basic/02-component-did-mount/src/demo.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export const MyComponent = () => {
4 | const [username, setUsername] = React.useState("");
5 |
6 | React.useEffect(() => {
7 | // Simulating async call
8 | setTimeout(() => {
9 | setUsername("John");
10 | }, 1500);
11 | }, []);
12 |
13 | return (
14 | <>
15 |
11 | );
12 | }
13 |
14 | const rootElement = document.getElementById("root");
15 | ReactDOM.render(, rootElement);
16 |
--------------------------------------------------------------------------------
/01-basic/02-component-did-mount/src/styles.css:
--------------------------------------------------------------------------------
1 | .App {
2 | font-family: sans-serif;
3 | text-align: center;
4 | }
5 |
--------------------------------------------------------------------------------
/01-basic/03-component-unmount/Readme.md:
--------------------------------------------------------------------------------
1 | # 03 Component unmount
2 |
3 | When we worked with Class component there was a way to free resources (e.g.
4 | a socket connection, or trapping x,y mouse coordinates...) when the component
5 | was unmounted (componentWillUnMount), is there a way to do something like
6 | that using hooks? The answer is yes, including more scenarios (beware to
7 | proper learning them).
8 |
9 | # Steps
10 |
11 | - We will take as starting point sample _02-component-did-mount_. Copy the content of the
12 | project to a fresh folder an execute _npm install_.
13 |
14 | ```bash
15 | npm install
16 | ```
17 |
18 | - Let's open the _demo.js_ file: this time we will create a parent component
19 | and a child component, the child component will be mounted / unmounted by
20 | clicking a button on the parent component.
21 |
22 | In the child component we will make use of _React.useEffect_ and using
23 | as a second parameter an empty array to ensure that the code that will
24 | called by _useEffect_ will be only executed when the component is mounted.
25 |
26 | Overwrite _demo.js_ file with the following content.
27 |
28 | _./src/demo.js_
29 |
30 | ```jsx
31 | import React from "react";
32 |
33 | export const MyComponent = () => {
34 | const [visible, setVisible] = React.useState(false);
35 |
36 | return (
37 | <>
38 | {visible && }
39 |
42 | >
43 | );
44 | };
45 |
46 | const MyChildComponent = () => {
47 | const [userInfo, setUserInfo] = React.useState({
48 | name: "John",
49 | lastname: "Doe"
50 | });
51 |
52 | React.useEffect(() => {
53 | console.log("called when the component is mounted");
54 | }, []);
55 |
56 | return (
57 |
11 | );
12 | }
13 |
14 | const rootElement = document.getElementById("root");
15 | ReactDOM.render(, rootElement);
16 |
--------------------------------------------------------------------------------
/01-basic/03-component-unmount/src/styles.css:
--------------------------------------------------------------------------------
1 | .App {
2 | font-family: sans-serif;
3 | text-align: center;
4 | }
5 |
--------------------------------------------------------------------------------
/01-basic/04 ajax-field-change/Readme.md:
--------------------------------------------------------------------------------
1 | # 04 Ajax field change
2 |
3 | Let's face the following scenario. A given user can enter a name on a input field,
4 | we want to trigger an ajax called each time the user types a value on the input
5 | (returned the filtered list of names). We can do this using _useEffect_ indicating
6 | in the second argument, instead of an empty array, the field name used to trigger
7 | the call.
8 |
9 | > Note down: ideally we should implement some kind of debounce behavior, we will
10 | > implement this as an exercise later on.
11 |
12 | # Steps
13 |
14 | - We will take as starting point sample _03 component unmount_. Copy the content of the
15 | project to a fresh folder an execute _npm install_.
16 |
17 | ```bash
18 | npm install
19 | ```
20 |
21 | - Let's open the _demo.js_, we will create the boiler plate code
22 | (add a filter input, display a list of names)
23 |
24 | _./src/demo.js_
25 |
26 | ```jsx
27 | import React from "react";
28 |
29 | export const MyComponent = () => {
30 | const [filter, setFilter] = React.useState("");
31 | const [userCollection, setUserCollection] = React.useState([]);
32 |
33 | return (
34 |
35 | setFilter(e.target.value)} />
36 |
37 | {userCollection.map((user, index) => (
38 |
{user.name}
39 | ))}
40 |
41 |
42 | );
43 | };
44 | ```
45 |
46 | - Now we want to fire an ajax request every time user types on the filter input.
47 |
48 | Let's do this in two steps, first let's define the effect and output an console.log
49 | with the filter updated.
50 |
51 | _./src/demo.js_
52 |
53 | ```diff
54 | export const MyComponent = () => {
55 | const [filter, setFilter] = React.useState('');
56 | const [userCollection, setUserCollection] = React.useState([]);
57 |
58 | + // Load full list when the component gets mounted and filter gets updated
59 | + React.useEffect(() => {
60 | + console.log(filter);
61 | + }, [filter]);
62 |
63 | return (
64 | ```
65 |
66 | if we run this sample we can check in the console log that every time we type in the filter
67 | the code located under the _useEffect_ gets executed.
68 |
69 | Now let's fire an ajax request on every call to update the list of users:
70 |
71 | _./src/demo.js_
72 |
73 | ```diff
74 | export const MyComponent = () => {
75 | const [filter, setFilter] = React.useState('');
76 | const [userCollection, setUserCollection] = React.useState([]);
77 |
78 | React.useEffect(() => {
79 | - console.log(filter);
80 | + fetch(`https://jsonplaceholder.typicode.com/users?name_like=${filter}`)
81 | + .then(response => response.json())
82 | + .then(json => setUserCollection(json));
83 | }, [filter]);
84 |
85 | return (
86 | ```
87 |
88 | > We are making use here of the create _jsonplaceholder_ rest mock api.
89 |
90 | - If we run the sample we can check that every time we start typing on the input
91 | an ajax called is triggered returning the list of results filtered by the filter
92 | field.
93 |
94 | # About Basefactor + Lemoncode
95 |
96 | We are an innovating team of Javascript experts, passionate about turning your ideas into robust products.
97 |
98 | [Basefactor, consultancy by Lemoncode](http://www.basefactor.com) provides consultancy and coaching services.
99 |
100 | [Lemoncode](http://lemoncode.net/services/en/#en-home) provides training services.
101 |
102 | For the LATAM/Spanish audience we are running an Online Front End Master degree, more info: http://lemoncode.net/master-frontend
103 |
--------------------------------------------------------------------------------
/01-basic/04 ajax-field-change/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "new",
3 | "version": "1.0.0",
4 | "description": "",
5 | "keywords": [],
6 | "main": "src/index.js",
7 | "dependencies": {
8 | "react": "16.8.6",
9 | "react-dom": "16.8.6",
10 | "react-scripts": "2.1.8"
11 | },
12 | "devDependencies": {
13 | "typescript": "3.3.3"
14 | },
15 | "scripts": {
16 | "start": "react-scripts start",
17 | "build": "react-scripts build",
18 | "test": "react-scripts test --env=jsdom",
19 | "eject": "react-scripts eject"
20 | },
21 | "browserslist": [
22 | ">0.2%",
23 | "not dead",
24 | "not ie <= 11",
25 | "not op_mini all"
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/01-basic/04 ajax-field-change/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
14 |
23 | React App
24 |
25 |
26 |
27 |
30 |
31 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/01-basic/04 ajax-field-change/src/demo.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export const MyComponent = () => {
4 | const [filter, setFilter] = React.useState("");
5 | const [userCollection, setUserCollection] = React.useState([]);
6 |
7 | React.useEffect(() => {
8 | fetch(`https://jsonplaceholder.typicode.com/users?name_like=${filter}`)
9 | .then(response => response.json())
10 | .then(json => setUserCollection(json));
11 | }, [filter]);
12 |
13 | return (
14 |
11 | );
12 | }
13 |
14 | const rootElement = document.getElementById("root");
15 | ReactDOM.render(, rootElement);
16 |
--------------------------------------------------------------------------------
/03-block-1/00-custom-hook/src/styles.css:
--------------------------------------------------------------------------------
1 | .App {
2 | font-family: sans-serif;
3 | text-align: center;
4 | }
5 |
--------------------------------------------------------------------------------
/03-block-1/01-pure-component/Readme.md:
--------------------------------------------------------------------------------
1 | # 01 Pure Components
2 |
3 | When we used class components we could make use of PureComponents, this
4 | components will just make a shallow compare of the props and only render
5 | if there were changes. Is there a way to do this using hooks? Yes,
6 | using _React.memo_
7 |
8 | # Steps
9 |
10 | - We will take as starting point sample _00 custom hooks_. Copy the content of the
11 | project to a fresh folder an execute _npm install_.
12 |
13 | ```bash
14 | npm install
15 | ```
16 |
17 | - Let's open the _demo.js_, we will create a parent and a child component
18 |
19 | _./src/demo.js_
20 |
21 | ```jsx
22 | import React from "react";
23 |
24 | export const MyComponent = () => {
25 | const [userInfo, setUserInfo] = React.useState({
26 | name: " John ",
27 | lastname: "Doe"
28 | });
29 |
30 | return (
31 | <>
32 |
33 |
36 | setUserInfo({
37 | ...userInfo,
38 | name: e.target.value
39 | })
40 | }
41 | />
42 |
45 | setUserInfo({
46 | ...userInfo,
47 | lastname: e.target.value
48 | })
49 | }
50 | />
51 | >
52 | );
53 | };
54 |
55 | export const DisplayUsername = props => {
56 | console.log(
57 | "Hey I'm only rerendered when name gets updated, check React.memo"
58 | );
59 |
60 | return
{props.name}
;
61 | };
62 | ```
63 |
64 | - If we run the sample we can check that any time we change for instance
65 | _lastname_ it will rerender _DisplayUsername_, the optimal approach
66 | could be only to rerender _DisplayUsername_ just when the _props.name_
67 | is updated, if we wrap the _DisplayUsername_ component using _React.memo_
68 | it will do the trick for us.
69 |
70 | _./src/demo.js_
71 |
72 | ```diff
73 | - export const DisplayUsername = props => {
74 | + export const DisplayUsername = React.memo(props => {
75 |
76 | console.log(
77 | "Hey I'm only rerendered when name gets updated, check React.memo"
78 | );
79 |
80 | return
11 | );
12 | }
13 |
14 | const rootElement = document.getElementById("root");
15 | ReactDOM.render(, rootElement);
16 |
--------------------------------------------------------------------------------
/03-block-1/01-pure-component/src/styles.css:
--------------------------------------------------------------------------------
1 | .App {
2 | font-family: sans-serif;
3 | text-align: center;
4 | }
5 |
--------------------------------------------------------------------------------
/03-block-1/02-pure-component-callback/Readme.md:
--------------------------------------------------------------------------------
1 | # 02 Pure Components Callback
2 |
3 | In the previous sample we saw how to make a component to be pure using
4 | _React.memo_, that's great, but when there's an issue
5 | what happens if we pass the function created inside function component to the child component?
6 | That function will be always different on every render thus
7 | the _memo_ won't take effect.
8 |
9 | How can we solve this? We can make use of _useCallback_, this won't mutate the setter
10 | function unless we indicate any dependency (same approach as with React.useEffect).
11 |
12 | # Steps
13 |
14 | - We will take as starting point sample _09 pure component callback_. Copy the content of the
15 | project to a fresh folder an execute _npm install_.
16 |
17 | ```bash
18 | npm install
19 | ```
20 |
21 | - Let's open the _demo.js_. We will create a parent and a child component
22 | (this time the child component will just reset the name content).
23 |
24 | _./src/demo.js_
25 |
26 | ```jsx
27 | import React from "react";
28 |
29 | export const MyComponent = () => {
30 | const [username, setUsername] = React.useState("John");
31 | const [lastname, setLastname] = React.useState("Doe");
32 |
33 | const resetNameCallback = () => {
34 | setUsername("");
35 | };
36 |
37 | return (
38 | <>
39 |
40 | {username} {lastname}
41 |
42 | setUsername(e.target.value)} />
43 | setLastname(e.target.value)} />
44 | Reset name
45 | >
46 | );
47 | };
48 |
49 | const ResetValue = React.memo(props => {
50 | console.log(
51 | "Hey I'm only rendered the first time, check React.memo + callback"
52 | );
53 |
54 | return ;
55 | });
56 | ```
57 |
58 | - If we run the sample we will check that the render is always triggered
59 | (_resetNameCallback_ is always recreated, shallow compare will fail).
60 |
61 | - The trick here is to use _React.useCallback_ and passing as a second
62 | argument an empty array (it will just hold the reference for the function
63 | forever).
64 |
65 | ```diff
66 | import React from "react";
67 |
68 | export const MyComponent = () => {
69 | const [username, setUsername] = React.useState("John");
70 | const [lastname, setLastname] = React.useState("Doe");
71 |
72 |
73 | - const resetNameCallback = () => {setUsername('');}
74 | + const resetNameCallback = React.useCallback(() => setUsername(''), []);
75 |
76 | return (
77 | <>
78 |
11 | );
12 | }
13 |
14 | const rootElement = document.getElementById("root");
15 | ReactDOM.render(, rootElement);
16 |
--------------------------------------------------------------------------------
/03-block-1/02-pure-component-callback/src/styles.css:
--------------------------------------------------------------------------------
1 | .App {
2 | font-family: sans-serif;
3 | text-align: center;
4 | }
5 |
--------------------------------------------------------------------------------
/03-block-1/Readme.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lemoncode/react-alicante-hooks-workshop/3770c81812b9e19bdd46f65de969bba686f6f1f4/03-block-1/Readme.md
--------------------------------------------------------------------------------
/04-block-2/00-use-reducer/Readme.md:
--------------------------------------------------------------------------------
1 | # 00 useReducer
2 |
3 | In the previous sample we worked around the issue with the function
4 | that was getting updated on every render by using _useCallback_, this
5 | approach is cool, but for more complex scenarios you may want to organize
6 | your code using a different approach. Another way of solving this issue
7 | is using _useReducer_, this hook will return a _dispatch_
8 | function that remains stable.
9 |
10 | # Steps
11 |
12 | - We will take as starting point sample _00 boilerplate_. Copy the content of the
13 | project to a fresh folder an execute _npm install_.
14 |
15 | ```bash
16 | npm install
17 | ```
18 |
19 | - Let's open the _demo.js_. We will create a parent and a child component
20 | (this time the child component will be able to display and edit a given name).
21 |
22 | _./src/demo.js_
23 |
24 | ```jsx
25 | import React from "react";
26 |
27 | export const MyComponent = () => {
28 | const [userInfo, setInfo] = React.useState({ name: "John", lastname: "Doe" });
29 |
30 | return (
31 | <>
32 |
12 |
13 | );
14 | }
15 |
16 | const rootElement = document.getElementById("root");
17 | ReactDOM.render(, rootElement);
18 |
--------------------------------------------------------------------------------
/04-block-2/01-use-context/src/styles.css:
--------------------------------------------------------------------------------
1 | .App {
2 | font-family: sans-serif;
3 | text-align: center;
4 | }
5 |
--------------------------------------------------------------------------------
/04-block-2/Readme.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lemoncode/react-alicante-hooks-workshop/3770c81812b9e19bdd46f65de969bba686f6f1f4/04-block-2/Readme.md
--------------------------------------------------------------------------------
/05 block-3/00 async-closure/Readme.md:
--------------------------------------------------------------------------------
1 | # 00 Async Closure
2 |
3 | When you are new to hooks, you can struggle if you need to manage async callbacks and get
4 | current values from useState, this doesn't work out of the box because the variables
5 | generated by useState are frozen (following js closure principle you don't get the current
6 | value).
7 |
8 | How can we solve this? by using _useRef_
9 |
10 | # Steps
11 |
12 | - We will take as starting point sample _00 boilerplate_. Copy the content of the
13 | project to a fresh folder an execute _npm install_.
14 |
15 | ```bash
16 | npm install
17 | ```
18 |
19 | > If you come from the _context_ example remember to remove unused provider in _./src/index.js_
20 |
21 | _./src/index.js_
22 |
23 | ```diff
24 | import React from "react";
25 | import ReactDOM from "react-dom";
26 | - import { MyComponent, MyContextProvider } from "./demo";
27 | + import { MyComponent } from "./demo";
28 |
29 | import "./styles.css";
30 |
31 | function App() {
32 | return (
33 | -
34 |
35 |
36 |
37 | -
38 | );
39 | }
40 |
41 | const rootElement = document.getElementById("root");
42 | ReactDOM.render(, rootElement);
43 | ```
44 |
45 | - Let's create an edge case, we will use _useEffect_. Inside we will have to asynchronous calls,
46 | the first one should setup a _seconds_ state to 1 (it will be executed after 1 seconds), then
47 | a second async call will be executed after 2 seconds of time elapse, in theory it should
48 | show the current seconds value (1) but instead we get 0. Let's give a try:
49 |
50 | _./src/demo.js_
51 |
52 | ```jsx
53 | import React from "react";
54 |
55 | export const MyComponent = () => {
56 | const [message, setMessage] = React.useState("initial message");
57 | const [seconds, setSeconds] = React.useState(0);
58 |
59 | React.useEffect(() => {
60 | setTimeout(() => {
61 | console.log(seconds);
62 | setSeconds(seconds + 1);
63 | }, 1000);
64 |
65 | setTimeout(() => {
66 | setMessage(`Total seconds: ${seconds}`);
67 | }, 2000);
68 | }, []); // Important right click and ignore linter rule (later on check what happens)
69 |
70 | return (
71 | <>
72 |
{message}
73 |
{seconds}
74 | >
75 | );
76 | };
77 | ```
78 |
79 | > Check the linter rule, is asking us to add seconds, we will explore this later on
80 |
81 | - The problem here is that seconds points out to the previous value (check javascript closures
82 | for more information), how can we solve this? using _useRef_ (it will hold a mutable value in
83 | a field called _current_).
84 |
85 | ```diff
86 | import React from "react";
87 |
88 | export const MyComponent = () => {
89 | const [message, setMessage] = React.useState("initial message");
90 | const [seconds, setSeconds] = React.useState(0);
91 |
92 | + const secondsRef = React.useRef(seconds);
93 |
94 |
95 | React.useEffect(() => {
96 | setTimeout(() => {
97 | console.log(seconds);
98 | setSeconds(seconds + 1);
99 | + secondsRef.current = seconds + 1;
100 | }, 1000);
101 |
102 | setTimeout(() => {
103 | - setMessage(`Total seconds: ${seconds}`);
104 | + setMessage(`Total seconds: ${secondsRef.current}`);
105 | }, 2000);
106 | }, []);
107 |
108 | return (
109 | <>
110 |
{message}
111 |
{seconds}
112 | >
113 | );
114 | };
115 | ```
116 |
117 | - Now if we run the sample we will get the expected behavior.
118 |
119 | - What would happen if we follow the linter rule?
120 |
121 | _./src/demo.js_
122 |
123 | ```diff
124 | React.useEffect(() => {
125 | setTimeout(() => {
126 | console.log(seconds);
127 | setSeconds(seconds + 1);
128 | secondsRef.current = seconds + 1;
129 | }, 1000);
130 |
131 | setTimeout(() => {
132 | setMessage(`Total seconds: ${secondsRef.current}`);
133 | }, 2000);
134 | // eslint-disable-next-line react-hooks/exhaustive-deps
135 | - }, []);
136 | + }, [seconds]);
137 | ```
138 |
139 | All this exercise was great to know a bit about issues and edge
140 | cases we can find when using hooks, but let's apply to real life
141 | issues, let's cover the following case: we have a rich gmail client
142 | running, we want to detect whether the user has an slow internet
143 | connection to redirect him to the lite html client (less
144 | functionallity but cover basic with decent speed on poor
145 | connections).
146 |
147 | https://codesandbox.io/s/ajax-checking-usestate-hx02u
148 |
149 | # About Basefactor + Lemoncode
150 |
151 | We are an innovating team of Javascript experts, passionate about turning your ideas into robust products.
152 |
153 | [Basefactor, consultancy by Lemoncode](http://www.basefactor.com) provides consultancy and coaching services.
154 |
155 | [Lemoncode](http://lemoncode.net/services/en/#en-home) provides training services.
156 |
157 | For the LATAM/Spanish audience we are running an Online Front End Master degree, more info: http://lemoncode.net/master-frontend
158 |
--------------------------------------------------------------------------------
/05 block-3/00 async-closure/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "my-app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "react": "^16.9.0",
7 | "react-dom": "^16.9.0",
8 | "react-scripts": "3.1.1"
9 | },
10 | "scripts": {
11 | "start": "react-scripts start",
12 | "build": "react-scripts build",
13 | "test": "react-scripts test",
14 | "eject": "react-scripts eject"
15 | },
16 | "eslintConfig": {
17 | "extends": "react-app"
18 | },
19 | "browserslist": {
20 | "production": [
21 | ">0.2%",
22 | "not dead",
23 | "not op_mini all"
24 | ],
25 | "development": [
26 | "last 1 chrome version",
27 | "last 1 firefox version",
28 | "last 1 safari version"
29 | ]
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/05 block-3/00 async-closure/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lemoncode/react-alicante-hooks-workshop/3770c81812b9e19bdd46f65de969bba686f6f1f4/05 block-3/00 async-closure/public/favicon.ico
--------------------------------------------------------------------------------
/05 block-3/00 async-closure/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/05 block-3/00 async-closure/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lemoncode/react-alicante-hooks-workshop/3770c81812b9e19bdd46f65de969bba686f6f1f4/05 block-3/00 async-closure/public/logo192.png
--------------------------------------------------------------------------------
/05 block-3/00 async-closure/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lemoncode/react-alicante-hooks-workshop/3770c81812b9e19bdd46f65de969bba686f6f1f4/05 block-3/00 async-closure/public/logo512.png
--------------------------------------------------------------------------------
/05 block-3/00 async-closure/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/05 block-3/00 async-closure/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 |
--------------------------------------------------------------------------------
/05 block-3/00 async-closure/src/demo.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | export const MyComponent = () => {
4 | const [message, setMessage] = React.useState("initial message");
5 | const [seconds, setSeconds] = React.useState(0);
6 |
7 | const secondsRef = React.useRef(seconds);
8 |
9 | React.useEffect(() => {
10 | setTimeout(() => {
11 | console.log(seconds);
12 | setSeconds(seconds + 1);
13 | secondsRef.current = seconds + 1;
14 | }, 1000);
15 |
16 | setTimeout(() => {
17 | setMessage(`Total seconds: ${secondsRef.current}`);
18 | }, 2000);
19 | // eslint-disable-next-line react-hooks/exhaustive-deps
20 | }, []);
21 |
22 | return (
23 | <>
24 |
10 | );
11 | }
12 |
13 | const rootElement = document.getElementById('root');
14 | ReactDOM.render(, rootElement);
15 |
--------------------------------------------------------------------------------
/08 Exercise 3/01-implemented/src/use-media-query.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export const useMediaQuery = mediaQuery => {
4 | const [matches, setMatches] = React.useState(window.matchMedia(mediaQuery));
5 |
6 | React.useEffect(() => {
7 | const listener = event => setMatches(event.matches);
8 | window.matchMedia(mediaQuery).addListener(listener);
9 |
10 | return () => {
11 | window.matchMedia(mediaQuery).removeListener(listener);
12 | };
13 | }, [mediaQuery]);
14 |
15 | return matches;
16 | };
17 |
--------------------------------------------------------------------------------
/08 Exercise 3/Readme.md:
--------------------------------------------------------------------------------
1 | # Challenge 3
2 |
3 | ## Starting material:
4 |
5 | CSS media queries are great when we are creating a responsive web design but sometimes, we need to apply same media queries in the Javascript side like "isDesktop ? use Header component else use HamburgerMenu"
6 |
7 | ## Startup
8 |
9 | - Copy `00-start` project folder.
10 | - Run `npm install`.
11 | - Run `npm start` and implement the challenge.
12 |
13 | ## Tips
14 |
15 | A. Play with component feeding `showMenu` props equals true / false.
16 |
17 | B. Create `useMediaQuery` hook. You can use [matchMedia](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia) Javascript method to check if the document matches the media query.
18 |
19 | # About Basefactor + Lemoncode
20 |
21 | We are an innovating team of Front End Developers, passionate about turning your ideas into robust products.
22 |
23 | [Basefactor, consultancy by Lemoncode](http://www.basefactor.com) provides consultancy and coaching services.
24 |
25 | [Lemoncode](http://lemoncode.net/services/en/#en-home) provides training services.
26 |
27 | For the LATAM/Spanish audience we are running an Online Front End Master degree, more info: http://lemoncode.net/master-frontend
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Lemoncode
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-alicante-hooks-workshop
2 |
3 | [React Alicante 2019 Workshop](https://reactalicante.es/).
4 |
5 | # About Basefactor + Lemoncode
6 |
7 | We are an innovating team of Front End Developers, passionate about turning your ideas into robust products.
8 |
9 | [Basefactor, consultancy by Lemoncode](http://www.basefactor.com) provides consultancy and coaching services.
10 |
11 | [Lemoncode](http://lemoncode.net/services/en/#en-home) provides training services.
12 |
13 | For the LATAM/Spanish audience we are running an Online Front End Master degree, more info: http://lemoncode.net/master-frontend
14 |
15 |
--------------------------------------------------------------------------------
/[React Alicante 19] [Workshop] Hooked on Hooks.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lemoncode/react-alicante-hooks-workshop/3770c81812b9e19bdd46f65de969bba686f6f1f4/[React Alicante 19] [Workshop] Hooked on Hooks.pdf
--------------------------------------------------------------------------------