13 |
14 |
15 |
16 | The goal of this tutorial is to quickly get you off the ground with `React` concepts. This tutorial has hands-on exercises which I consider to be the most important part of this tutorial.
17 |
18 | The way this tutorial works is that first, you have to checkout this project on your computer, and run the application locally. Then open the application on a browser and you can go through the tutorial as you like.
19 |
20 | 
21 |
22 |
23 | ## Checkout
24 |
25 | Checkout the project to your computer using `git`:
26 |
27 | ```
28 | git clone https://github.com/tyroprogrammer/learn-react-app.git
29 | ```
30 |
31 | ## Environment Setup
32 |
33 | You can either use `npm` or `yarn` to run this application. Please pick one and follow below instructions.
34 |
35 | *If you want to use `yarn` and don't have `yarn` installed on your local machine please execute below command to install `yarn`:*
36 |
37 | ```
38 | npm install -g yarn
39 | ```
40 |
41 | ### Installing Dependencies
42 |
43 | On the root directory of the project please execute **either one** of the below commands to install all the project dependencies. You don't have to run both commands, just pick one.
44 |
45 | ```
46 | yarn install
47 | ```
48 |
49 | **OR**
50 |
51 | ```
52 | npm install
53 | ```
54 |
55 | ### Starting application
56 |
57 | On the root directory of the project please execute either one of the below commands to start the tutorial application:
58 |
59 | ```
60 | yarn start
61 | ```
62 |
63 | **OR**
64 | ```
65 | npm start
66 | ```
67 |
68 | After this is complete, the application will be deployed on port `3000`. Open a browser and navigate to `localhost:3000`.
69 |
70 | -----
71 |
72 | ## Online Tutorial
73 |
74 | You can also run this tutorial in Gitpod, a free online dev environment for GitHub:
75 |
76 | [](https://gitpod.io/#https://github.com/tyroprogrammer/learn-react-app/blob/master/src/exercise/01-HelloWorld.js)
77 |
78 | -----
79 |
80 | ## Following the tutorial
81 |
82 | Tutorials on this application are fairly straightforward to follow. Each tutorial has one or more exercises. You'll see once you are in the tutorial.
83 |
84 | The exercise panel has split view. The left-hand side of the screen has your solution rendered and the right-hand side of the screen has the target solution.
85 | Right above the exercise panel, you'll see the location of exercise files.
86 |
87 | Please open the exercise file on your favorite editor (VS Code, Atom, Sublime, IntelliJ etc.) and start making changes by following the instructions. Exercise files are heavily commented. Read through the comments and you should be able to write up the solution. If you have any confusion you can refer to the solution file for that exercise. Every time you make changes to the exercise file and save it, the browser will reload automatically reflecting your changes.
88 |
89 | ### Exercise Comment Guide
90 |
91 | Most comments in the exercise files start with one of the below signs. This is to help you understand what you should do to the code immediately following these comments.
92 |
93 | 🏆 - **Trophy** - Describes the overall goal of the exercise. You can find this at the top of the exercise file.
94 |
95 | 💡 - **Light Bulb** - General information regarding the code immediately following this comment. You might find it throughout the code. No action is required on your part, just read them.
96 |
97 | ✏️ - **Pencil** - You are supposed to edit the code immediately following this comment. It is followed by a description of the change that you need to do.
98 |
99 | 🧭 - **Compass** - When the description of change is not enough, the compass will give you more direction. You will find it alongside the pencil when more elaborate instruction is deemed necessary.
100 |
101 | 🚨 - **Alarm** - This means danger. Read the comment carefully. Usually, it's used to say you shouldn't change the code immediately following this. It will create havoc.
102 |
103 | ### FAQ
104 |
105 |
106 | Do I need to install `yarn` or can I use `npm`?
107 |
You don't really need yarn. Just use npm if you like.
108 |
109 |
110 |
111 | Which browser should I use?
112 |
This tutorial has been tested in Chrome only so I highly recommend you use Chrome.
113 |
114 |
115 |
116 | Which code editor should I use for exercise?
117 |
Anything really (Sublime, Atom, VS Code, IntelliJ) - its your preference.
118 |
119 |
120 |
121 | I accidentally deleted something in an exercise that I shouldn't have. What should I do?
122 |
The easiest way is to just revert back to the previous version on your editor. If you want to start anew, then just checkout that particular file from GitHub again using something like:
22 |
23 |
24 | );
25 | }
26 | }
27 |
28 | export default App;
29 |
--------------------------------------------------------------------------------
/src/FileReader.js:
--------------------------------------------------------------------------------
1 | class FileReader {
2 | readFile(fileLocation) {
3 | let _fileLocation = fileLocation.replace('src', '.').replace('.js', '');
4 |
5 | if (fileLocation.includes("exercise") && !fileLocation.includes("solution") && process.env.REACT_APP_TS === "true") {
6 | _fileLocation = fileLocation.replace('src', '.').replace('.js', '_ts');
7 | }
8 |
9 | return import(`${_fileLocation}`)
10 | .then(data => data.default);
11 | }
12 | }
13 |
14 | export default new FileReader();
15 |
--------------------------------------------------------------------------------
/src/api/CompanyDataGenerator.js:
--------------------------------------------------------------------------------
1 | import { sample } from 'lodash';
2 |
3 | const PRICE = { MIN: 25, MAX: 250 };
4 | const BETA = { MIN: 1, MAX: 2 };
5 | const VOLAVG = { MIN: 10000, MAX: 1000000 };
6 | const MKTCAP = { MIN: 10000000, MAX: 50000000000 };
7 | const LASTDIV_PERC = { MIN: 0, MAX: 0.03 };
8 | const RANGESPREAD_PERC = { MIN: 0.02, MAX: 0.05 };
9 | const CHANGESPERC = { MIN: 0.01, MAX: 0.05 };
10 |
11 | const SECTOR_INDUSTRY = {
12 | 'Technology': ['Computer Hardware', 'Online Media', 'SemiConductor', 'Application Software'],
13 | 'Consumer': ['Restaurant', 'Utilities', 'Retail', 'Entertainment', 'Apparel'],
14 | 'Health Care': ['Medical Device'],
15 | 'Industrial': ['Airlines', 'Manufacturing'],
16 | 'Financial Services': ['Brokers & Exchanges', 'Banks']
17 | };
18 |
19 | const EXCHANGE = ['NASDAQ', 'NYSE'];
20 |
21 | class CompanyDataGenerator {
22 | constructor() {
23 |
24 | }
25 |
26 | getPrice() {
27 | return getRandomNumberBetween(PRICE.MIN, PRICE.MAX);
28 | }
29 |
30 | getBeta() {
31 | return getRandomNumberBetween(BETA.MIN, BETA.MAX, true);
32 | }
33 |
34 | getVolAvg() {
35 | return `${getRandomNumberBetween(VOLAVG.MIN, VOLAVG.MAX).toLocaleString('US')}`;
36 | }
37 |
38 | getMktCap() {
39 | return `$${getRandomNumberBetween(MKTCAP.MIN, MKTCAP.MAX).toLocaleString('US')}`;
40 | }
41 |
42 | getLastDiv(price) {
43 | return (price * getRandomNumberBetween(LASTDIV_PERC.MIN, LASTDIV_PERC.MAX, true)).toFixed(2);
44 | }
45 |
46 | getRange(price) {
47 | return `$${(price - price * RANGESPREAD_PERC.MIN).toFixed(2)}
48 | - $${(price + price * RANGESPREAD_PERC.MAX).toFixed(2)}`
49 | }
50 |
51 | getChangePerc() {
52 | return getRandomNumberBetween(CHANGESPERC.MIN, CHANGESPERC.MAX, true);
53 | }
54 |
55 | getChange(price, changePerc) {
56 | return (price * changePerc).toFixed(2);
57 | }
58 |
59 | getCompanyFinancial() {
60 | const Price = this.getPrice();
61 | const Beta = this.getBeta();
62 | const VolAvg = this.getVolAvg();
63 | const MktCap = this.getMktCap();
64 | const LastDiv = this.getLastDiv(Price);
65 | const Range = this.getRange(Price);
66 | const ChangePerc = this.getChangePerc();
67 | const Change = this.getChange(Price, ChangePerc);
68 | return {
69 | Price: `$${Price}`,
70 | Beta,
71 | VolAvg,
72 | MktCap,
73 | LastDiv,
74 | Range,
75 | ChangePerc: `${ChangePerc * 100}%`,
76 | Change
77 | }
78 | }
79 |
80 | getDescription(name, sector) {
81 | const randomYear = getRandomNumberBetween(5, 50);
82 | return `${name} is an excellent company in ${sector} sector for past ${randomYear} years producing outstanding results for it's shareholders.`
83 | }
84 |
85 | getExchange() {
86 | return sample(EXCHANGE);
87 | }
88 |
89 | getIndustry(sector) {
90 | return sample(SECTOR_INDUSTRY[sector]);
91 | }
92 |
93 | getSector() {
94 | return sample(Object.keys(SECTOR_INDUSTRY));
95 | }
96 |
97 | getWebsite(ticker) {
98 | return `http://www.${ticker.toLowerCase()}.com`
99 | }
100 |
101 | getCompanyProfile(ticker) {
102 | const companyName = ticker.toUpperCase();
103 | const exchange = this.getExchange();
104 | const sector = this.getSector();
105 | const industry = this.getIndustry(sector);
106 | const website = this.getWebsite(ticker);
107 | const description = this.getDescription(companyName, sector);
108 | return {
109 | companyName,
110 | description,
111 | exchange,
112 | industry,
113 | sector,
114 | website
115 | }
116 | }
117 | }
118 |
119 | function getRandomNumberBetween(min, max, floatResult) {
120 | if (floatResult) {
121 | return (Math.random() * (max - min) + min).toFixed(2);
122 | }
123 | return Math.floor(min + (max - min + 1) * Math.random());
124 | }
125 |
126 | export default new CompanyDataGenerator();
--------------------------------------------------------------------------------
/src/api/DataApi.js:
--------------------------------------------------------------------------------
1 | import CompanyDataGenerator from './CompanyDataGenerator';
2 |
3 | export default class DataApi {
4 | static async getCompanyProfile(company) {
5 | return new Promise((resolve, reject) => {
6 | if (!company || company.trim().length !== 3) {
7 | reject('Ticker cannot be empty and must be 3 character long');
8 | }
9 | try {
10 | const result = CompanyDataGenerator.getCompanyProfile(company);
11 | resolve(result);
12 | } catch(e) {
13 | console.error(e);
14 | reject(e);
15 | }
16 | })
17 | }
18 |
19 | static async getCompanyFinancial(company) {
20 | return new Promise((resolve, reject) => {
21 | if (!company || company.trim().length !== 3) {
22 | reject('Ticker cannot be empty and must be 3 character long');
23 | }
24 | try {
25 | const result = CompanyDataGenerator.getCompanyFinancial(company);
26 | resolve(result);
27 | } catch(e) {
28 | console.error(e);
29 | reject(e);
30 | }
31 | })
32 | }
33 | }
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/capstone/Capstone.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import isEmpty from 'lodash/isEmpty';
3 |
4 | import Search from './Search';
5 | import CompanyProfile from './CompanyProfile';
6 | import CompanyFinancial from './CompanyFinancial';
7 | import style from './solution/style';
8 |
9 | const EMPTY_TICKER_MSG = 'Please type a stock ticker and click Search button.';
10 |
11 | /**
12 | * 🏆
13 | * The goal of this capstone project is to bring together most of the
14 | * concepts you have learned in this tutorial together by building this feature.
15 | * The feature we are building is pretty straight forward. There's an input
16 | * field and a search button. When the user types in a valid stock ticker and
17 | * clicks Search button, it will display the company profile as well as
18 | * company financial for the given stock ticker selected.
19 | */
20 | class Capstone extends Component {
21 | constructor(props) {
22 | super(props);
23 | //💡 Always initialize the state with whatever you think is appropriate default value
24 | this.state = {
25 | stockTicker: ''
26 | }
27 |
28 | //✏️ Bind the handleSearch function below to appropriate `this`
29 | // this.handleSearch = this.handleSearch.bind(this);
30 | }
31 |
32 | handleSearch(stockTicker) {
33 | /**
34 | * ✏️
35 | * When this method is called, it will be given a stockTicker
36 | * as an argument. You need to call setState to set the stockTicker
37 | * state to the value provided to this function
38 | */
39 | }
40 |
41 | render() {
42 | /**
43 | * ✏️
44 | * We want to render a message if no stock ticker has been searched.
45 | * We want to conditionally render the EMPTY_TICKER_MSG that's already provided
46 | * 🧭 There's an isEmpty function that's already imported. Use that
47 | * to check if the stockTicker state is empty
48 | * Like: isEmpty(this.state.stockTicker)
49 | * 🧭 If it is empty assign
{EMPTY_TICKER_MSG}
to EmptyTickerMessage
50 | * 🧭 If the stockTicker is not empty assign null to EmptyTickerMessage
51 | * You can either use ternery operator -
52 | * const a = isEmpty(b) ? c : null;
53 | * OR you can use '&&' operator -
54 | * const a = isEmpty(b) && c;
55 | * Both ways you are telling EmptyTickerMessage to display the div with the
56 | * error message only when the stockTicker state is empty
57 | */
58 | const EmptyTickerMessage = null;
59 |
60 | /**
61 | * 💡 Some things to note below inside the return:
62 | * 1. We are passing `handleSearch` function to `Search` component as `onSearch` props.
63 | * `Search` component will execute this props when the user clicks Search button.
64 | * and it will pass the current value on the input field as an argument to this function.
65 | * Remember above we updated the state when this function is called
66 | * 2. We are passing `stockTicker` props to both `CompanyProfile` and
67 | * `CompanyFinancial` components. These components should use the `stockTicker`
68 | * props to fetch the appropriate data from the API provided and render it.
69 | * 3. We have a line like {EmptyTickerMessage}. It's the constant we defined above.
70 | * We assigned a JSX to a constant and rendered it here inside curly braces.
71 | * This is a cool thing about JSX. They can be passed around like any object or function or data.
72 | */
73 | return (
74 |
75 |
76 |
77 |
78 | {EmptyTickerMessage}
79 |
80 | );
81 | }
82 | }
83 |
84 | export default Capstone;
--------------------------------------------------------------------------------
/src/capstone/CompanyFinancial.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import isEmpty from 'lodash/isEmpty';
3 |
4 | import DataAPI from '../api/DataApi';
5 | import style from './solution/style';
6 |
7 | const ERROR_MSG = `Error when fetching company financial data. Please check if you entered valid stock ticker`;
8 |
9 | /**
10 | * 🏆
11 | * This component is responsible for displaying the financial of the stock ticker
12 | * provided to it as a `props` by it's parent.
13 | * Here we will see how to fetch data using some lifecycle methods and also potential
14 | * way to handle an error if it were to arise. We will conditionally render different
15 | * things based on some state values.
16 | */
17 | class CompanyFinancial extends Component {
18 | constructor(props) {
19 | super(props);
20 | /**
21 | * 💡
22 | * Initialized the state with empty values
23 | */
24 | this.state = {
25 | companyFinancial: {},
26 | errorMsg: ''
27 | }
28 |
29 | /**
30 | * 💡
31 | * These functions are already bound for you with `this`
32 | */
33 | this.updateCompanyFinancial = this.updateCompanyFinancial.bind(this);
34 | this.handleError = this.handleError.bind(this);
35 | }
36 |
37 | /**
38 | * 💡
39 | * This is a lifecycle method. React will invoke this lifecyle method
40 | * after the component is mounted to the DOM for the first time
41 | */
42 | componentDidMount() {
43 | /**
44 | * ✏️
45 | * You need to fetch the financial data here.
46 | * There's a fetchCompanyFinancial function already defined below
47 | * that will fetch the data from the given API
48 | * You just need to invoke it here
49 | */
50 | }
51 |
52 | /*
53 | * 💡
54 | * This is another lifecycle method. React will invoke this lifecyle method
55 | * after the component is updated. Remember the component will be updated
56 | * every time either the props changes or the state changes for this component
57 | */
58 | componentDidUpdate(prevProps, prevState) {
59 | /**
60 | * ✏️
61 | * You need to call the fetchCompanyFinancial method to fetch the
62 | * comanyFinancial data when the parent passes you a different stockTicker
63 | * 🧭 Remember to check if the stockTicker props changed before calling the
64 | * fetchCompanyFinancial method though. You DON'T want to fetch the data
65 | * if the stockTicker hasn't changed. If you don't check whether props
66 | * changed your component will go in an infinite loop - it will be
67 | * fetching the same data over and over again.
68 | * This lifecycle method will be given multiple arguments.
69 | * First argument is the value of props before this component updated
70 | * Second argument is the value of the state before this component updated
71 | * In our case we just want to check props to see if value for stockTicker
72 | * changed and the way to do this is:
73 | * if (this.props.stockTicker !== prevProps.stockTicker) {
74 | * //Fetch data here only if the current props is not same as previous props
75 | * }
76 | */
77 | }
78 |
79 | /**
80 | * 💡
81 | * This is a method to fetch the company financial data from the API.
82 | * Couple things to note here:
83 | * 1. We are updating the errorMsg state to empty string. This is jus to
84 | * reset any error message we might have from previous search
85 | * 2. We invoke the API only when the stockTicker is truthy. No point in
86 | * calling the API if we don't have any value for stockTicker
87 | * 3. We use the data received from the API to update companyFinancial state
88 | * (look below for updateCompanyFinancial function implementation)
89 | * 4. We catch any Error we get from the API and call handleError method
90 | * to handle the error.
91 | */
92 | fetchCompanyFinancial() {
93 | const { stockTicker } = this.props;
94 | this.updateErrorMsg('');
95 | if (stockTicker) {
96 | DataAPI.getCompanyFinancial(this.props.stockTicker)
97 | .then(this.updateCompanyFinancial)
98 | .catch(this.handleError)
99 | }
100 | }
101 |
102 | /**
103 | * 💡
104 | * Updates the companyFinancial state with the argument provided
105 | */
106 | updateCompanyFinancial(companyFinancial) {
107 | this.setState({ companyFinancial })
108 | }
109 |
110 | /**
111 | * 💡
112 | * Updates the errorMsg state with the argument provided
113 | */
114 | updateErrorMsg(errorMsg) {
115 | this.setState({ errorMsg });
116 | }
117 |
118 | /**
119 | * 💡
120 | * This is used to handle any error that we might get when we call the API
121 | * The API throws an error when for example the stockTicker provided
122 | * is not a valid stockTicker.
123 | */
124 | handleError(error) {
125 | //This sets the state `errorMsg` with the ERROR_MSG defined at the very top
126 | this.updateErrorMsg(ERROR_MSG);
127 | //Since there's an error we want to reset the companyFincial state
128 | //with empty object. We don't want to display stale data
129 | this.updateCompanyFinancial({});
130 | }
131 |
132 | render() {
133 | const { companyFinancial } = this.state;
134 | /**
135 | * ✏️
136 | * We want to render an error message if the API returns some error.
137 | * We want to check if we have `errorMsg` state is not empty and
138 | * if it's not render the message inside a div
139 | * 🧭 There's an `isEmpty` function that's already imported. Use that
140 | * to check if the `errorMsg` state is empty
141 | * Like: isEmpty(this.state.errorMsg)
142 | * 🧭 If it is empty assign null to ErrorMsg
143 | * 🧭 If the errorMsg is not empty assign
{this.state.errorMsg}
144 | * to the ErrorMsg constant.
145 | * You can either use ternery operator -
146 | * const a = isEmpty(b) ? c : null;
147 | * OR you can use '&&' operator -
148 | * const a = isEmpty(b) && c;
149 | * Either ways you are telling ErrorMsg to display the div with the
150 | * error message only when the `erroMsg` state is not empty
151 | */
152 | const ErrorMsg = null;
153 |
154 | /**
155 | * 💡
156 | * Here we are doing same thing as the ErrorMsg above
157 | * Instead of checking for `errorMsg` we are checking for `companyFinancial`
158 | * state. We are displaying the `div` only if the `companyFinancial`
159 | * state is not empty.
160 | */
161 | const CompanyFinancialSection = (
162 | !isEmpty(this.state.companyFinancial) &&
163 |
188 | );
189 | }
190 | }
191 |
192 | export default CompanyFinancial;
--------------------------------------------------------------------------------
/src/capstone/CompanyProfile.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import isEmpty from 'lodash/isEmpty';
3 | import DataAPI from '../api/DataApi';
4 | import style from './solution/style';
5 |
6 | const ERROR_MSG = `Error when fetching company profile data. Please check if you entered valid stock ticker`;
7 |
8 | /**
9 | * 🏆
10 | * This component is responsible for displaying the Compnay Profile of the stock ticker
11 | * provided to it as a `props` by it's parent.
12 | * Here we will see how to fetch data using some lifecycle methods and also potential
13 | * way to handle an error if it were to arise. We will conditionally render different
14 | * things based on some state values.
15 | */
16 | class CompanyProfile extends Component {
17 | constructor(props) {
18 | super(props);
19 | /**
20 | * 💡
21 | * Initialized the state with empty values
22 | */
23 | this.state = {
24 | companyProfile: {},
25 | errorMsg: ''
26 | }
27 |
28 | /**
29 | * 💡
30 | * These functions are already bound for you with `this`
31 | */
32 | this.updateCompanyProfile = this.updateCompanyProfile.bind(this);
33 | this.updateErrorMsg = this.updateErrorMsg.bind(this);
34 | this.handleError = this.handleError.bind(this);
35 | }
36 |
37 | /**
38 | * 💡
39 | * This is a lifecycle method. React will invoke this lifecyle method
40 | * after the component is mounted to the DOM for the first time
41 | */
42 | componentDidMount() {
43 | /**
44 | * ✏️
45 | * You need to fetch the profile data here.
46 | * There's a fetchCompanyProfile function already defined below
47 | * that will fetch the data from the given API
48 | * You just need to invoke it here
49 | */
50 | }
51 |
52 | /**
53 | * 💡
54 | * This is another lifecycle method. React will invoke this lifecyle method
55 | * after the component is updated. Remember the component will be updated
56 | * every time either the props changes or the state changes for this component
57 | */
58 | componentDidUpdate(prevProps, prevState) {
59 | /**
60 | * ✏️
61 | * You need to call the fetchCompanyProfile method to fetch the
62 | * comanyProfile data when the parent passes you a different stockTicker
63 | * 🧭 Remember to check if the stockTicker props changed before calling the
64 | * fetchCompanyProfile method though. You DON'T want to fetch the data
65 | * if the stockTicker hasn't changed. If you don't check whether props
66 | * changed your component will go in an infinite loop - it will be
67 | * fetching the same data over and over again.
68 | * This lifecycle method will be given multiple arguments.
69 | * First argument is the value of props before this component updated
70 | * Second argument is the value of the state before this component updated
71 | * In our case we just want to check props to see if value for stockTicker
72 | * changed and the way to do this is:
73 | * if (this.props.stockTicker !== prevProps.stockTicker) {
74 | * //Fetch data here only if the current props is not same as previous props
75 | * }
76 | */
77 | }
78 |
79 | /**
80 | * 💡
81 | * This is a method to fetch the company profile data from the API.
82 | * Couple things to note here:
83 | * 1. We are updating the errorMsg state to empty string. This is jus to
84 | * reset any error message we might have from previous search
85 | * 2. We invoke the API only when the stockTicker is truthy. No point in
86 | * calling the API if we don't have any value for stockTicker
87 | * 3. We use the data received from the API to update companyProfile state
88 | * (look below for updateCompanyProfile function implementation)
89 | * 4. We catch any Error we get from the API and call handleError method
90 | * to handle the error.
91 | */
92 | fetchCompanyProfile() {
93 | const { stockTicker } = this.props;
94 | this.updateErrorMsg('');
95 | if (stockTicker) {
96 | DataAPI.getCompanyProfile(this.props.stockTicker)
97 | .then(this.updateCompanyProfile)
98 | .catch(this.handleError)
99 | }
100 | }
101 |
102 | /**
103 | * 💡
104 | * Updates the companyProfile state with the argument provided
105 | */
106 | updateCompanyProfile(companyProfile) {
107 | this.setState({ companyProfile });
108 | }
109 |
110 | /**
111 | * 💡
112 | * Updates the errorMsg state with the argument provided
113 | */
114 | updateErrorMsg(errorMsg) {
115 | this.setState({ errorMsg });
116 | }
117 |
118 | /**
119 | * 💡
120 | * This is used to handle any error that we might get when we call the API
121 | * The API throws an error when for example the stockTicker provided
122 | * is not a valid stockTicker.
123 | */
124 | handleError(error) {
125 | //This sets the state `errorMsg` with the ERROR_MSG defined at the very top
126 | this.updateErrorMsg(ERROR_MSG);
127 | //Since there's an error we want to reset the `companyProfile` state
128 | //with empty object. We don't want to display stale data
129 | this.updateCompanyProfile({});
130 | }
131 |
132 | render() {
133 | const { companyProfile } = this.state;
134 | const { description, ...rest } = companyProfile;
135 | /**
136 | * ✏️
137 | * We want to render an error message if the API returns some error.
138 | * We want to check if we have `errorMsg` state is not empty and
139 | * if it's not render the message inside a div
140 | * 🧭 There's an `isEmpty` function that's already imported. Use that
141 | * to check if the `errorMsg` state is empty
142 | * Like: isEmpty(this.state.errorMsg)
143 | * 🧭 If it is empty assign null to ErrorMsg
144 | * 🧭 If the errorMsg is not empty assign
{this.state.errorMsg}
145 | * to the ErrorMsg constant.
146 | * You can either use ternery operator -
147 | * const a = isEmpty(b) ? c : null;
148 | * OR you can use '&&' operator -
149 | * const a = isEmpty(b) && c;
150 | * Either ways you are telling ErrorMsg to display the div with the
151 | * error message only when the `erroMsg` state is not empty
152 | */
153 | const ErrorMsg = null;
154 |
155 | /**
156 | * 💡
157 | * Here we are doing same thing as the ErrorMsg above
158 | * Instead of checking for `errorMsg` we are checking for `companyProfile`
159 | * state. We are displaying the `div` only if the `companyProfile`
160 | * state is not empty.
161 | */
162 |
163 | const CompanyProfileSection = (
164 | !isEmpty(this.state.companyProfile) &&
165 |
189 | );
190 | }
191 | }
192 |
193 | export default CompanyProfile;
--------------------------------------------------------------------------------
/src/capstone/Search.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import style from './solution/style';
3 |
4 | /**
5 | * 🏆
6 | * This component houses the input and the Search button.
7 | * When the user types in something we handle the change event and
8 | * store the values typed in the input field to the state
9 | * When user clicks the Search button it will invoke a callback function
10 | * that was passed to this component as a props with the latest input value
11 | * as an argument
12 | */
13 | class Search extends Component {
14 | constructor(props) {
15 | super(props);
16 | /**
17 | * Initialized state here with default empty string
18 | */
19 | this.state = {
20 | stockTicker: ''
21 | }
22 |
23 | /**
24 | * Functions are already bound here for you.
25 | */
26 | this.handleInputChange = this.handleInputChange.bind(this);
27 | this.handleSearch = this.handleSearch.bind(this);
28 | }
29 |
30 | handleInputChange(e) {
31 | /**
32 | * ✏️
33 | * You need to get the value typed on the input and use that to update
34 | * the `stockTicker` state
35 | *🧭 You can get the value of the input using the SyntheticEvent passed
36 | * to this function as argument. e.target.value here will give you that value
37 | *🧭 Set this value to the state `stockTicker`
38 | */
39 | }
40 |
41 | handleSearch() {
42 | /**
43 | * ✏️
44 | * You need to invoke the `handleSearch` props passed by the parent
45 | * Pass the latest `stateTicker` state when you invoke that function
46 | * this.props.onSearch(this.state.stockTicker)
47 | */
48 | }
49 |
50 | render() {
51 | const { stockTicker } = this.state;
52 | return (
53 |
31 | );
32 | }
33 | }
34 |
35 | export default Search;
--------------------------------------------------------------------------------
/src/capstone/solution/style.js:
--------------------------------------------------------------------------------
1 | export default {
2 | container: {
3 | fontSize: '0.9rem',
4 | margin: 10
5 | },
6 | searchInput: {
7 | padding: 9
8 | },
9 | searchContainer: {
10 | padding: `0px 0px 10px 0px`
11 | },
12 | financialTitle: {
13 | fontWeight: 'bold',
14 | borderBottom: '1px solid gray',
15 | padding: 10,
16 | backgroundColor: `darkgray`
17 | },
18 | financialContent: {
19 | padding: 10
20 | },
21 | financialContainer: {
22 | border: `1px solid gray`,
23 | },
24 | financialMetricRow: {
25 | display: 'flex',
26 | justifyContent: 'flext-start'
27 | },
28 | financialMetric: {
29 | width: `30%`,
30 | fontWeight: 'bold'
31 | },
32 | financialMetricValue: {
33 |
34 | },
35 | profileContainer: {
36 | display: 'flex',
37 | justifyContent: 'flex-start',
38 | flexWrap: 'wrap'
39 | },
40 | profileDescription: {
41 | border: `1px solid gray`,
42 | padding: 10,
43 | },
44 | profileAttrTitle: {
45 | fontWeight: 'bold',
46 | background: 'cadetblue',
47 | color: 'white',
48 | padding: `5px 0px 5px 5px`
49 | },
50 | profileAttrContainer: {
51 | width: '33%',
52 | padding: `10px 10px 10px 0px`
53 | }
54 | }
--------------------------------------------------------------------------------
/src/exercise/01-HelloWorld.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | /**
4 | *🏆
5 | * The goal here is just to say Hello World.
6 | * Follow the instruction inside return statement
7 | */
8 | function HelloWorld(props) {
9 | return (
10 | /**
11 | * ✏️
12 | * Instead of returning null you would need to return a React element
13 | * Use the React.createElement function to display a div
14 | * and Hello World text inside the div
15 | */
16 | null
17 | );
18 | }
19 |
20 | /**
21 | * 🚨 🚨 DO NOT DELETE OR CHANGE THIS.🚨 🚨
22 | * This is how you would use your above component and
23 | * the output of this code is displayed on the browser
24 | */
25 | const Usage = (props) => {
26 | return
27 | }
28 |
29 | export default Usage;
30 |
31 |
--------------------------------------------------------------------------------
/src/exercise/01-HelloWorld_ts.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | /**
4 | *🏆
5 | * The goal here is just to say Hello World.
6 | * Follow the instruction inside return statement
7 | */
8 | function HelloWorld(props) {
9 | return (
10 | /**
11 | * ✏️
12 | * Instead of returning null you would need to return a React element
13 | * Use the React.createElement function to display a div
14 | * and Hello World text inside the div
15 | */
16 | null
17 | );
18 | }
19 |
20 | /**
21 | * 🚨 🚨 DO NOT DELETE OR CHANGE THIS.🚨 🚨
22 | * This is how you would use your above component and
23 | * the output of this code is displayed on the browser
24 | */
25 | const Usage = (props) => {
26 | return
27 | }
28 |
29 | export default Usage;
30 |
--------------------------------------------------------------------------------
/src/exercise/02-IntroToJSX.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | /**
4 | *🏆
5 | * The goal here is just to say Hello World.
6 | * It is similar to previous exercise we did except instead of using
7 | * React.createElement function we want to use JSX
8 | */
9 | function HelloWorld(props){
10 | return (
11 | /**
12 | * ✏️
13 | * Instead of returning null you would need to return a React element
14 | * Unlike earlier exercise where you returned React.createElement
15 | * here you should use JSX to return a div with 'Hello World'
16 | */
17 | null
18 | );
19 | }
20 |
21 | /**
22 | * 🚨 🚨 DO NOT DELETE OR CHANGE THIS.🚨 🚨
23 | * This is how you would use your above component and
24 | * the output of this code is displayed on the browser
25 | */
26 | const Usage = (props) => {
27 | return
28 | }
29 |
30 | export default Usage;
31 |
32 |
--------------------------------------------------------------------------------
/src/exercise/02-IntroToJSX_ts.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | /**
4 | *🏆
5 | * The goal here is just to say Hello World.
6 | * It is similar to previous exercise we did except instead of using
7 | * React.createElement function we want to use JSX
8 | */
9 | function HelloWorld(props){
10 | return (
11 | /**
12 | * ✏️
13 | * Instead of returning null you would need to return a React element
14 | * Unlike earlier exercise where you returned React.createElement
15 | * here you should use JSX to return a div with 'Hello World'
16 | */
17 | null
18 | );
19 | }
20 |
21 | /**
22 | * 🚨 🚨 DO NOT DELETE OR CHANGE THIS.🚨 🚨
23 | * This is how you would use your above component and
24 | * the output of this code is displayed on the browser
25 | */
26 | const Usage = (props) => {
27 | return
28 | }
29 |
30 | export default Usage;
31 |
32 |
--------------------------------------------------------------------------------
/src/exercise/03-PowerOfJSX.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | /**
4 | *🏆
5 | * The goal here is to get you more familiar with JSX.
6 | * You will use javascript code inside JSX to loop through object keys
7 | * and render a div element for each element in that object
8 | */
9 | function CompanyProfile(props) {
10 | /**
11 | * 💡 some variables to store mock data
12 | * We will use these data to display the company profile information
13 | */
14 | const stockTicker = 'AAPL';
15 | const companyProfileInfo = {
16 | 'Company Name': 'Apple Inc.',
17 | 'Price': 150,
18 | 'Exchange': "Nasdaq Global Select",
19 | 'Industry': "Computer Hardware",
20 | 'CEO': 'Timothy D. Cook'
21 | }
22 |
23 | return (
24 |
25 |
Profile of: {/**✏️ display stock ticker here*/}
26 |
27 |
28 | {
29 | /**
30 | * ✏️
31 | * This block is surrounded by curly braces {} so
32 | * we can really execute any Javascript stuff here.
33 | *
34 | * Loop through the keys of companyProfileInfo
35 | * object to render one div per key/value pair. The div should
36 | * render key followed by a colon followed by value.
37 | *
38 | * 🧭 Object.keys(obj) can be used to loop through the object
39 | * eg:
40 | * const obj = { 'key1': 'value1', 'key2': 'value2'};
41 | * Object.keys(obj) will return ['key1', 'key2']
42 | * 🧭 You can use Array.map() to map any key to a div element
43 | * eg:
44 | * ['a', 'b', 'c'].map(d =>
{d}
)
45 | * 🧭 Remember to use curly braces inside the div to render
46 | * any text content you want
47 | */
48 | }
49 |
50 |
51 | );
52 | }
53 |
54 | /**
55 | * 🚨 🚨 DO NOT DELETE OR CHANGE THIS.🚨 🚨
56 | * This is how you would use your above component and
57 | * the output of this code is displayed on the browser
58 | */
59 | const Usage = (props) => {
60 | return
61 | }
62 |
63 | export default Usage;
64 |
--------------------------------------------------------------------------------
/src/exercise/03-PowerOfJSX_ts.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | /**
4 | *🏆
5 | * The goal here is to get you more familiar with JSX.
6 | * You will use javascript code inside JSX to loop through object keys
7 | * and render a div element for each element in that object
8 | */
9 | function CompanyProfile(props) {
10 | /**
11 | * 💡 some variables to store mock data
12 | * We will use these data to display the company profile information
13 | */
14 | const stockTicker = 'APPL';
15 | const companyProfileInfo = {
16 | 'Company Name': 'Apple Inc.',
17 | 'Price': 150,
18 | 'Exchange': "Nasdaq Global Select",
19 | 'Industry': "Computer Hardware",
20 | 'CEO': 'Timothy D. Cook'
21 | }
22 |
23 | return (
24 |
25 |
Profile of: {/**✏️ display stock ticker here*/}
26 |
27 |
28 | {
29 | /**
30 | * ✏️
31 | * This block is surrounded by curly braces {} so
32 | * we can really execute any Javascript stuff here.
33 | *
34 | * Loop through the keys of companyProfileInfo
35 | * object to render one div per key/value pair. The div should
36 | * render key followed by a colon followed by value.
37 | *
38 | * 🧭 Object.keys(obj) can be used to loop through the object
39 | * eg:
40 | * const obj = { 'key1': 'value1', 'key2': 'value2'};
41 | * Object.keys(obj) will return ['key1', 'key2']
42 | * 🧭 You can use Array.map() to map any key to a div element
43 | * eg:
44 | * ['a', 'b', 'c'].map(d =>
{d}
)
45 | * 🧭 Remember to use curly braces inside the div to render
46 | * any text content you want
47 | */
48 | }
49 |
50 |
51 | );
52 | }
53 |
54 | /**
55 | * 🚨 🚨 DO NOT DELETE OR CHANGE THIS.🚨 🚨
56 | * This is how you would use your above component and
57 | * the output of this code is displayed on the browser
58 | */
59 | const Usage = (props) => {
60 | return
61 | }
62 |
63 | export default Usage;
64 |
--------------------------------------------------------------------------------
/src/exercise/04-Props.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | /**
4 | * 🏆
5 | * We are trying to make `CompanyProfile` component reusable
6 | * Unlike previous exercise where we had `stockTicker` and `companyProfileInfo`
7 | * hard-coded inside this component itself, we are now getting it as a props
8 | */
9 | function CompanyProfile(props) {
10 | /**
11 | * Where do we get stockTicker and companyProfileInfo from?
12 | * Well we get it as "props". The user of this component will
13 | * pass the value for these two variables.
14 | */
15 | const stockTicker = ''; //✏️ Instead of empty string we want to get this value from props
16 | const companyProfileInfo = {}; //✏️ Instead of empty object we want to get this value from props
17 | return (
18 |
30 | );
31 | }
32 |
33 | function FBCompanyProfile() {
34 | /**
35 | * We need to pass these data to the `CompanyProfile` component
36 | * as the props
37 | */
38 | const stockTicker = 'FB';
39 | const companyProfileInfo = {
40 | 'Company Name': 'Facebook',
41 | 'Price': 150,
42 | 'Exchange': "Nasdaq Global Select",
43 | 'Industry': "Computer Software",
44 | 'CEO': 'Mark Zuckerberg'
45 | }
46 | /**
47 | * ✏️ need to pass the props to the `CompanyProfile` component
48 | * we need to pass `stockTicker` and `companyProfileInfo`
49 | * */
50 | return (
51 |
52 | )
53 | }
54 |
55 | /**
56 | * 🚨 🚨 DO NOT DELETE OR CHANGE THIS.🚨 🚨
57 | * This is how you would use your above component and
58 | * the output of this code is displayed on the browser
59 | */
60 | const Usage = (props) => {
61 | return
62 | }
63 |
64 | export default Usage;
65 |
--------------------------------------------------------------------------------
/src/exercise/04-Props_ts.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | /**
4 | * 🏆
5 | * We are trying to make `CompanyProfile` component reusable
6 | * Unlike previous exercise where we had `stockTicker` and `companyProfileInfo`
7 | * hard-coded inside this component itself, we are now getting it as a props
8 | */
9 | function CompanyProfile(props) {
10 | /**
11 | * Where do we get stockTicker and companyProfileInfo from?
12 | * Well we get it as "props". The user of this component will
13 | * pass the value for these two variables.
14 | */
15 | const stockTicker = ''; //✏️ Instead of empty string we want to get this value from props
16 | const companyProfileInfo = {}; //✏️ Instead of empty object we want to get this value from props
17 | return (
18 |
30 | );
31 | }
32 |
33 | function FBCompanyProfile() {
34 | /**
35 | * We need to pass these data to the `CompanyProfile` component
36 | * as the props
37 | */
38 | const stockTicker = 'FB';
39 | const companyProfileInfo = {
40 | 'Company Name': 'Facebook',
41 | 'Price': 150,
42 | 'Exchange': "Nasdaq Global Select",
43 | 'Industry': "Computer Software",
44 | 'CEO': 'Mark Zuckerberg'
45 | }
46 | /**
47 | * ✏️ need to pass the props to the `CompanyProfile` component
48 | * we need to pass `stockTicker` and `companyProfileInfo`
49 | * */
50 | return (
51 |
52 | )
53 | }
54 |
55 | /**
56 | * 🚨 🚨 DO NOT DELETE OR CHANGE THIS.🚨 🚨
57 | * This is how you would use your above component and
58 | * the output of this code is displayed on the browser
59 | */
60 | const Usage = (props) => {
61 | return
62 | }
63 |
64 | export default Usage;
65 |
--------------------------------------------------------------------------------
/src/exercise/05-State.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | /**
4 | * 🏆
5 | * Here we have a Counter component that display the current value of the counter
6 | * It also has two buttons to increase ('+') or decrease('-') the counter.
7 | * The counter value will be stored in the state.
8 | * You need to update the state to add 1 to the counter when
9 | * "+" is clicked and substract 1 to the current when "-" is clicked
10 | */
11 | class Counter extends Component {
12 | constructor(props){
13 | super(props);
14 | /**
15 | * ✏️
16 | * Initialize a state here with initial value of counter set to 0
17 | * this.state = { counter: defaultValue }
18 | */
19 | this.state = {};
20 |
21 | /**
22 | * 💡
23 | * We are binding the methods here, don't worry about this right now
24 | * We will look at why we do this later in the tutorial
25 | */
26 | this.increment = this.increment.bind(this);
27 | this.decrement = this.decrement.bind(this);
28 | }
29 |
30 | /**
31 | *💡
32 | * This method will be called when the user clicks "+" button to increase the counter
33 | */
34 | increment(){
35 | /**
36 | * ✏️
37 | * You need to call setState here to update the `counter` state
38 | * When user clicks the "+" we need to add 1 to the current state and
39 | * set the state with the new value.
40 | * We need to use value of current state to derive the new state,
41 | * so it's better to use the updater function like
42 | * this.setState(function(currentState) {
43 | * return newState
44 | * });
45 | */
46 | }
47 |
48 | /**
49 | *💡
50 | * This method will be called when the user clicks "-" button to decrease the counter
51 | */
52 | decrement(){
53 | /**
54 | * ✏️
55 | * You need to call setState here to update the `counter` state
56 | * When user clicks the "-" we need to subtract 1 to the current state and
57 | * set the state with the new value.
58 | * We need to use value of current state to derive the new state,
59 | * so it's better for us to use the updater function like
60 | * this.setState(function(currentState) {
61 | * return newState
62 | * });
63 | */
64 | }
65 |
66 | render() {
67 | return (
68 |
69 |
71 | -
72 |
73 |
74 | {this.state.counter}
75 |
76 |
78 | +
79 |
80 |
81 | );
82 | }
83 | }
84 |
85 | /**
86 | * 💡
87 | * This is just some styling used
88 | * You don't need to worry about this or change this
89 | */
90 | const style = {
91 | container: {
92 | display: 'flex'
93 | },
94 | buttons: {
95 | padding: `0px 7px 0px 7px`,
96 | backgroundColor: 'grey',
97 | cursor: 'pointer'
98 | },
99 | counter: {
100 | padding: `0px 7px 0px 7px`
101 | }
102 | }
103 |
104 |
105 | /**
106 | * 🚨 🚨 DO NOT DELETE OR CHANGE THIS.🚨 🚨
107 | * This is how you would use your above component and
108 | * the output of this code is displayed on the browser
109 | */
110 | const Usage = (props) => {
111 | return
112 | };
113 |
114 | export default Usage;
--------------------------------------------------------------------------------
/src/exercise/05-State_ts.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | /**
4 | * 🏆
5 | * Here we have a Counter component that display the current value of the counter
6 | * It also has two buttons to increase ('+') or decrease('-') the counter.
7 | * The counter value will be stored in the state.
8 | * You need to update the state to add 1 to the counter when
9 | * "+" is clicked and substract 1 to the current when "-" is clicked
10 | */
11 | class Counter extends Component {
12 | constructor(props){
13 | super(props);
14 | /**
15 | * ✏️
16 | * Initialize a state here with initial value of counter set to 0
17 | * this.state = { counter: defaultValue }
18 | */
19 | this.state = {};
20 |
21 | /**
22 | * 💡
23 | * We are binding the methods here, don't worry about this right now
24 | * We will look at why we do this later in the tutorial
25 | */
26 | this.increment = this.increment.bind(this);
27 | this.decrement = this.decrement.bind(this);
28 | }
29 |
30 | /**
31 | *💡
32 | * This method will be called when the user clicks "+" button to increase the counter
33 | */
34 | increment(){
35 | /**
36 | * ✏️
37 | * You need to call setState here to update the `counter` state
38 | * When user clicks the "+" we need to add 1 to the current state and
39 | * set the state with the new value.
40 | * We need to use value of current state to derive the new state,
41 | * so it's better to use the updater function like
42 | * this.setState(function(currentState) {
43 | * return newState
44 | * });
45 | */
46 | }
47 |
48 | /**
49 | *💡
50 | * This method will be called when the user clicks "-" button to decrease the counter
51 | */
52 | decrement(){
53 | /**
54 | * ✏️
55 | * You need to call setState here to update the `counter` state
56 | * When user clicks the "-" we need to subtract 1 to the current state and
57 | * set the state with the new value.
58 | * We need to use value of current state to derive the new state,
59 | * so it's better for us to use the updater function like
60 | * this.setState(function(currentState) {
61 | * return newState
62 | * });
63 | */
64 | }
65 |
66 | render() {
67 | return (
68 |
69 |
71 | -
72 |
73 |
74 | {this.state.counter}
75 |
76 |
78 | +
79 |
80 |
81 | );
82 | }
83 | }
84 |
85 | /**
86 | * 💡
87 | * This is just some styling used
88 | * You don't need to worry about this or change this
89 | */
90 | const style = {
91 | container: {
92 | display: 'flex'
93 | },
94 | buttons: {
95 | padding: `0px 7px 0px 7px`,
96 | backgroundColor: 'grey',
97 | cursor: 'pointer'
98 | },
99 | counter: {
100 | padding: `0px 7px 0px 7px`
101 | }
102 | }
103 |
104 |
105 | /**
106 | * 🚨 🚨 DO NOT DELETE OR CHANGE THIS.🚨 🚨
107 | * This is how you would use your above component and
108 | * the output of this code is displayed on the browser
109 | */
110 | const Usage = (props) => {
111 | return
112 | };
113 |
114 | export default Usage;
115 |
--------------------------------------------------------------------------------
/src/exercise/06-LifecycleMethods.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import DataAPI from '../api/DataApi';
3 |
4 | /**
5 | *🏆
6 | * This component is similar to what we looked at for the exercise of `Understanding Props`
7 | * section (./04-Props.js) but there are some differences.
8 | * We expect the user of this component to pass `stockTicker` as props.
9 | * But one difference here compared to earlier exercise is that we are not expecting `companyProfileInfo`
10 | * to be passed as props. Instead we have an API that we would like to use to
11 | * fetch the data from. It has a method called `getCompanyProfile(ticker)`
12 | * that will return you `companyProfile` for given ticker.
13 | * The goal is to help you understand some use cases where we might want to use
14 | * some of the React lifecycle methods
15 | */
16 | class CompanyProfile extends Component {
17 | constructor(props) {
18 | super(props);
19 | /**
20 | * We have initialized the state with companyProfileInformation
21 | */
22 | this.state = {
23 | companyProfileInfo: {}
24 | }
25 | }
26 |
27 | /**
28 | * ✏️
29 | * We need to use componentDidMount lifecycle method to fetch company profile
30 | * information for given stock ticker using the DataAPI provided
31 | * 🧭 Add lifecycle method called componentDidMount
32 | * 🧭 Inside that method you need to use the DataAPI that's already imported.
33 | * Make a call to `getCompanyProfile()` method and pass the `stockTicker` from the props.
34 | * This method will return a promise that resolves into `companyProfile` info
35 | * 🧭 Using the data from the promise use `setState` to set companyProfileInfo
36 | * like - `this.setState({ companyProfileInfo: data })`
37 | * 🧭 What if the promise resolves into an error? You might want to catch the error
38 | * and do something with it (Remember .catch in Promise). For example below I'm
39 | * catching an error and just logging it in console. You can do the same for the
40 | * sake of this exercise:
41 | * Api.getData()
42 | * .then(data => doSth(data))
43 | * .catch(error => console.log(error))
44 | * */
45 | componentDidMount() {
46 |
47 | }
48 |
49 | render() {
50 | const stockTicker = this.props.stockTicker;
51 | const companyProfileInfo = this.state.companyProfileInfo;
52 | return (
53 |
65 | );
66 | }
67 | }
68 |
69 | /**
70 | * 🚨 🚨 DO NOT DELETE OR CHANGE THIS.🚨 🚨
71 | * This is how you would use your above component and
72 | * the output of this code is displayed on the browser
73 | */
74 | const Usage = (props) => {
75 | return
76 | }
77 |
78 | export default Usage;
--------------------------------------------------------------------------------
/src/exercise/06-LifecycleMethods_ts.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import DataAPI from '../api/DataApi';
3 |
4 | /**
5 | *🏆
6 | * This component is similar to what we looked at for the exercise of `Understanding Props`
7 | * section (./04-Props.js) but there are some differences.
8 | * We expect the user of this component to pass `stockTicker` as props.
9 | * But one difference here compared to earlier exercise is that we are not expecting `companyProfileInfo`
10 | * to be passed as props. Instead we have an API that we would like to use to
11 | * fetch the data from. It has a method called `getCompanyProfile(ticker)`
12 | * that will return you `companyProfile` for given ticker.
13 | * The goal is to help you understand some use cases where we might want to use
14 | * some of the React lifecycle methods
15 | */
16 | class CompanyProfile extends Component {
17 | constructor(props) {
18 | super(props);
19 | /**
20 | * We have initialized the state with companyProfileInformation
21 | */
22 | this.state = {
23 | companyProfileInfo: {}
24 | }
25 | }
26 |
27 | /**
28 | * ✏️
29 | * We need to use componentDidMount lifecycle method to fetch company profile
30 | * information for given stock ticker using the DataAPI provided
31 | * 🧭 Add lifecycle method called componentDidMount
32 | * 🧭 Inside that method you need to use the DataAPI that's already imported.
33 | * Make a call to `getCompanyProfile()` method and pass the `stockTicker` from the props.
34 | * This method will return a promise that resolves into `companyProfile` info
35 | * 🧭 Using the data from the promise use `setState` to set companyProfileInfo
36 | * like - `this.setState({ companyProfileInfo: data })`
37 | * 🧭 What if the promise resolves into an error? You might want to catch the error
38 | * and do something with it (Remember .catch in Promise). For example below I'm
39 | * catching an error and just logging it in console. You can do the same for the
40 | * sake of this exercise:
41 | * Api.getData()
42 | * .then(data => doSth(data))
43 | * .catch(error => console.log(error))
44 | * */
45 | componentDidMount() {
46 |
47 | }
48 |
49 | render() {
50 | const stockTicker = this.props.stockTicker;
51 | const companyProfileInfo = this.state.companyProfileInfo;
52 | return (
53 |
65 | );
66 | }
67 | }
68 |
69 | /**
70 | * 🚨 🚨 DO NOT DELETE OR CHANGE THIS.🚨 🚨
71 | * This is how you would use your above component and
72 | * the output of this code is displayed on the browser
73 | */
74 | const Usage = (props) => {
75 | return
76 | }
77 |
78 | export default Usage;
79 |
--------------------------------------------------------------------------------
/src/exercise/07-HandlingEvents.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | /**
4 | *🏆
5 | * The goal of this exercise is to get you more familiar with event handling
6 | * in React. Here we would render an input element and add function to handle
7 | * events on that input element
8 | */
9 | class FancyInput extends Component {
10 | constructor(props) {
11 | super(props);
12 | /**
13 | * 💡 Here we have initialized the state with inputValue
14 | */
15 | this.state = {
16 | inputValue: ''
17 | }
18 |
19 | /**
20 | * ✏️
21 | * Need to bind the handleChange function to appropriate `this`
22 | */
23 | }
24 |
25 | /**
26 | * ✏️
27 | * Need to get the value of the input and set it to the state
28 | * 🧭 Get the value of the input from the synthetic event
29 | * You can get the value by using event.target.value.
30 | * 🧭 Set the value to the state `inputValue` by calling `setState`
31 | */
32 | handleChange(e) {
33 |
34 | }
35 |
36 | render() {
37 |
38 | return (
39 |
40 | {
41 | /**
42 | * ✏️
43 | * Need to pass the event handler to the input element.
44 | * In this case we need to pass handleChange function to the
45 | * onChange event
46 | */
47 | }
48 |
49 | {
50 | /**
51 | * 💡
52 | * This div will mirror the user input. For this to work though
53 | * you need to add the handleChange event on the input above
54 | * and update the state when the change happens on the input
55 | */
56 | }
57 |
You typed: {this.state.inputValue}
58 |
59 |
60 | )
61 | }
62 | }
63 |
64 | /**
65 | * 🚨 🚨 DO NOT DELETE OR CHANGE THIS.🚨 🚨
66 | * This is how you would use your above component
67 | * The output of this code is displayed on the browser on the left hand side
68 | */
69 | const Usage = (props) => {
70 | return
71 | }
72 |
73 | export default Usage;
--------------------------------------------------------------------------------
/src/exercise/07-HandlingEvents_ts.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | /**
4 | *🏆
5 | * The goal of this exercise is to get you more familiar with event handling
6 | * in React. Here we would render an input element and add function to handle
7 | * events on that input element
8 | */
9 | class FancyInput extends Component {
10 | constructor(props) {
11 | super(props);
12 | /**
13 | * 💡 Here we have initialized the state with inputValue
14 | */
15 | this.state = {
16 | inputValue: ''
17 | }
18 |
19 | /**
20 | * ✏️
21 | * Need to bind the handleChange function to appropriate `this`
22 | */
23 | }
24 |
25 | /**
26 | * ✏️
27 | * Need to get the value of the input and set it to the state
28 | * 🧭 Get the value of the input from the synthetic event
29 | * You can get the value by using event.target.value.
30 | * 🧭 Set the value to the state `inputValue` by calling `setState`
31 | */
32 | handleChange(e) {
33 |
34 | }
35 |
36 | render() {
37 |
38 | return (
39 |
40 | {
41 | /**
42 | * ✏️
43 | * Need to pass the event handler to the input element.
44 | * In this case we need to pass handleChange function to the
45 | * onChange event
46 | */
47 | }
48 |
49 | {
50 | /**
51 | * 💡
52 | * This div will mirror the user input. For this to work though
53 | * you need to add the handleChange event on the input above
54 | * and update the state when the change happens on the input
55 | */
56 | }
57 |
You typed: {this.state.inputValue}
58 |
59 |
60 | )
61 | }
62 | }
63 |
64 | /**
65 | * 🚨 🚨 DO NOT DELETE OR CHANGE THIS.🚨 🚨
66 | * This is how you would use your above component
67 | * The output of this code is displayed on the browser on the left hand side
68 | */
69 | const Usage = (props) => {
70 | return
71 | }
72 |
73 | export default Usage;
74 |
--------------------------------------------------------------------------------
/src/exercise/08-ComposingComponents.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | /**
4 | *🏆
5 | * The goal of this exercise is to get you aquainted with composing
6 | * different components in React. Here we will create a simple Card
7 | * component with Header, Body and Footer section. Nothing fancy - each
8 | * section has different background-color and each section should be
9 | * agnostic to what it's displaying. The user of the card
10 | * should pass "render props" so that the Card can render contents
11 | * to different section without knowing what they are displaying
12 | */
13 | class Card extends Component {
14 | render() {
15 | return (
16 |
17 |
18 | {
19 | /**
20 | * ✏️
21 | * Use renderHeader props here to render header content
22 | */
23 | }
24 |
25 |
26 | {
27 | /**
28 | * ✏️
29 | * Use renderBody props here to render body content
30 | */
31 | }
32 |
33 |
34 | {
35 | /**
36 | * ✏️
37 | * Use renderFooter props here to render footer content
38 | */
39 | }
40 |
41 |
42 | )
43 | }
44 | }
45 |
46 | class CardUser extends Component {
47 | render() {
48 | /**
49 | * ✏️
50 | * We need to pass renderHeader, renderBody and renderFooter props
51 | * to the Card with what we wanted to display inside that component
52 | * 🧭 Render props are functions when executed return something to render
53 | * 🧭 For simplicity with each render props function you can return a div
54 | * with the text saying which section it is.
55 | * For ex renderHeader can return a
Header
:
56 | * renderHeader={() =>
Header
}
57 | */
58 | return (
59 |
60 | )
61 | }
62 | }
63 |
64 |
65 | /**
66 | * 🚨 🚨 DO NOT DELETE OR CHANGE THIS.🚨 🚨
67 | * This is how you would use your above component
68 | * The output of this code is displayed on the browser on the left hand side
69 | */
70 | const Usage = (props) => {
71 | return
72 | }
73 |
74 | export default Usage;
75 |
--------------------------------------------------------------------------------
/src/exercise/08-ComposingComponents_ts.tsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | /**
4 | *🏆
5 | * The goal of this exercise is to get you aquainted with composing
6 | * different components in React. Here we will create a simple Card
7 | * component with Header, Body and Footer section. Nothing fancy - each
8 | * section has different background-color and each section should be
9 | * agnostic to what it's displaying. The user of the card
10 | * should pass "render props" so that the Card can render contents
11 | * to different section without knowing what they are displaying
12 | */
13 | class Card extends Component {
14 | render() {
15 | return (
16 |
17 |
18 | {
19 | /**
20 | * ✏️
21 | * Use renderHeader props here to render header content
22 | */
23 | }
24 |
25 |
26 | {
27 | /**
28 | * ✏️
29 | * Use renderBody props here to render body content
30 | */
31 | }
32 |
33 |
34 | {
35 | /**
36 | * ✏️
37 | * Use renderFooter props here to render footer content
38 | */
39 | }
40 |
41 |
42 | )
43 | }
44 | }
45 |
46 | class CardUser extends Component {
47 | render() {
48 | /**
49 | * ✏️
50 | * We need to pass renderHeader, renderBody and renderFooter props
51 | * to the Card with what we wanted to display inside that component
52 | * 🧭 Render props are functions when executed return something to render
53 | * 🧭 For simplicity with each render props function you can return a div
54 | * with the text saying which section it is.
55 | * For ex renderHeader can return a
Header
:
56 | * renderHeader={() =>
Header
}
57 | */
58 | return (
59 |
60 | )
61 | }
62 | }
63 |
64 |
65 | /**
66 | * 🚨 🚨 DO NOT DELETE OR CHANGE THIS.🚨 🚨
67 | * This is how you would use your above component
68 | * The output of this code is displayed on the browser on the left hand side
69 | */
70 | const Usage = (props) => {
71 | return
72 | }
73 |
74 | export default Usage;
75 |
--------------------------------------------------------------------------------
/src/exercise/solution/01-HelloWorld-solution.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | function HelloWorld(props){
4 | return (
5 | React.createElement('div', null, 'Hello World')
6 | );
7 | }
8 |
9 | /**
10 | * !!!!!!!!!!!!!!!!!!!!!!!!!!!
11 | * DO NOT DELETE OR CHANGE THIS.
12 | * This is how you would use your above component and
13 | * the output of this code is displayed on the browser
14 | */
15 | const Usage = (props) => {
16 | return
17 | }
18 |
19 | export default Usage;
20 |
21 |
--------------------------------------------------------------------------------
/src/exercise/solution/02-IntroToJSX-solution.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | function HelloWorld(props){
4 | return (
5 |
Hello World
6 | );
7 | }
8 |
9 | /**
10 | * !!!!!!!!!!!!!!!!!!!!!!!!!!!
11 | * DO NOT DELETE OR CHANGE THIS.
12 | * This is how you would use your above component and
13 | * the output of this code is displayed on the browser
14 | */
15 | const Usage = (props) => {
16 | return
17 | }
18 |
19 | export default Usage;
20 |
21 |
--------------------------------------------------------------------------------
/src/exercise/solution/03-PowerOfJSX-solution.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | function StockProfile(props) {
4 | const stockTicker = 'AAPL';
5 | const companyProfileInfo = {
6 | 'Company Name': 'Apple Inc.',
7 | 'Price': 150,
8 | 'Exchange': "Nasdaq Global Select",
9 | 'Industry': "Computer Hardware",
10 | 'CEO': 'Timothy D. Cook'
11 | }
12 |
13 | return (
14 |
38 | );
39 | }
40 | }
41 |
42 | /**
43 | * 🚨 🚨 DO NOT DELETE OR CHANGE THIS.🚨 🚨
44 | * This is how you would use your above component and
45 | * the output of this code is displayed on the browser
46 | */
47 | const Usage = (props) => {
48 | return
49 | }
50 |
51 | export default Usage;
--------------------------------------------------------------------------------
/src/exercise/solution/07-HandlingEvents-solution.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | class FancyInput extends Component {
4 | constructor(props) {
5 | super(props);
6 | this.state = {
7 | inputValue: ''
8 | }
9 | this.handleChange = this.handleChange.bind(this);
10 | }
11 |
12 | handleChange(e) {
13 | this.setState({
14 | inputValue: e.target.value
15 | });
16 | }
17 |
18 | render() {
19 | return (
20 |
21 |
22 |
You typed: {this.state.inputValue}
23 |
24 |
25 | )
26 | }
27 | }
28 |
29 | /**
30 | * 🚨 🚨 DO NOT DELETE OR CHANGE THIS.🚨 🚨
31 | * This is how you would use your above component
32 | * The output of this code is displayed on the browser on the left hand side
33 | */
34 | const Usage = (props) => {
35 | return
36 | }
37 |
38 | export default Usage;
--------------------------------------------------------------------------------
/src/exercise/solution/08-ComposingComponents-solution.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 |
3 | class Card extends Component {
4 | render() {
5 | return (
6 |
81 | );
82 | }
83 | }
84 |
85 | Markdown.propTypes = {
86 | source: PropTypes.string
87 | };
88 |
89 | export default Markdown;
--------------------------------------------------------------------------------
/src/setup/ScrollToTop.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { withRouter } from 'react-router';
3 |
4 | class ScrollToTop extends React.Component {
5 | componentDidUpdate(prevProps) {
6 | if (this.props.location !== prevProps.location) {
7 | window.scrollTo(0, 0);
8 | }
9 | }
10 |
11 | render() {
12 | return this.props.children;
13 | }
14 | }
15 |
16 | export default withRouter(ScrollToTop);
--------------------------------------------------------------------------------
/src/setup/Solution.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import PropTypes from 'prop-types';
3 | import styled from 'styled-components';
4 | import FileReader from '../FileReader';
5 |
6 | const SolutionContainer = styled.div`
7 | padding: 10px;
8 | border: 1px solid gray;
9 | height: calc(100% - 30px);
10 | background-color: ${props => props.finalSolution ? "#dbe4d8" : "#e3e4e4"};
11 | `
12 | class Solution extends Component {
13 | constructor(props){
14 | super(props);
15 | this.state = {
16 | solutionUsage: () =>
17 | }
18 | }
19 |
20 | componentDidUpdate(prevProps){
21 | if (prevProps.location !== this.props.location){
22 | this.loadSolution();
23 | }
24 | }
25 |
26 | componentDidMount(){
27 | this.loadSolution();
28 | }
29 |
30 | componentDidCatch(e){
31 | console.log('error', e);
32 | }
33 |
34 | loadSolution(){
35 | FileReader.readFile(`${this.props.location}`)
36 | .then(solutionUsage => {
37 | this.setState({ solutionUsage });
38 | })
39 | }
40 |
41 | render(){
42 | if (this.props.location) {
43 | const SolutionUsage = this.state.solutionUsage;
44 | return (
45 |
46 |
47 |
48 | )
49 | }
50 | return ()
51 | }
52 | }
53 |
54 | Solution.propTypes = {
55 | //location of the example file to be rendered
56 | location: PropTypes.string.isRequired,
57 |
58 | finalSolution: PropTypes.bool
59 | }
60 |
61 | export default Solution;
62 |
63 |
64 |
--------------------------------------------------------------------------------
/src/setup/Tutorial.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | import TutorialNavigation from './TutorialNavigation';
5 | import { TutorialMetadataApi } from './TutorialMetadataApi';
6 | import Markdown from './Markdown';
7 | import AppShell from './AppShell';
8 |
9 | const Container = styled.div`
10 | padding: 10px;
11 | margin: 10px 100px 10px 100px;
12 | `;
13 |
14 | const TutorialContainer = styled.div`
15 | margin-top: 100px;
16 | margin-bottom: 50px;
17 | `;
18 |
19 | const BottomNavigation = styled.div`
20 | margin-bottom: 30px;
21 | width: 100%;
22 | `;
23 |
24 | const Tutorial = ({ match }) => {
25 | const route = match.url;
26 | const currentTutorial = TutorialMetadataApi.getCurrentTutorial(route);
27 | const nextTutorial = TutorialMetadataApi.getNextTutorial(route);
28 | const previousTutorial = TutorialMetadataApi.getPreviousTutorial(route);
29 | return (
30 |
31 |
32 |
33 |
34 |
35 |
36 |
40 |
41 |
42 | );
43 | };
44 |
45 | export default Tutorial;
46 |
--------------------------------------------------------------------------------
/src/setup/TutorialMetadataApi.js:
--------------------------------------------------------------------------------
1 | import TutorialMetadata from './tutorialMetadata';
2 |
3 | export class TutorialMetadataApi {
4 | static getTutorialMetadata() {
5 | return [...TutorialMetadata];
6 | }
7 |
8 | static getCurrentTutorial(route) {
9 | return (TutorialMetadata.filter(d => d.route === route) || [])[0];
10 | }
11 |
12 | static getCurrentTutorialIndex(route) {
13 | return TutorialMetadata.findIndex(d => d.route === route);
14 | }
15 |
16 | static getPreviousTutorial(route) {
17 | const currentIndex = TutorialMetadataApi.getCurrentTutorialIndex(route);
18 | if (currentIndex >= 1) {
19 | return TutorialMetadata[currentIndex - 1];
20 | }
21 | }
22 |
23 | static getNextTutorial(route) {
24 | const currentIndex = TutorialMetadataApi.getCurrentTutorialIndex(route);
25 | if (currentIndex < TutorialMetadata.length - 1) {
26 | return TutorialMetadata[currentIndex + 1];
27 | }
28 | }
29 | }
30 |
31 | export default new TutorialMetadataApi();
--------------------------------------------------------------------------------
/src/setup/TutorialNavigation.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router-dom';
3 | import styled from 'styled-components';
4 |
5 | const Container = styled.div`
6 | display: table;
7 | text-align: center;
8 | width: 100%;
9 | `;
10 |
11 | const Navigation = styled.div`
12 | width: 33%;
13 | display: table-cell;
14 | `;
15 |
16 | const linkStyle = {
17 | padding: 10,
18 | backgroundColor: "#3f51b5",
19 | color: "white",
20 | borderRadius: 5
21 | };
22 |
23 | const TutorialNavigation = ({ previousTutorial, nextTutorial }) => {
24 | return (
25 |
26 |
27 | {previousTutorial && (
28 |
29 | ← {previousTutorial.displayName}
30 |
31 | )}
32 |
33 |
34 |
35 | Home
36 |
37 |
38 |
39 | {nextTutorial && (
40 |
41 | {nextTutorial.displayName} →
42 |
43 | )}
44 |
45 |
46 | );
47 | };
48 |
49 | export default TutorialNavigation;
50 |
--------------------------------------------------------------------------------
/src/setup/renderer/CodeBlock.css:
--------------------------------------------------------------------------------
1 | .hljs-number {
2 | color: #dff162;
3 | }
4 |
--------------------------------------------------------------------------------
/src/setup/renderer/CodeBlock.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Lowlight from 'react-lowlight';
3 | import js from 'highlight.js/lib/languages/javascript';
4 | import xml from 'highlight.js/lib/languages/xml';
5 | import 'highlight.js/styles/default.css';
6 | import 'highlight.js/styles/dracula.css';
7 | import './CodeBlock.css';
8 |
9 | Lowlight.registerLanguage('js', js);
10 | Lowlight.registerLanguage('xml', xml);
11 | Lowlight.registerLanguage('html', xml);
12 |
13 | function CodeBlock (props){
14 | return (
15 |
20 | )
21 | }
22 | CodeBlock.defaultProps = {
23 | language: 'js',
24 | value: ''
25 | }
26 | export default CodeBlock;
27 |
--------------------------------------------------------------------------------
/src/setup/renderer/HyperLink.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import styled from 'styled-components';
3 |
4 | const StyledHyperlink = styled.a`
5 | border-bottom: 3px solid #3f51b5;
6 | &:hover {
7 | border-bottom: 5px solid #3f51b5;
8 | }
9 | `;
10 |
11 | export function HyperLink(props) {
12 | const { href, children } = props;
13 | return (
14 | {children}
15 | );
16 | }
17 |
18 | export default HyperLink;
19 |
20 |
--------------------------------------------------------------------------------
/src/setup/tutorialMetadata.js:
--------------------------------------------------------------------------------
1 | export default [
2 | {
3 | id: 'Introduction',
4 | route: '/tutorial/react-introduction',
5 | displayName: '0 - React Introduction',
6 | markdownLocation: 'src/tutorial/build/00-ReactIntroduction.js'
7 | },
8 | {
9 | id: 'HelloWorld',
10 | route: '/tutorial/hello-world',
11 | displayName: '1 - Hello World',
12 | markdownLocation: 'src/tutorial/build/01-HelloWorld.js',
13 | exercises: [{
14 | tag: "exercise1",
15 | location: "src/exercise/01-HelloWorld.js",
16 | solutionLocation: "src/exercise/solution/01-HelloWorld-solution.js"
17 | }]
18 | },
19 | {
20 | id: 'IntroToJSX',
21 | route: '/tutorial/intro-to-jsx',
22 | displayName: '2 - Introduction to JSX',
23 | markdownLocation: 'src/tutorial/build/02-IntroToJSX.js',
24 | exercises: [{
25 | tag: "exercise1",
26 | location: "src/exercise/02-IntroToJSX.js",
27 | solutionLocation: "src/exercise/solution/02-IntroToJSX-solution.js"
28 | }]
29 | },
30 | {
31 | id: 'PowerOfJSX',
32 | route: '/tutorial/power-of-jsx',
33 | displayName: '3 - Power of JSX',
34 | markdownLocation: 'src/tutorial/build/03-PowerOfJSX.js',
35 | exercises: [{
36 | tag: "exercise1",
37 | location: "src/exercise/03-PowerOfJSX.js",
38 | solutionLocation: "src/exercise/solution/03-PowerOfJSX-solution.js"
39 | }]
40 | },
41 | {
42 | id: 'Props',
43 | route: '/tutorial/understanding-props',
44 | displayName: '4 - Understanding Props',
45 | markdownLocation: 'src/tutorial/build/04-Props.js',
46 | exercises: [{
47 | tag: "exercise1",
48 | location: "src/exercise/04-Props.js",
49 | solutionLocation: "src/exercise/solution/04-Props-solution.js"
50 | }]
51 | },
52 | {
53 | id: 'State',
54 | route: '/tutorial/understanding-state',
55 | displayName: '5 - Understanding State',
56 | markdownLocation: 'src/tutorial/build/05-State.js',
57 | exercises: [{
58 | tag: "exercise1",
59 | location: "src/exercise/05-State.js",
60 | solutionLocation: "src/exercise/solution/05-State-solution.js"
61 | }]
62 | },
63 | {
64 | id: 'LifecycleMethods',
65 | route: '/tutorial/lifecycle-methods',
66 | displayName: '6 - Lifecycle Methods',
67 | markdownLocation: 'src/tutorial/build/06-LifecycleMethods.js',
68 | exercises: [{
69 | tag: "exercise1",
70 | location: "src/exercise/06-LifecycleMethods.js",
71 | solutionLocation: "src/exercise/solution/06-LifecycleMethods-solution.js"
72 | }]
73 | },
74 | {
75 | id: 'HandlingEvents',
76 | route: '/tutorial/handling-events',
77 | displayName: '7 - Handling Events',
78 | markdownLocation: 'src/tutorial/build/07-HandlingEvents.js',
79 | exercises: [{
80 | tag: "exercise1",
81 | location: "src/exercise/07-HandlingEvents.js",
82 | solutionLocation: "src/exercise/solution/07-HandlingEvents-solution.js"
83 | }]
84 | },
85 | {
86 | id: 'ComposingComponents',
87 | route: '/tutorial/composing-components',
88 | displayName: '8 - Composing Components',
89 | markdownLocation: 'src/tutorial/build/08-ComposingComponents.js',
90 | exercises: [{
91 | tag: "exercise1",
92 | location: "src/exercise/08-ComposingComponents.js",
93 | solutionLocation: "src/exercise/solution/08-ComposingComponents-solution.js"
94 | }]
95 | },
96 | {
97 | id: 'Capstone',
98 | route: '/tutorial/capstone',
99 | displayName: '9 - Capstone',
100 | markdownLocation: 'src/tutorial/build/09-Capstone.js',
101 | exercises: [{
102 | tag: "exercise1",
103 | location: "src/capstone/Capstone.js",
104 | solutionLocation: "src/capstone/solution/Capstone.js"
105 | }]
106 | },
107 | {
108 | id: 'Conclusion',
109 | route: '/tutorial/conclusion',
110 | displayName: '10 - Conclusion',
111 | markdownLocation: 'src/tutorial/build/10-Conclusion.js'
112 | }
113 | ]
--------------------------------------------------------------------------------
/src/tutorial/00-ReactIntroduction.md:
--------------------------------------------------------------------------------
1 | [React](https://reactjs.org) is a flexible and declarative framework for building UI. React lets us build reusable "components" and compose those components to build any UI. Don't worry about what this all means right now, once you get through the tutorial you'll have a better idea. Let's start with Components.
2 |
3 | ### Component
4 |
5 | Components are building block of your UI - similar to directives in angular, or modules and widgets in other frameworks. Components in React are more or less self sufficient in that they constitute the presentation (HTML) as well as the behavior (eg. event handlers). They are also composable - meaning we can easily use one component within other component. So how do we create a Component? There are couple common ways how you can create a React component.
6 |
7 | #### 1. React.Component
8 |
9 | One way to create a React component is to create an ES6 `class` and extend `React.Component`. Each component created using this method should have a `render` function that returns what the DOM should look like if this component is rendered on the browser.
10 |
11 | ```jsx
12 | import React from 'react';
13 |
14 | class Component extends React.Component {
15 | //needs render function
16 | render() {
17 | return (
18 | //return should tell React what the DOM should look like
19 | //if this component is rendered in the browser
20 | );
21 | }
22 | }
23 | ```
24 |
25 | #### 2. function
26 | Another common way to create a React component is to create a simple function that takes in a parameter (we call `props`) and returns the exact same thing as above - what the DOM should look like if this component is rendered on the browser.
27 |
28 | ```jsx
29 | function Component(props) {
30 | return (
31 | //return should tell React what the DOM should look like
32 | //if this component is rendered in the browser
33 | );
34 | }
35 | ```
36 | *Note: Components that are user-defined - meaning the components that you and I write and are not available in the React implementation itself - must have [first letter capital](https://reactjs.org/docs/jsx-in-depth.html#user-defined-components-must-be-capitalized).*
37 |
38 | The above two approaches are identical except there are certain things that `React.Component` can do that the `function` cannot do but we will park that for now and come back to it later in this tutorial.
39 |
40 | Lets build our first `HelloWorld` React Component.
41 |
--------------------------------------------------------------------------------
/src/tutorial/01-HelloWorld.md:
--------------------------------------------------------------------------------
1 | Our `HelloWorld` component will look like this:
2 |
3 | ```jsx
4 | import React from 'react';
5 |
6 | function HelloWorld(props){
7 | //function must return something
8 | return (
9 | //return should tell React to render Hello World in the browser
10 | );
11 | }
12 | ```
13 | Now the question is what do we return from this function.
14 |
15 | The return of this function is telling React what the DOM should look like when this component is rendered on the browser. In case you're using `React.Component` approach (instead of `function` approach like above), it's what you return from `render` function that tells React what the DOM should look like when the component is rendered.
16 |
17 | In our case let's say we want to render a `div` element that has `Hello World` text like `
Hello World
`
18 |
19 | One way to tell React to display the above HTML is by using `React.createElement` function:
20 |
21 | ```jsx
22 | return React.createElement('div', null, 'Hello World');
23 | ```
24 |
25 | You should try this for yourself. Open the exercise file and edit the function to return a React element like above. Once you make the changes, save the file and you will see left panel updated with what React has rendered.
26 |
27 |
28 |
29 | Notice that `React.createElement` is a simple JavaScript function which takes three arguments. First argument is the element you want to render. In our case it's a `div` element. Second argument is any properties we want to pass to that element. In our case we are not passing anything so it's null. Third argument is the children for this component. In this case it's the text we want to display - `Hello World`. So with this we are telling React to render a div element like this:
30 |
31 | ```html
32 |
33 | Hello World
34 |
35 | ```
36 |
37 | Congratulations, you have created your first Hello World React component.
38 |
39 | Now your inquisitive mind is probably asking - how in the world does React render this thing on the browser?
40 |
41 | ## Rendering
42 |
43 | Let's step back from React for a moment and think about how we can create the similar Hello World `div` using pure JavaScript. Yes pure JavaScript - without any frameworks.
44 |
45 | ### Good Ol' Days
46 |
47 | Let's imagine you have a barebone `html` file that looks like below. It has a `div` with id `root` inside `body`. Pretty simple.
48 |
49 | ```html
50 |
51 |
52 |
53 |
54 |
55 |
56 | ```
57 |
58 | Now imagine inside the `div` with id `root` we want to render another `div` that says `Hello World`. The only catch is we want to do that programmatically using pure JavaScript.
59 | To achieve this we can probably do something like this:
60 |
61 | ```js
62 | //Create a div node and append Hello World text
63 | const helloWorldDiv = document.createElement('div');
64 | helloWorldDiv.append('Hello World');
65 |
66 | //Select the root node and append the div created above
67 | const root = document.getElementById('root');
68 | root.appendChild(helloWorldDiv);
69 | ```
70 |
71 | Here we are creating a `div` node with `Hello World` text and appending that `div` as a child of root `div`.
72 |
73 | We can actually write our entire application this way - creating elements, removing elements, appending elements, etc ourselves. As a matter of fact we did write applications this way before all these UI frameworks/libraries started to mushroom.
74 |
75 | ### Age of React
76 | A simple example like the one above are not that hard to write with pure JavaScript but once your application gets bigger, it gets messier. That's where libraries like React come to the rescue - they hide away from us the messier part of rendering on the browser.
77 |
78 | The Core React library itself doesn't really know how to render anything on the browser because it is designed to work in a web browser as well as native applications. Thus the job of rendering your component on the browser is done by another library provided by React team called `ReactDOM`.
79 |
80 | Now let's get back to the `HelloWorld` React component we created at the top of this page and see how we can use ReactDOM to render that component to the browser.
81 |
82 | ```jsx
83 | ReactDOM.render(HelloWorld, document.getElementById('root'))
84 | ```
85 |
86 | Here we are calling a function called `render` on `ReactDOM` object. The first argument of the function is the component you want to render - in our case `HelloWorld`. Second argument is a document selector. ReactDOM appends the component we want to display (first argument) as a child of the node returned by the selector (second argument).
87 |
88 | Compare this solution to the pure JavaScript solution we looked at earlier. With pure JavaScript we were doing the DOM manipulation ourselves - creating the `div`, appending the text and appending the newly created `div` to the `div` with id `root` as its child. But with React we are not doing any DOM manipulation ourselves. Basically we are saying to React -
89 |
90 | > Hey React I have a component I want to render. I will tell you what the component should look like when it's rendered (remember this is what the return of the Component function tells). I will also tell you where to render this component (second argument we passed to `ReactDOM.render` function). But I don't want to get involved in DOM manipulation - I will let you do all the DOM manipulation yourself. You can call all these DOM api like `document.createElement`, `.append`, `.appendChild` etc. whenever you wish - I trust you and I don't care as long as I see on the browser what I expected to see.
91 |
92 | Ok then how does React do it internally? We won't go into all the details of it but briefly talk about one important part of React implementation called Virtual DOM.
93 |
94 | ### Virtual DOM
95 | [DOM (Document Object Model)](https://www.w3schools.com/js/js_htmldom.asp) is an object representation of the HTML. To see what it looks like open chrome dev tools (Right Click + Inspect) and type `console.dir(document)` and hit enter, you will see a JSON-like tree structure with fields and methods. React for it's part maintains a copy of this DOM - what's called a [virtual DOM](https://reactjs.org/docs/faq-internals.html), named so because it's not a real one, it's a virtual copy.
96 |
97 | Why does React hold a copy of the DOM? The main reason it maintains a virtual copy of the DOM is to improve performance of the application.
98 | Web applications these days are very complex. User interacts with the app or the app fetches data and based on that the DOM is updated so that users sees the effects of their interaction or new data. This updating of DOM, however, is an expensive operation - creating and removing DOM nodes (like we did with `document.createElement('div')` above) are expensive. So React optimizes this updating operations using virtual DOM.
99 |
100 | The way this roughly works is: when there's anything that will cause a UI to update (called re-render), React first updates its virtual DOM instead of real DOM. Then it compares the virtual DOMs (before and after the update). It has a heuristic algorithm to determine which piece of the DOM might have changed. Once it figures that out, it updates only the changed piece on the real DOM. Again the reason it does this is because updating the DOM is an expensive operation and it wants to optimize this piece so it only updates what is absolutely necessary instead of tearing down the entire DOM and recreating it.
101 |
--------------------------------------------------------------------------------
/src/tutorial/02-IntroToJSX.md:
--------------------------------------------------------------------------------
1 | In the [previous section](/tutorial/hello-world) we created our first Hello World function component. Remember we returned `React.createElement` from the function to tell React what the DOM should look like when we render this component. Another alternative way of telling React what the DOM should look like is by using JSX. JSX is a very common and recommended way (preferred over `React.createElement` syntax in most cases) to write React code. JSX is a funny looking syntax though - it's not purely HTML, it's not purely JavaScript. But it's an extension of JavaScript where you can write HTML like syntax with full power of JavaScript. For example, the equivalent of return statement we saw in [previous page](/tutorial/hello-world) (using `React.createElement`) in JSX would be:
2 |
3 | ```jsx
4 | return (
5 |
Hello World
6 | )
7 | ```
8 |
9 | Instead of returning JavaScript code, it's returning HTML-like code (it's not HTML) and notice it's not a string. Wait, what?!! Welcome to JSX!
10 |
11 | You don't trust that this weird syntax works, do you? Open the exercise file and edit the return statement with the JSX and save to see the magic happen!
12 |
13 |
14 |
15 | Although you write HTML looking syntax, your JSX code is compiled into a JavaScript function like the one we saw in the previous page. The above JSX code is compiled into:
16 |
17 | ```jsx
18 | return React.createElement('div', null, 'Hello World');
19 | ```
20 |
21 | So writing above JSX code or `React.createElement` code will generate exactly same output on the browser. You can definitely write all your React code using `React.createElement` and not ever care about JSX. But it's gonna get pretty complicated pretty soon. Imagine writing all the nested `React.createElement` functions for generating simple HTML like below:
22 |
23 | ```html
24 |
25 |
26 |
27 |
Col
28 |
29 |
30 |
31 |
32 |
Cell
33 |
34 |
35 |
36 | ```
37 | You'd have to write something like this, which is not very pretty:
38 | ```js
39 | React.createElement('table', null,
40 | [
41 | React.createElement('thead', null,
42 | React.createElement('th', null,
43 | React.createElement('td', null, 'Col')
44 | )
45 | ),
46 | React.createElement('tbody', null,
47 | React.createElement('tr', null,
48 | React.createElement('td', 'null', 'Cell')
49 | )
50 | )
51 | ]
52 | ```
53 | So in essence JSX is nothing more than a syntactic sugar for complicated `React.createElement` functions to make React code more elegant and readable.
54 |
--------------------------------------------------------------------------------
/src/tutorial/03-PowerOfJSX.md:
--------------------------------------------------------------------------------
1 | In the previous page we looked at JSX syntax where we displayed a string `Hello World` inside the `div` element. What's the big deal! What else can it do for us?
2 |
3 | Since JSX is technically JavaScript it is pretty powerful in many senses. It can do everything that JavaScript can do.
4 |
5 | If you want to execute any JavaScript code within JSX then you surround your JavaScript code with curly braces `{ //JavaScript code }` and put it anywhere in JSX. It will evaluate your code every time it renders the component to find out what it should render on the browser.
6 |
7 | For example let's imagine we want to display a company profile information and a Company ticker. And imagine the Company ticker is stored on some variable somewhere and the company profile information is stored in another variable as an object. In that case we might write:
8 |
9 | ```jsx
10 | function CompanyProfile(props) {
11 | //ticker and companyProfileInfo stored in a variable
12 | const ticker = 'AAPL';
13 | const companyProfileInfo = {
14 | 'Company Name': 'Apple Inc.',
15 | 'Exchange': 'Nasdaq',
16 | 'Sector': 'Technology',
17 | 'Industry': 'Computer Hardware',
18 | 'CEO': 'Timothy D. Cook'
19 | };
20 | return (
21 |
33 | )
34 | }
35 | ```
36 |
37 | The HTML output of the above Component when it's rendered will be:
38 |
39 | ```html
40 |
41 |
Profile of: AAPL
42 |
43 |
Company Name: Apple Inc.
44 |
Exchange: Nasdaq
45 |
Sector: Technology
46 |
Industry: Computer Hardware
47 |
CEO: Timothy D. Cook
48 |
49 |
50 | ```
51 |
52 | Well that's a handful, so let's review what's happening here. We have a `ticker` variable with a value `AAPL` and an object `companyProfileInfo` that has company profile. Inside the JSX (inside the `return` statement) we have a `div` enclosing everything. In JSX, **a component must return one and only one enclosing tag**. That tag can have as many children as it wants.
53 | ```jsx
54 | // ❌ This is illegal in React since the return has more than one tag.
55 | return (
56 |
57 |
58 | )
59 |
60 | // ✅ This is perfectly legal because there is just one enclosing tag and
61 | // it can have as many children as it likes
62 | return (
63 |
64 |
65 |
66 |
67 |
68 |
69 | )
70 |
71 | // ✅ If you don't want to wrap your component with some enclosing tag like `div`
72 | // you can wrap everything with `React.Fragment` which is an empty tag provided by React
73 | return (
74 |
75 |
76 |
77 |
78 | )
79 | ```
80 |
81 | Going back to the company profile example, the first child of the enclosing `div` is another `div`. Inside that `div` we used curly braces to display the `ticker` alongside `Profile of:`. Remember curly braces is how we inject JavaScript code inside JSX. So here the `ticker` variable would be evaluated and rendered inside that `div` tag. Then we have another `div` as a second child of the enclosing parent. Inside this `div` we again have curly braces and we execute some JavaScript code. In this case we mapped each key of the `companyProfileInfo` object to a `div` element. The content of this `div` is again evaluated using another curly braces like: `{key} : {companyProfileInfo[key]}`. What we did here is that we told React that for each key of the `companyProfileInfo` object we want to render a `div` whose content would be the `key` followed by a colon `:` followed by the corresponding value for the key on the object (`companyProfileInfo[key]`).
82 |
83 | Let's write some code here to hit the nail on the head. Please open the exercise file and follow the instructions.
84 |
85 |
86 |
87 | Key takeaways:
88 | - Components must return only one tag. This tag can have as many children as it likes. Instead of a tag, it can however return a string or null.
89 | - You can run any JavaScript code inside the `return` using curly braces `{//run any JavaScript}`.
90 | - Outside of the `return` it's exactly like any other JavaScript class or function. You can do whatever you desire to do.
91 |
92 | ### Differences with HTML
93 |
94 | There are some commonly used things that are slightly different in JSX than in HTML.
95 |
96 | - Styles
97 |
98 | In HTML, styles are passed as string. The css properties are kebab-cased.
99 |
100 | ```html
101 |
102 | ```
103 |
104 | In JSX, styles are passed as an object. The css properties are camelCased.
105 |
106 | ```jsx
107 |
108 | ```
109 |
110 | - Class
111 |
112 | In HTML class attribute is passed as string.
113 |
114 | ```html
115 |
116 | ```
117 |
118 | In JSX also class attribute is passed as string but instead of calling it `class` we call it `className`. That's because JSX is extension of JavaScript and "class" is a reserved keyword in JavaScript.
119 |
120 | ```jsx
121 |
122 | ```
123 | - Event Handler
124 |
125 | In HTML event handler attribute is all lower cased and the handlers are passed as string.
126 |
127 | ```html
128 |
129 | ```
130 |
131 | In JSX, event handler are camelCased and instead of string we pass the actual function.
132 |
133 | ```jsx
134 |
135 | ```
136 |
137 | We will look more into [event handler](/tutorial/handling-events) later in the tutorial.
138 |
--------------------------------------------------------------------------------
/src/tutorial/04-Props.md:
--------------------------------------------------------------------------------
1 | Let's continue with the earlier example where we displayed company profile of `AAPL`. Let's say I want to display the Company Profile for `FB` at some other place in the application. Since we have two variables `ticker` and `companyProfileInfo` hard-coded inside this component, should we copy and paste the entire component to some other place and replace the `ticker` and `companyProfileInfo` to be that of `FB`?
2 |
3 | Well, no. Remember that React is about building reusable Components. So we want to build a component called `CompanyProfile` that can be reused for any company ticker.
4 |
5 | Let's look at the `CompanyProfile` component again. Here we have two variables `ticker` and `companyProfileInfo`. So what if instead of hard coding the values of these two variables here inside the component, we could pass those values to this component instead?
6 |
7 | If we were to pass these values to this component from outside, then this Component will not be tied to one Company ticker. We can pass in `XYZ` and it's profile info to this component and it should be able to render the profile for `XYZ` or any other company for that matter. This component becomes truly reusable. Wonderful, that's what we want.
8 |
9 | ```jsx
10 | function CompanyProfile(props) {
11 | //Instead of storing these variables we want to pass
12 | //these values to this component from outside.
13 | const ticker = //pass the value for this variable from outside
14 | const companyProfileInfo = //pass the value for this variable from outside
15 | return (
16 |
27 | )
28 | }
29 | ```
30 |
31 | Well then how do we pass these values to a component from outside? That's where `props` comes in. In React lingo, `props` is something that the component is passed by the user of that component (parent component passes `props` to child component).
32 | You can pass anything as a prop: `function`, `object`, `boolean`, `string`, `number`, etc. Here's an example of a Component passing the `props` to its children.
33 |
34 | ```jsx
35 | function Children(props) {
36 | return (
37 |
{props.textToDisplay}
38 | )
39 | }
40 |
41 | function Parent(props) {
42 | return (
43 |
44 | )
45 | }
46 | ```
47 | There are couple things going on here. First - remember on the [first page of this tutorial](/tutorial/react-introduction) we said that we can compose components (we can use one component inside the other)? Well that's what we are doing here. `Parent` component uses `Children` component inside it's return.
48 |
49 | Second - if you inspect the above code snippet carefully, we see that when the `Parent` uses the `Children` (inside `return`) it's also passing something called `textToDisplay` with some value `Hello`. We call this "passing the props". So `Parent` is passing a `props` called `textToDisplay` to the `Children`. How, then, does the `Children` use the value that the `Parent` passes down to it?
50 |
51 | 1. Component created with function
52 |
53 | If you created your component as a `function` like we did here, all the `props` its `Parent` passed down will be accessible through the argument. If you look at the `Children` above it's using `props.textToDisplay` inside the `div`. All the `props` passed to the `Children` are passed as this single `props` argument. For example, if the `Parent` had passed a props called `defaultValue`, the `Children` would access it as `props.defaultValue`.
54 |
55 | 2. Component created as React.Component
56 |
57 | If you created your Component by extending `React.Component` then all the `props` would be available as `this.props`. The equivalent of above `Children` function using `React.Component` would look like this:
58 |
59 | ```jsx
60 | class Children extends React.Component {
61 | render(){
62 | return (
63 |
{this.props.textToDisplay}
64 | )
65 | }
66 | }
67 | ```
68 |
69 | Now that we know what `props` are, lets do some exercise and see how we can make `CompanyProfile` component reusable.
70 |
71 |
72 |
73 |
74 | One thing you must remember regarding `props` is that you should **never** mutate `props` - React will complain if you do. This is something given to the component by it's parent - accept with love and don't try to mess around with things to make your parent angry!
75 |
76 | ```jsx
77 | function Children(props) {
78 | //❌ NEVER DO THIS
79 | props.textToDisplay = 'I want to mutate props'
80 | return (
81 |
{props.textToDisplay}
82 | )
83 | }
84 | ```
85 |
86 | ### PropTypes
87 | In many cases it's better for a component to clearly define a contract regarding the `props` it can accept - data type, data structure, if the props is required etc.
88 | There are couple obvious benefits of this:
89 | - React can enforce type checking to avoid many bugs arising from parents passing props with a type that's different from what the children expects (ex. parent passing `string` when children expects an `object`).
90 | - If you are writing components that will be used by different people at different parts of the application, it's always useful for those users to know what are the props they can pass, what is the expected structure of the props etc.
91 |
92 | To define this contract - first you need to add `prop-types` as a [project dependency (provided by React team)](https://www.npmjs.com/package/prop-types) and you need to define a special property called `propTypes` in your component.
93 |
94 | ```jsx
95 | import React from 'react';
96 | import PropTypes from 'prop-types';
97 |
98 | class SoftwareEngineer extends React.Component {
99 | render(){
100 | return (...)
101 | }
102 | }
103 |
104 | //defines "propTypes" property in this component
105 | SoftwareEngineer.propTypes = {
106 | name: PropTypes.string.isRequired, //expects string and is required
107 | hobbies: PropTypes.arrayOf(PropTypes.string), //expects array of string
108 | address: PropTypes.shape({
109 | street: PropTypes.string,
110 | city: PropTypes.string
111 | }) //must be an object with 'street' and 'city' fields
112 | }
113 | ```
114 | Here we have defined the `propTypes` property and assigned an object. Each key in this object represents the name of the `props` the user of this component can pass. The value defines the "type" of the `props` - you know: `string`, `number`, `array`, etc. All `props` are optional (user of the component doesn't have to pass them) except the one that has `.isRequired`. Here's a quick explanation on three `props` defined above:
115 | - `name` - It expects the value of this `props` to be a `string` and it's required because, well, it has `.isRequired`.
116 | - `hobbies` - It's optional but if passed it must be an array of strings.
117 | - `address` - It's also optional but if passed it must be an object with two fields - `street` and `city` - and both must be string.
118 |
119 | These are just some examples of what you can do to enable type checking. There are plenty more types you can define - please check out [the documentation](https://reactjs.org/docs/typechecking-with-proptypes.html#proptypes) for more.
120 |
121 | ### Default Props
122 | In some cases you might want to define a default value for a `props` in case it is not passed to you.
123 | You can use `defaultProps` property to define your defaults. With this you're basically saying - "if someone doesn't pass me a value for a `props` that I'm expecting, then I want the value of that `props` to be what I have defined in the `defaultProps`". For example - for the above component we can define `defaultProps` as follows:
124 |
125 | ```jsx
126 | import React from 'react';
127 | import PropTypes from 'prop-types';
128 |
129 | class SoftwareEngineer extends React.Component {
130 | render(){
131 | //if this props is not passed, it will print default value as defined by `defaultProps`
132 | console.log(this.props.hobbies);
133 |
134 | //if this props is not passed, it will print `undefined` because we haven't defined any default value for this props
135 | console.log(this.props.address);
136 | return (...)
137 | }
138 | }
139 |
140 | //defines "defaultProps" property in this component
141 | SoftwareEngineer.defaultProps = {
142 | hobbies: ['Writing React code']
143 | }
144 | ```
145 |
146 | Let's say if the user of this component doesn't pass any value for `hobbies`, then it will be defaulted to `['Writing React code']`.
147 | And if the user of the component doesn't pass any value for `address` then it will resolve to `undefined` because we haven't defined the default value for it.
148 |
--------------------------------------------------------------------------------
/src/tutorial/05-State.md:
--------------------------------------------------------------------------------
1 | React Components can have local state. This state is not shared with the component's parent or the component's child by default. It is fully owned and controlled by the component itself.
2 |
3 | Remember when we looked at different ways of creating React component in the first page of this tutorial, we said there are things that component extending `React.Component` can do that a `function` component cannot do? Well state is one of them. `React.Component` can have states but `function` cannot have states.
4 |
5 | *Note: There's a new feature in React called hooks that let's us use state within function component but that's still in alpha so if we want to use state right now on a component we still need to use `React.Component`.*
6 |
7 | Below is a simple component that has a state.
8 |
9 | ```jsx
10 | class Component extends React.Component {
11 | constructor(props){
12 | super(props);
13 | this.state = {
14 | counter: 0
15 | }
16 | }
17 |
18 | render() {
19 | return
{this.state.counter}
20 | }
21 | }
22 | ```
23 |
24 | `state` is just an object. If you notice the constructor, we initialized the state with `{counter: 0}`. And we used the state inside the `return` of the `render` function as `{this.state.counter}`.
25 |
26 | We initialized the `state` in the constructor, but how do we update it? For example in the above example, how do we change the `counter` to let's say 1? For that React provides a function called `setState`. You should **always** use the `setState` function to change `state` and **never** mutate it directly.
27 |
28 | ```jsx
29 | //❌ NEVER DO THIS
30 | this.state.counter = 2;
31 |
32 | // ✅ ALWAYS DO THIS
33 | this.setState({
34 | counter: 2
35 | });
36 | ```
37 |
38 | If `state` is just an instance variable in the component, can we call it some other name? And why do we **have** to use `setState`? Well no we cannot call it by some other name, and we have to use `setState` to update the `state` mainly because React understands `state`. When `state` of your component changes, React re-renders your component (by re-render I mean calls the `render` function again to see if the DOM will change as a result of change in `state`). This is fundamental to the declarative nature of React.
39 |
40 | ### setState
41 |
42 | Let's look deeper into `setState` function. The first argument of `setState` function can take either a new state object or a function. It also has an optional second argument, a callback which is executed when the state is updated.
43 |
44 | ```jsx
45 | setState(newState || function, optional callback)
46 | ```
47 |
48 | The way `setState` updates the state is:
49 | - If the first argument of the `setState` function is an object, it merges the current `state` object with whatever you passed to the `setState` function. For example:
50 |
51 | ```jsx
52 | state = { a: 1, b: 2, c: 3} //current state
53 | this.setState({ a: 3 }); //we call setState with just one key value pair
54 |
55 | //it will **merge** the initial state with the object passed to setState
56 | //as a result only the value for that one key is updated
57 | state = { a: 3, b: 2, c: 3 } //state after setState is flushed
58 | ```
59 |
60 | - If the first argument is a function, then it first executes the function by passing the current `state` as it's argument. The function must return an object. It then merges this output with the current `state` just like it did above. For example:
61 |
62 | ```jsx
63 | state = { a: 1, b: 2, c: 3} //initial state
64 | //we called setState with a function
65 | this.setState(currentState => ({
66 | a: currentState.a + 1
67 | }));
68 | //it executes the function by passing the current state object as argument.
69 | //since currentState.a is 1, function returns { a: 2 }
70 | //it now merges this returned object with the original state
71 | state = { a: 2, b: 2, c: 3 } //state after setState is called
72 | ```
73 |
74 | One thing you must know about `setState` function is that it may be asynchronous. So **do not** rely on it to update the state immediately. This is not a bug, it's by design. If you want to read up on the design decision behind `setState` call being asynchonous, here's a [nice explanation](https://github.com/facebook/react/issues/11527#issuecomment-360199710).
75 |
76 | Since `setState` can be asynchronous below code will not give you the right result because by the time we `console.log` the `state.counter` value, it won't be updated.
77 |
78 | ```jsx
79 | //❌ WRONG RESULT. Do not rely on setState to be synchronous
80 | console.log(this.state.counter);//prints 0
81 | this.setState({
82 | counter: this.state.counter + 1
83 | }); //this is asynchronous call
84 | console.log(this.state.counter);//still prints 0
85 | ```
86 |
87 | Also if you want to update `state` using the current state value, **always** use the updater function inside `setState` instead of passing object. For example below code will not work.
88 |
89 | ```jsx
90 | //❌ DONT DO THIS
91 | //If you are using the current state value to update the state, never use this.state directly inside setState
92 | console.log(this.state.counter); //prints 0
93 | this.setState({ counter: this.state.counter + 1 });
94 | this.setState({ counter: this.state.counter + 1 });
95 | this.setState({ counter: this.state.counter + 1 });
96 | //the state will be 1 when all of the calls are flushed
97 | //because since the calls were asynchronous, this.state.counter
98 | //on all three calls were 0 and adding 1 resulted in 1.
99 |
100 |
101 | //✅ ALWAYS DO THIS
102 | //If you are using current state value to update the state, always use updater function
103 | console.log(this.state.counter); //prints 0
104 | this.setState((state) => ({ counter: state.counter + 1}) );
105 | this.setState((state) => ({ counter: state.counter + 1}) );
106 | this.setState((state) => ({ counter: state.counter + 1}) );
107 | //this is guaranted to work!
108 | //when all three calls are flushed the value of
109 | //this.state.counter will be 3
110 | ```
111 |
112 | Let's look at an exercise. Click the '+', '-' on the right side to see the expected behavior. Now please open the exercise file and make changes as instructed in the exercise file to achieve the expected behavior.
113 |
114 |
115 |
116 | I know we discussed several things about `state` and it must be overwhelming. Let's just recap the rules:
117 | - Never mutate `this.state` directly. Always use `this.setState` to update the `state`.
118 | - If your new `state` doesn't depend on the old `state` then you can use `this.setState(object)` construct.
119 | - If your new `state` depends on the old `state` then use `this.setState(function(currentState){ .. })` construct.
120 |
121 | #### Props and State
122 | Since we now have looked into both `state` and `props` how are they different and how are they similar?
123 |
124 | The difference between `state` and `props` is that `state` is owned by the component itself while `props` is something that is passed down to the component by it's parent.
125 |
126 | And the similarity (sort of) is that React automatically re-renders your component when either the component's `state` changes or when the component's `props` changes.
127 |
128 | Your component's `render` function is a function of both `state` and `props` meaning it defines what your component should look like given the `state` and `props`. It should be pure function in a sense that if the component has same `state` and `props` it should render exactly same content no matter how many times it's called and shouldn't have any side effects.
129 |
--------------------------------------------------------------------------------
/src/tutorial/06-LifecycleMethods.md:
--------------------------------------------------------------------------------
1 | React provides a way to take some actions on different lifecycle phases of the component. There are several benefits to that. For example, we may want to fetch some data when a component is rendered, or clean up some resources before the component is removed from the DOM. React calls these lifecycle methods (if we have defined them) during these lifecycle phases. You are not required to implement any of these lifecycle methods, you implement only those that you need on your particular component.
2 |
3 | ```jsx
4 | class ComponentWithLifecycle extends React.Component {
5 | constructor(props){
6 | super(props);
7 | }
8 |
9 | componentDidMount(){
10 | //This will be called after the component is mounted to the DOM
11 | }
12 |
13 | componentDidUpdate(prevProps, prevState){
14 | //This will be called after the component is updated
15 | //Remember component can only be updated when the state changes
16 | //or the props changes
17 | }
18 |
19 | componentWillUnmount(){
20 | //This will be called right before this component is unmounted from
21 | //the DOM
22 | }
23 | }
24 | ```
25 |
26 | Lets look at some common use cases where these lifecycle methods comes in handy.
27 |
28 | Let's consider the `CompanyProfile` example we looked at earlier where we were displaying the `ticker` and the `companyProfileInformation`. In the example, the parent of the component passed both `ticker` and `companyProfileInformation` as `props`. But let's assume that the parent only has the `ticker` information, which it will pass as `props`, but it doesn't have the profile information data. Assume there's an API that the `CompanyProfile` component can use to fetch the profile information data. This is a very common use case. So how/when should the `CompanyProfile` component fetch the data?
29 |
30 | `componentDidMount` is the right lifecycle method to make the network call (fetch data using the API). This method is called only once - when the component is mounted on the DOM. After the API returns the data, we can set the data to the `state` and use `this.state.companyProfileInformation` instead of `this.props.companyProfileInformation` inside the `render` function.
31 | Remember that in this case the parent didn't pass the `companyProfileInformation` props but instead `CompanyProfile` fetched that data itself and stored in the `state`.
32 |
33 | ```jsx
34 | import DataApi from '../api';
35 |
36 | class CompanyProfile extends React.Component {
37 | componentDidMount() {
38 | DataApi.getCompanyProfile(this.props.ticker)
39 | .then(profile => {
40 | this.setState({
41 | companyProfileInformation: profile
42 | })
43 | })
44 | }
45 | }
46 | ```
47 |
48 | Let's do the same exercise. Please open the exercise file and make the change as instructed on the file.
49 |
50 |
51 |
52 | That works for the initial value of `ticker` passed by parent because `componentDidMount` is called only once. But what happens when the `props` changes (meaning the parent component passes a new `ticker` value)? How/when would `CompanyProfile` component know to fetch data again for the new `ticker`? In that case you can use `componentDidUpdate`. This lifecycle method is called every time component is updated. Remember component is updated every time the `props` or the `state` changes.
53 |
54 | You're probably thinking - 'This doesn't sound good'. If you were to fetch the data inside `componentDidUpdate` (which would be called every time your component updates), your component will make a lot of repetitive network calls to get the profile information for same ticker because the component might have updated, not just when `ticker` props changed but when any other `props` changes or some other `state` changes.
55 | Well you're thinking correctly. That's the reason why you should **always** check if the `props` you are interested in changed (in this case `ticker` changed) before making the network request.
56 |
57 | ```jsx
58 | import DataApi from '../api';
59 |
60 | class CompanyProfile extends React.Component {
61 | //when react calls componentDidUpdate, it gives as argument the value of
62 | // props and state before the update happened so you can do the comparision
63 | componentDidUpdate(prevProps, prevState) {
64 | //always be defensive, otherwise you will make a lot of
65 | //unnecessary network calls
66 | //in this case we only make the network call if the
67 | //ticker props before and after the component updated are not same
68 | if (prevProps.ticker !== this.props.ticker) {
69 | DataApi.getCompanyProfile(this.props.ticker)
70 | .then(profile => {
71 | this.setState({
72 | companyProfileInformation: profile
73 | })
74 | })
75 | }
76 | }
77 | }
78 | ```
79 |
80 | These are just some common use cases when you want to use the lifecycle methods provided by React. Please refer to the
81 | [React documentations](https://reactjs.org/docs/react-component.html#the-component-lifecycle)
82 | for an extensive list of all the lifecycle methods available.
83 |
84 | Also note that there are some lifecycle methods that were available as part of earlier versions of React (16 and earlier), and they will be deprecated as part of version 17.0, so **DO NOT** use them:
85 |
86 | - componentWillMount
87 | - componentWillUpdate
88 | - componentWillReceiveProps
89 |
90 | Please refer to this [blog post](https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html) to understand why these lifecycle methods are being deprecated and also more about some best practices regarding usage of lifecycle methods.
91 |
--------------------------------------------------------------------------------
/src/tutorial/07-HandlingEvents.md:
--------------------------------------------------------------------------------
1 | Handling events in JSX is pretty similar to handling events in actual DOM.
2 | For example if we have a `button` element and we want to pass an event handler for a `click` event, we can pass a props called `onClick` to the button element.
3 |
4 |
5 | ```jsx
6 | function ClickableButton(props){
7 | //callback function that's called when button is clicked
8 | const handleClick = () => { alert('Clicked') }
9 | return (
10 |
11 | )
12 | }
13 | ```
14 |
15 | Notice there are couple difference between how events are handled in HTML vs JSX.
16 |
17 | 1. With HTML we would have an attribute called `onclick` (all lowercased), but with JSX have a camelCased `props` called `onClick`. In JSX always use camelCase instead of lowercase like in HTML.
18 | Ex:
19 | - `onclick` => `onClick`
20 | - `onmouseover` => `onMouseOver`
21 | - `onselect` => `onSelect`
22 | - `onchange` => `onChange`
23 |
24 | 2. With HTML we would pass a string as a value of the attribute, but in JSX we pass the actual function. For example compare the above `button` in JSX to the following HTML equivalent:
25 |
26 | ```html
27 |
28 | ```
29 |
30 | ### Synthetic events
31 |
32 | When React invokes the event-handler, it provides a [SyntheticEvent](https://reactjs.org/docs/events.html) as an argument. Not to worry about any of the details, it's a wrapper around browser's native event and it has the same interface as the native event.
33 |
34 | ```jsx
35 | function InputComponent(props){
36 | //callback function that's called when input changes
37 | //It gets synthetic event as an argument
38 | //SyntheticEvent has interface just like the native browser event
39 | //In this case - to get the value of the input we did e.target.value
40 | const handleChange = (e) => { alert(e.target.value) }
41 | return (
42 |
43 | )
44 | }
45 | ```
46 | ### Function Binding
47 |
48 | One gotcha when using event handlers with components created as `ES6` classes (by extending `React.Component`) is the method binding. This has nothing to do with React or JSX - it's a new JavaScript feature in `ES6` and trips even many experienced folks.
49 |
50 | Class functions in `ES6` are not bound to anything by default. For example take below example of `Input` component. Here we just have an input element whose value is assigned to `this.state.inputValue`, and we are handling the `onChange` event on this `input` by passing our `this.handleChange` function. Inside the `handleChange` function we are calling `setState` with the new value typed by the user. Now if we run this code it will error out saying something like "Cannot read property setState of undefined". Weird huh? The reason is because `handleChange` is not bound to anything. So `this` inside `handleChange` is `undefined`.
51 |
52 | ```jsx
53 | class Input extends React.Component {
54 |
55 | handleChange(e){
56 | this.setState({
57 | inputValue: e.target.value
58 | });
59 | }
60 |
61 | render(){
62 | return(
63 |
64 | )
65 | }
66 | }
67 | ```
68 |
69 | To fix above issue we just need to `bind` the function to proper `this`. The common practice is to do that in the constructor. So below we added a line in a constructor `this.handleChange = this.handleChange.bind(this)` to bind the `handleChange` function and it would work like a charm.
70 |
71 | ```jsx
72 | class Input extends React.Component {
73 | constructor(props){
74 | super(props);
75 |
76 | //bind handleChange function to proper this
77 | this.handleChange = this.handleChange.bind(this);
78 | }
79 |
80 | handleChange(e){
81 | this.setState({
82 | inputValue: e.target.value
83 | });
84 | }
85 |
86 | render(){
87 | return(
88 |
89 | )
90 | }
91 | }
92 | ```
93 |
94 | Let's write some code. Please open the exercise file and follow the instruction on the file.
95 |
96 |
97 |
98 | Please read [this article](https://cmichel.io/es6-class-methods-differences/) to get more idea on the `ES6` binding dilemma.
99 |
--------------------------------------------------------------------------------
/src/tutorial/08-ComposingComponents.md:
--------------------------------------------------------------------------------
1 | One of the most important characteristics of React is that it lets you compose different components to build your UI.
2 |
3 | Let's think about a use case. Imagine we want to build a reusable `Dialog` component. Assume it's a simple dialog component - nothing fancy. When this component is rendered we want to display some content within a modal window.
4 |
5 | ```jsx
6 | class Dialog extends React.Component {
7 | render(){
8 | return (
9 |
10 | //display content of dialog
11 | //????? but we don't know what to display
12 |
13 | )
14 | }
15 | }
16 | ```
17 | That sounds simple except we don't know what the content of the dialog is going to be. Imagine at one place we might want to display an employee contact within a dialog and at some other place we might want to display a grid. This component itself has no knowledge of it beforehand. And if we wanted to make this component truly reusable it **should not** have any knowledge of it beforehand.
18 |
19 | The job of the `Dialog` component is to render a modal window but it shouldn't care about the content - we should be able to use this component at different places to render different things.
20 |
21 | How do we tell this dialog to display different things?
22 |
23 | There are a couple of common patterns used in React to do this:
24 |
25 | 1. props.children
26 |
27 | One way to achieve this goal of reusing `Dialog` anywhere we like without `Dialog` having to know what it's displaying, is by using the `children` props. Let's look at one potential usage of `Dialog` component below to understand this:
28 |
29 | ```jsx
30 | //Displays EmployeeProfile inside a Dialog
31 | class EmployeeProfileDialog extends React.Component {
32 | render(){
33 | //We have passed a child component inside the Dialog
34 | return (
35 |
38 | )
39 | }
40 | }
41 | ```
42 | Here we have a component called `EmployeeProfile` (any other valid React component would also work here) as a "child" of `Dialog`. When we pass a "child" to a component it will be available inside that component as a `props` called `children`. So in this case, `EmployeeProfile` will be available inside the `Dialog` component as `props.children`. We can rewrite the render function of `Dialog` so that it renders `props.children`:
43 |
44 | ```jsx
45 | class Dialog extends React.Component {
46 | render(){
47 | //Render the children provided to this component
48 | //Here we don't really care what the children is
49 | return (
50 |
51 | {this.props.children}
52 |
53 | )
54 | }
55 | }
56 | ```
57 | Think about what we did here. The `Dialog` component really doesn't know what it's `children` would be beforehand. Whoever is using this `Dialog` component can pass in any `children` that they like. Within the `render` function `Dialog` says "Hey, I'll display anything my user passes me as `children`, I don't need to know what that is."
58 |
59 | This my friend is composition and this is mighty powerful if you want to write reusable components.
60 |
61 | 2. render props
62 |
63 | Another pattern to achieve a similar thing in React is by using what's known as the "render props" pattern.
64 |
65 | So now let's take the same dialog example, but let's make it more sophisticated. Imagine the dialog has a header, body and footer section. We want the user of this component to be able to display anything they like within either of those three sections.
66 |
67 | ```jsx
68 | class Dialog extends React.Component {
69 | render(){
70 | return (
71 |
72 |
73 | {
74 | /** user of this component should be able to display any header inside here */
75 | }
76 |
77 |
78 | {
79 | /** user of this component should be able to display any body inside here */
80 | }
81 |
82 |
83 | {
84 | /** user of this component should be able to display any footer inside here */
85 | }
86 |
87 |
88 | )
89 | }
90 | }
91 | ```
92 |
93 | The `props.children` approach we used above might not work well here. All the children we pass as "children" would be available as `props.children` and it might be little cumbersome to split the children into our three segments - "header", "body" and "footer". Wouldn't it be nice if we could tell the `Dialog` component explicitly what it should render within each "header", "body" and "footer" section?
94 |
95 | We can use the "render props" pattern to do exactly that. All this means is that we can pass three render functions as `props` that tells the dialog what it should render inside each segment. For example, lets look at a usage:
96 |
97 | ```jsx
98 | class EmployeeProfileDialog extends React.Component {
99 | render(){
100 | return (
101 |