├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── 1 - HOC (Deprecated) ├── README.md ├── config │ ├── env.js │ ├── jest │ │ ├── CSSStub.js │ │ └── FileStub.js │ ├── paths.js │ ├── polyfills.js │ ├── webpack.config.dev.js │ └── webpack.config.prod.js ├── package.json ├── public │ ├── favicon.ico │ └── index.html ├── scripts │ ├── build.js │ ├── start.js │ └── test.js └── src │ ├── App.css │ ├── App.js │ ├── components │ ├── ContactList.js │ ├── ContactsApp.css │ ├── ContactsApp.js │ ├── HOC │ │ ├── LoadingHOC.css │ │ └── LoadingHOC.js │ └── SearchBar.js │ ├── index.css │ └── index.js ├── 10 - Redux Thunk Tricks ├── README.md ├── package.json ├── public │ ├── books │ │ ├── gameofthrones.jpg │ │ ├── harrypotter.jpg │ │ ├── lotr.jpg │ │ ├── murderorient.jpg │ │ ├── neuromancer.jpg │ │ ├── readyp1.jpg │ │ └── sherlockholmes.jpg │ ├── favicon.ico │ └── index.html └── src │ ├── App.js │ ├── actions │ ├── book.js │ ├── cart.js │ └── user.js │ ├── api │ └── index.js │ ├── components │ ├── Auth.js │ ├── BookDetails.css │ └── BookDetails.js │ ├── constants.js │ ├── index.css │ ├── index.js │ ├── reducers │ ├── books.js │ ├── cart.js │ ├── index.js │ └── user.js │ └── store.js ├── 11 - Recompose (Deprecated) ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── manifest.json │ └── pictures │ │ ├── classic.jpg │ │ ├── fast.jpg │ │ ├── intelligent.jpg │ │ ├── thriller.jpg │ │ ├── voodoo.jpg │ │ └── walkers-biters.jpg └── src │ ├── App.js │ ├── components │ ├── Card.css │ ├── Card.js │ ├── Spinner.css │ └── Spinner.js │ ├── index.css │ └── index.js ├── 12 - SSR pt1 (Deprecated) ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── bundle.js │ ├── bundle.js.map │ ├── css │ │ ├── main.css │ │ └── main.css.map │ └── media │ │ └── logo.svg ├── server.js ├── server.js.map ├── src │ ├── browser │ │ └── index.js │ ├── server │ │ └── index.js │ └── shared │ │ ├── App.css │ │ ├── App.js │ │ └── logo.svg └── webpack.config.js ├── 13 - SSR pt2 (Deprecated) ├── README.md ├── episode-source-code(redux) │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── bundle.js │ │ ├── bundle.js.map │ │ ├── css │ │ │ ├── main.css │ │ │ └── main.css.map │ │ ├── favicon.ico │ │ └── media │ │ │ ├── logo.png │ │ │ └── wizards.jpg │ ├── server.js │ ├── server.js.map │ ├── src │ │ ├── browser │ │ │ └── index.js │ │ ├── server │ │ │ └── index.js │ │ └── shared │ │ │ ├── App.css │ │ │ ├── App.js │ │ │ ├── configureStore.js │ │ │ ├── ducks.js │ │ │ ├── home │ │ │ ├── index.css │ │ │ ├── index.js │ │ │ ├── logo.png │ │ │ └── wizards.jpg │ │ │ ├── news │ │ │ ├── NewsList.css │ │ │ ├── NewsList.js │ │ │ └── index.js │ │ │ └── routes.js │ └── webpack.config.js └── episode-source-code │ ├── home │ ├── Home.css │ ├── Home.js │ ├── logo.png │ └── wizards.jpg │ ├── package-lock.json │ ├── package.json │ ├── public │ ├── bundle.js │ ├── bundle.js.map │ ├── css │ │ ├── main.css │ │ └── main.css.map │ ├── favicon.ico │ └── media │ │ ├── flags.png │ │ ├── icons.svg │ │ ├── logo.png │ │ ├── w18.png │ │ └── wizards.jpg │ ├── server.js │ ├── server.js.map │ ├── src │ ├── browser │ │ └── index.js │ ├── server │ │ └── index.js │ └── shared │ │ ├── App.css │ │ ├── App.js │ │ ├── news │ │ ├── News.js │ │ ├── NewsList.css │ │ ├── NewsList.js │ │ └── w18.png │ │ └── routes.js │ └── webpack.config.js ├── 14 - Big boring forms ├── .eslintcache ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt └── src │ ├── App.css │ ├── App.js │ ├── ContactForm.css │ ├── ContactForm.js │ ├── index.css │ └── index.js ├── 15 - React.memo ├── .eslintcache ├── .gitignore ├── README.md ├── knuth premature optimization.webp ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ ├── pictures │ │ ├── classic.jpg │ │ ├── fast.jpg │ │ ├── intelligent.jpg │ │ ├── thriller.jpg │ │ ├── voodoo.jpg │ │ └── walkers-biters.jpg │ └── robots.txt └── src │ ├── App.css │ ├── App.js │ ├── Card.css │ ├── Card.js │ ├── index.css │ └── index.js ├── 16 - Server components ├── .gitignore ├── .prettierignore ├── .prettierrc.js ├── README.md ├── credentials.json ├── package.json ├── public │ ├── books │ │ ├── gameofthrones.jpg │ │ ├── harrypotter.jpg │ │ ├── lotr.jpg │ │ ├── murderorient.jpg │ │ ├── neuromancer.jpg │ │ ├── readyp1.jpg │ │ └── sherlockholmes.jpg │ ├── favicon.ico │ └── index.html ├── scripts │ ├── build.js │ └── seed.js ├── server │ ├── api.server.js │ └── package.json └── src │ ├── App.server.js │ ├── BookDetails.server.js │ ├── BookList.client.js │ ├── BookList.server.js │ ├── BuyButton.client.js │ ├── Cache.client.js │ ├── Root.client.js │ ├── SelectedBookContext.client.js │ ├── SimilarBooks.server.js │ ├── db.server.js │ ├── index.client.css │ └── index.client.js ├── 17 - An alternative to ejecting in CRA └── README.md ├── 18 - Hook stale closures ├── .gitignore ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── src │ ├── App.css │ ├── App.jsx │ ├── index.css │ └── main.jsx └── vite.config.js ├── 19 - State machines ├── .gitignore ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── src │ ├── StopWatch.css │ ├── StopWatch.jsx │ ├── formatTime.js │ ├── index.css │ └── main.jsx └── vite.config.js ├── 2 - Render Props ├── README.md ├── package.json ├── public │ ├── favicon.ico │ └── index.html └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── faac │ └── ScrollPos.js │ ├── index.css │ └── index.js ├── 20 - Custom State machine Hook ├── .gitignore ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── src │ ├── StopWatch.css │ ├── StopWatch.tsx │ ├── formatTime.js │ ├── index.css │ └── main.jsx ├── tsconfig.json └── vite.config.js ├── 3 - Children API ├── README.md ├── package.json ├── public │ ├── favicon.ico │ └── index.html └── src │ ├── App.css │ ├── App.js │ ├── Parent.js │ ├── SlideShow.css │ ├── SlideShow.js │ ├── index.css │ └── index.js ├── 4 - Context pt1 (Deprecated) ├── README.md ├── package.json ├── public │ ├── favicon.ico │ └── index.html └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── components │ ├── ContentPanel.js │ ├── InternalPanel.js │ └── Panel.js │ ├── index.css │ ├── index.js │ └── locales │ ├── en.json │ └── pt.json ├── 5 - Context pt2 (Deprecated) ├── README.md ├── package.json ├── public │ ├── favicon.ico │ └── index.html └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── components │ ├── ContentPanel.js │ ├── InternalPanel.js │ ├── Panel.js │ └── WithLocaleHOC.js │ ├── index.css │ ├── index.js │ └── locales │ ├── Locale.js │ ├── en.json │ └── pt.json ├── 6 - Redux Middleware ├── README.md ├── package.json ├── public │ └── index.html └── src │ ├── actions │ └── index.js │ ├── components │ ├── Footer.js │ ├── Link.js │ ├── Todo.css │ ├── Todo.js │ └── TodoList.js │ ├── constants.js │ ├── containers │ ├── AddTodo.js │ ├── App.js │ ├── FilterLink.js │ └── VisibleTodoList.js │ ├── index.css │ ├── index.js │ └── reducers │ ├── index.js │ ├── todos.js │ └── visibilityFilter.js ├── 7 - HMR (Deprecated) ├── README.md ├── hmr-quick │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ └── index.html │ └── src │ │ ├── App.css │ │ ├── App.js │ │ ├── App.test.js │ │ ├── index.css │ │ ├── index.js │ │ └── logo.svg └── hrm-with-react-hot-loader │ ├── .gitignore │ ├── README.md │ ├── config │ ├── env.js │ ├── jest │ │ ├── CSSStub.js │ │ └── FileStub.js │ ├── paths.js │ ├── polyfills.js │ ├── webpack.config.dev.js │ └── webpack.config.prod.js │ ├── package.json │ ├── public │ ├── favicon.ico │ └── index.html │ ├── scripts │ ├── build.js │ ├── start.js │ └── test.js │ └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── index.css │ ├── index.js │ └── logo.svg ├── 8 - Selectors in Redux ├── README.md ├── package.json ├── public │ ├── fakeserver_images │ │ ├── deluxe-city.jpg │ │ ├── deluxe-ocean.jpg │ │ ├── grandlux.jpg │ │ ├── royal.jpg │ │ ├── standard-city.jpg │ │ └── standard-ocean.jpg │ ├── favicon.ico │ └── index.html ├── src │ ├── App.css │ ├── App.js │ ├── actions │ │ ├── auth.js │ │ └── rooms.js │ ├── api │ │ └── fakeApi.js │ ├── components │ │ ├── SlideShow.css │ │ ├── SlideShow.js │ │ └── background.jpg │ ├── constants.js │ ├── index.css │ ├── index.js │ ├── logo.png │ └── reducers │ │ ├── auth.js │ │ ├── index.js │ │ └── rooms.js └── yarn.lock ├── 9 - Immutability in JS ├── README.md ├── index.js ├── package-lock.json └── package.json ├── LICENSE └── README.md /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | 4 | # misc 5 | .DS_Store 6 | .env 7 | npm-debug.log 8 | -------------------------------------------------------------------------------- /1 - HOC (Deprecated)/README.md: -------------------------------------------------------------------------------- 1 | # Higher Order Components 2 | 3 | ReactCasts, episode 1. 4 | 5 | In simple terms, we could say that a Higher Order Component is a function that accepts a component as parameter and returns another component that wraps it. 6 | 7 | This screencast shows how to create Higher Order Components and give examples of how it can be useful. 8 | 9 | Screencast video: 10 | https://www.youtube.com/watch?v=LTunyI2Oyzw 11 | 12 | # Outline 13 | 14 | - What is a Higher Order Component 15 | - Render highjacking 16 | - Usage as a decorator 17 | - Using Curried functions for configuration 18 | - Manipulating Props 19 | 20 | # Build & Run Instructions 21 | 22 | 23 | 1. To build and run the code in this directory, ensure you have [npm](https://www.npmjs.com) installed 24 | 25 | 2. Install 26 | ``` 27 | npm install 28 | ``` 29 | 30 | 3. Start the application 31 | ``` 32 | npm start 33 | ``` 34 | -------------------------------------------------------------------------------- /1 - HOC (Deprecated)/config/env.js: -------------------------------------------------------------------------------- 1 | // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be 2 | // injected into the application via DefinePlugin in Webpack configuration. 3 | 4 | var REACT_APP = /^REACT_APP_/i; 5 | 6 | function getClientEnvironment(publicUrl) { 7 | return Object 8 | .keys(process.env) 9 | .filter(key => REACT_APP.test(key)) 10 | .reduce((env, key) => { 11 | env['process.env.' + key] = JSON.stringify(process.env[key]); 12 | return env; 13 | }, { 14 | // Useful for determining whether we’re running in production mode. 15 | // Most importantly, it switches React into the correct mode. 16 | 'process.env.NODE_ENV': JSON.stringify( 17 | process.env.NODE_ENV || 'development' 18 | ), 19 | // Useful for resolving the correct path to static assets in `public`. 20 | // For example, . 21 | // This should only be used as an escape hatch. Normally you would put 22 | // images into the `src` and `import` them in code to get their paths. 23 | 'process.env.PUBLIC_URL': JSON.stringify(publicUrl) 24 | }); 25 | } 26 | 27 | module.exports = getClientEnvironment; 28 | -------------------------------------------------------------------------------- /1 - HOC (Deprecated)/config/jest/CSSStub.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /1 - HOC (Deprecated)/config/jest/FileStub.js: -------------------------------------------------------------------------------- 1 | module.exports = "test-file-stub"; 2 | -------------------------------------------------------------------------------- /1 - HOC (Deprecated)/config/paths.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var fs = require('fs'); 3 | 4 | // Make sure any symlinks in the project folder are resolved: 5 | // https://github.com/facebookincubator/create-react-app/issues/637 6 | var appDirectory = fs.realpathSync(process.cwd()); 7 | function resolveApp(relativePath) { 8 | return path.resolve(appDirectory, relativePath); 9 | } 10 | 11 | // We support resolving modules according to `NODE_PATH`. 12 | // This lets you use absolute paths in imports inside large monorepos: 13 | // https://github.com/facebookincubator/create-react-app/issues/253. 14 | 15 | // It works similar to `NODE_PATH` in Node itself: 16 | // https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders 17 | 18 | // We will export `nodePaths` as an array of absolute paths. 19 | // It will then be used by Webpack configs. 20 | // Jest doesn’t need this because it already handles `NODE_PATH` out of the box. 21 | 22 | var nodePaths = (process.env.NODE_PATH || '') 23 | .split(process.platform === 'win32' ? ';' : ':') 24 | .filter(Boolean) 25 | .map(resolveApp); 26 | 27 | // config after eject: we're in ./config/ 28 | module.exports = { 29 | appBuild: resolveApp('build'), 30 | appPublic: resolveApp('public'), 31 | appHtml: resolveApp('public/index.html'), 32 | appIndexJs: resolveApp('src/index.js'), 33 | appPackageJson: resolveApp('package.json'), 34 | appSrc: resolveApp('src'), 35 | testsSetup: resolveApp('src/setupTests.js'), 36 | appNodeModules: resolveApp('node_modules'), 37 | ownNodeModules: resolveApp('node_modules'), 38 | nodePaths: nodePaths 39 | }; 40 | -------------------------------------------------------------------------------- /1 - HOC (Deprecated)/config/polyfills.js: -------------------------------------------------------------------------------- 1 | if (typeof Promise === 'undefined') { 2 | // Rejection tracking prevents a common issue where React gets into an 3 | // inconsistent state due to an error, but it gets swallowed by a Promise, 4 | // and the user has no idea what causes React's erratic future behavior. 5 | require('promise/lib/rejection-tracking').enable(); 6 | window.Promise = require('promise/lib/es6-extensions.js'); 7 | } 8 | 9 | // fetch() polyfill for making API calls. 10 | require('whatwg-fetch'); 11 | 12 | // Object.assign() is commonly used with React. 13 | // It will use the native implementation if it's present and isn't buggy. 14 | Object.assign = require('object-assign'); 15 | -------------------------------------------------------------------------------- /1 - HOC (Deprecated)/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/1 - HOC (Deprecated)/public/favicon.ico -------------------------------------------------------------------------------- /1 - HOC (Deprecated)/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | React App 17 | 18 | 19 |
20 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /1 - HOC (Deprecated)/scripts/test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = 'test'; 2 | process.env.PUBLIC_URL = ''; 3 | 4 | // Load environment variables from .env file. Surpress warnings using silent 5 | // if this file is missing. dotenv will never modify any environment variables 6 | // that have already been set. 7 | // https://github.com/motdotla/dotenv 8 | require('dotenv').config({silent: true}); 9 | 10 | const jest = require('jest'); 11 | const argv = process.argv.slice(2); 12 | 13 | // Watch unless on CI 14 | if (!process.env.CI) { 15 | argv.push('--watch'); 16 | } 17 | 18 | 19 | jest.run(argv); 20 | -------------------------------------------------------------------------------- /1 - HOC (Deprecated)/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | width: 300px; 3 | margin: auto; 4 | } 5 | -------------------------------------------------------------------------------- /1 - HOC (Deprecated)/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ContactsApp from './components/ContactsApp'; 3 | import './App.css'; 4 | 5 | class App extends Component { 6 | state = { contacts: [] } 7 | 8 | componentDidMount() { 9 | fetch('https://api.randomuser.me/?nat=us,gb&results=50') 10 | .then(response => response.json()) 11 | .then(parsedResponse => parsedResponse.results.map(user => ( 12 | { 13 | name: `${user.name.first} ${user.name.last}`, 14 | email: user.email, 15 | thumbnail: user.picture.thumbnail 16 | } 17 | ))) 18 | .then(contacts => this.setState({contacts})); 19 | } 20 | 21 | render() { 22 | return ( 23 |
24 | 25 |
26 | ); 27 | } 28 | } 29 | 30 | export default App; 31 | -------------------------------------------------------------------------------- /1 - HOC (Deprecated)/src/components/ContactList.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | 3 | const ContactList = ({ contacts, filterText }) => { 4 | const filteredContacts = contacts.filter(contact => contact.name.indexOf(filterText) !== -1); 5 | 6 | return( 7 | 17 | ) 18 | } 19 | 20 | ContactList.propTypes = { 21 | contacts: PropTypes.arrayOf(PropTypes.object), 22 | filterText: PropTypes.string.isRequired 23 | } 24 | 25 | export default ContactList; 26 | -------------------------------------------------------------------------------- /1 - HOC (Deprecated)/src/components/ContactsApp.css: -------------------------------------------------------------------------------- 1 | .contactApp { 2 | width: 300px; 3 | } 4 | 5 | .contactApp > *{ 6 | width: 100%; 7 | } 8 | 9 | ul { 10 | padding: 0; 11 | } 12 | 13 | li { 14 | list-style: none; 15 | margin: 5px; 16 | } 17 | 18 | .contactData { 19 | padding-top: 5px; 20 | height: 40px; 21 | } 22 | 23 | li img{ 24 | border-radius: 50%; 25 | margin-right: 10px; 26 | float: left; 27 | } 28 | -------------------------------------------------------------------------------- /1 - HOC (Deprecated)/src/components/ContactsApp.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import SearchBar from './SearchBar'; 3 | import ContactList from './ContactList'; 4 | import LoadingHOC from './HOC/LoadingHOC' 5 | import './ContactsApp.css'; 6 | 7 | @LoadingHOC('contacts') 8 | class ContactsApp extends Component { 9 | state = { 10 | filterText: '' 11 | }; 12 | 13 | static propTypes = { 14 | contacts: PropTypes.arrayOf( 15 | PropTypes.shape({ 16 | thumbnail: PropTypes.string.isRequired, 17 | name: PropTypes.string.isRequired, 18 | email: PropTypes.string 19 | }) 20 | ).isRequired, 21 | loadingTime: PropTypes.string 22 | } 23 | 24 | handleUserInput = (searchTerm) => { 25 | this.setState({filterText: searchTerm}) 26 | } 27 | 28 | render() { 29 | const { loadingTime } = this.props; 30 | return( 31 |
32 | 34 | 36 |

Loading time {loadingTime} seconds

37 |
38 | ) 39 | } 40 | } 41 | 42 | 43 | export default ContactsApp; 44 | -------------------------------------------------------------------------------- /1 - HOC (Deprecated)/src/components/HOC/LoadingHOC.css: -------------------------------------------------------------------------------- 1 | .loader { 2 | position: absolute; 3 | top: 50%; 4 | left: 50%; 5 | -webkit-transform: translateX(-50%) translateY(-50%); 6 | -ms-transform: translateX(-50%) translateY(-50%); 7 | transform: translateX(-50%) translateY(-50%); 8 | } 9 | 10 | /* Static Shape */ 11 | 12 | .loader:before { 13 | position: absolute; 14 | content: ''; 15 | top: 0%; 16 | left: 50%; 17 | width: 100%; 18 | height: 100%; 19 | border-radius: 500rem; 20 | border: 0.2em solid rgba(0, 0, 0, 0.1); 21 | } 22 | 23 | /* Active Shape */ 24 | 25 | .loader:after { 26 | position: absolute; 27 | content: ''; 28 | top: 0%; 29 | left: 50%; 30 | width: 100%; 31 | height: 100%; 32 | animation: loader 0.6s linear; 33 | animation-iteration-count: infinite; 34 | border-radius: 500rem; 35 | border-color: #767676 transparent transparent; 36 | border-style: solid; 37 | border-width: 0.2em; 38 | box-shadow: 0px 0px 0px 1px transparent; 39 | } 40 | 41 | /* Active Animation */ 42 | 43 | @-webkit-keyframes loader { 44 | from { 45 | -webkit-transform: rotate(0deg); 46 | transform: rotate(0deg); 47 | } 48 | 49 | to { 50 | -webkit-transform: rotate(360deg); 51 | transform: rotate(360deg); 52 | } 53 | } 54 | 55 | @keyframes loader { 56 | from { 57 | -webkit-transform: rotate(0deg); 58 | transform: rotate(0deg); 59 | } 60 | 61 | to { 62 | -webkit-transform: rotate(360deg); 63 | transform: rotate(360deg); 64 | } 65 | } 66 | 67 | .loader:before, 68 | .loader:after { 69 | width: 2.28571429rem; 70 | height: 2.28571429rem; 71 | margin: 0em; 72 | } 73 | -------------------------------------------------------------------------------- /1 - HOC (Deprecated)/src/components/HOC/LoadingHOC.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import './LoadingHOC.css'; 3 | 4 | const isEmpty = (prop) => ( 5 | prop === null || 6 | prop === undefined || 7 | (prop.hasOwnProperty('length') && prop.length === 0) || 8 | (prop.constructor === Object && Object.keys(prop).length === 0) 9 | ); 10 | 11 | const LoadingHOC = (loadingProp) => (WrappedComponent) => { 12 | return class LoadingHOC extends Component { 13 | componentDidMount(){ 14 | this.startTimer = Date.now(); 15 | } 16 | 17 | componentWillUpdate(nextProps){ 18 | if(!isEmpty(nextProps[loadingProp])) { 19 | this.endTimer = Date.now(); 20 | } 21 | } 22 | 23 | render() { 24 | const myProps = { 25 | loadingTime: ((this.endTimer - this.startTimer)/1000).toFixed(2), 26 | }; 27 | 28 | return isEmpty(this.props[loadingProp]) ?
: ; 29 | } 30 | } 31 | } 32 | 33 | 34 | export default LoadingHOC; 35 | -------------------------------------------------------------------------------- /1 - HOC (Deprecated)/src/components/SearchBar.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react'; 2 | 3 | const getInput = (event) => event.target.value; 4 | 5 | const SearchBar = ({ filterText, onUserInput }) => ( 6 | onUserInput(getInput(event)) } 11 | /> 12 | ); 13 | 14 | SearchBar.propTypes = { 15 | onUserInput: PropTypes.func.isRequired, 16 | filterText: PropTypes.string.isRequired 17 | }; 18 | 19 | export default SearchBar; 20 | -------------------------------------------------------------------------------- /1 - HOC (Deprecated)/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /1 - HOC (Deprecated)/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import './index.css'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /10 - Redux Thunk Tricks/README.md: -------------------------------------------------------------------------------- 1 | # Redux Thunk Tricks 2 | 3 | ReactCasts, episode 10. 4 | 5 | Redux Thunk is the most used library for side effects and asyncronous calls in Redux - and that's for a reason. But despite being so used, there are a few useful thunk tricks that aren't yet commonplace, so in this episode I'll share some pieces of thunk knowledge that might help you build better applications. 6 | 7 | Screencast video: 8 | https://youtu.be/xihoZZU0gao 9 | 10 | # Outline 11 | 12 | - The first tip is very simple: You can return values from thunks, and this can be useful, for example, for asynchronous orchestration. 13 | 14 | - The second tip is about thunk's getState, and how it can be a bad idea to rely on this mechanism for accessing data. 15 | 16 | - The third tip is about using thunk withExtraArguments to make your thunks easy to test and run on multiple environments. 17 | 18 | 19 | # Build & Run Instructions 20 | 21 | 1. To build and run the code in this directory, ensure you have [npm](https://www.npmjs.com) installed 22 | 23 | 2. Install 24 | ``` 25 | npm install 26 | ``` 27 | 28 | 3. Start the application 29 | ``` 30 | npm start 31 | ``` 32 | -------------------------------------------------------------------------------- /10 - Redux Thunk Tricks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bookstore", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "lodash.intersection": "^4.4.0", 7 | "react": "^15.5.4", 8 | "react-dom": "^15.5.4", 9 | "react-redux": "^5.0.4", 10 | "redux": "^3.6.0", 11 | "semantic-ui-react": "^0.68.2" 12 | }, 13 | "devDependencies": { 14 | "react-scripts": "0.9.5" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test --env=jsdom", 20 | "eject": "react-scripts eject" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /10 - Redux Thunk Tricks/public/books/gameofthrones.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/10 - Redux Thunk Tricks/public/books/gameofthrones.jpg -------------------------------------------------------------------------------- /10 - Redux Thunk Tricks/public/books/harrypotter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/10 - Redux Thunk Tricks/public/books/harrypotter.jpg -------------------------------------------------------------------------------- /10 - Redux Thunk Tricks/public/books/lotr.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/10 - Redux Thunk Tricks/public/books/lotr.jpg -------------------------------------------------------------------------------- /10 - Redux Thunk Tricks/public/books/murderorient.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/10 - Redux Thunk Tricks/public/books/murderorient.jpg -------------------------------------------------------------------------------- /10 - Redux Thunk Tricks/public/books/neuromancer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/10 - Redux Thunk Tricks/public/books/neuromancer.jpg -------------------------------------------------------------------------------- /10 - Redux Thunk Tricks/public/books/readyp1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/10 - Redux Thunk Tricks/public/books/readyp1.jpg -------------------------------------------------------------------------------- /10 - Redux Thunk Tricks/public/books/sherlockholmes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/10 - Redux Thunk Tricks/public/books/sherlockholmes.jpg -------------------------------------------------------------------------------- /10 - Redux Thunk Tricks/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/10 - Redux Thunk Tricks/public/favicon.ico -------------------------------------------------------------------------------- /10 - Redux Thunk Tricks/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 17 | React App 18 | 19 | 20 |
21 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /10 - Redux Thunk Tricks/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Dropdown, Segment } from 'semantic-ui-react' 3 | import Auth from './components/Auth'; 4 | import BookDetails from './components/BookDetails'; 5 | 6 | class App extends Component { 7 | state = { 8 | selectedId: null 9 | } 10 | render() { 11 | return ( 12 |
13 | 14 | { this.state.selectedId ? 15 | 16 | : 17 | 18 | this.setState({selectedId: selected.value})} 30 | fluid 31 | search 32 | selection 33 | /> 34 | 35 | } 36 |
37 | ); 38 | } 39 | } 40 | 41 | export default App; 42 | -------------------------------------------------------------------------------- /10 - Redux Thunk Tricks/src/actions/book.js: -------------------------------------------------------------------------------- 1 | import { 2 | BOOK_REQUESTING, BOOK_SUCCESS, BOOK_FAILURE, SIMILAR_REQUESTING, SIMILAR_SUCCESS, SIMILAR_FAILURE 3 | } from '../constants'; 4 | 5 | const entryLoading = id => ({ type: BOOK_REQUESTING, payload: id }); 6 | const entryLoaded = book => ({ type: BOOK_SUCCESS, payload: book }); 7 | const entryLoadError = () => ({ type: BOOK_FAILURE }); 8 | 9 | export const requestBook = id => ( 10 | (dispatch, getState, api) => { 11 | dispatch(entryLoading(id)); 12 | return api.fetchBook(id) 13 | .then(book => { 14 | dispatch( entryLoaded(book) ); 15 | return book; 16 | }) 17 | .catch(err => { 18 | dispatch( entryLoadError() ); 19 | }); 20 | } 21 | ); 22 | 23 | const similarEntriesLoading = tags => ({ type: SIMILAR_REQUESTING, payload: tags }); 24 | const similarEntriesLoaded = books => ({ type: SIMILAR_SUCCESS, payload: books }); 25 | const similarEntriesLoadError = () => ({ type: SIMILAR_FAILURE }); 26 | 27 | export const requestBooksByTags = (id, tags) => ( 28 | (dispatch, getState, api) => { 29 | dispatch(similarEntriesLoading(tags)); 30 | api.fetchBooksByTags(tags) 31 | .then(books => { 32 | dispatch( similarEntriesLoaded(books.filter(p => p.id !== id)) ); 33 | }) 34 | .catch(err => { 35 | dispatch( similarEntriesLoadError() ); 36 | }); 37 | } 38 | ); 39 | 40 | 41 | export const requestBookAndSimilars = (id) => ( 42 | dispatch => { 43 | dispatch(requestBook(id)).then(book => dispatch(requestBooksByTags(id, book.tags))); 44 | } 45 | ) 46 | -------------------------------------------------------------------------------- /10 - Redux Thunk Tricks/src/actions/cart.js: -------------------------------------------------------------------------------- 1 | import { ADDED_TO_CART } from '../constants'; 2 | 3 | const added = id => ({ type: ADDED_TO_CART, payload: id }); 4 | 5 | export const addToCart = (bookId) => ( 6 | (dispatch, getState, api) => { 7 | const user = getState().user; 8 | if(user){ 9 | api.addToCart(bookId) 10 | .then(dispatch(added(bookId))); 11 | } 12 | } 13 | ); 14 | -------------------------------------------------------------------------------- /10 - Redux Thunk Tricks/src/actions/user.js: -------------------------------------------------------------------------------- 1 | import { 2 | LOG_USER, LOGGED_USER, USER_FAILURE 3 | } from '../constants'; 4 | 5 | const userLoading = id => ({ type: LOG_USER, payload: id }); 6 | const userLoaded = user => ({ type: LOGGED_USER, payload: user }); 7 | const userLoadError = () => ({ type: USER_FAILURE }); 8 | 9 | export const getUser = () => ( 10 | (dispatch, getState, api) => { 11 | dispatch(userLoading); 12 | return api.getUser() 13 | .then(user => { 14 | dispatch( userLoaded(user) ); 15 | return user; 16 | }) 17 | .catch(err => { 18 | dispatch( userLoadError() ); 19 | }); 20 | } 21 | ); 22 | -------------------------------------------------------------------------------- /10 - Redux Thunk Tricks/src/components/Auth.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { getUser } from '../actions/user'; 4 | import { Button } from 'semantic-ui-react' 5 | 6 | class Auth extends Component { 7 | render() { 8 | const { user, getUser } = this.props 9 | return ( 10 | 13 | ) 14 | } 15 | } 16 | 17 | const mapStateToProps = (state) => { 18 | return { user: state.user }; 19 | } 20 | 21 | const mapDispatchToProps = { 22 | getUser 23 | } 24 | 25 | export default connect(mapStateToProps, mapDispatchToProps)(Auth); 26 | -------------------------------------------------------------------------------- /10 - Redux Thunk Tricks/src/components/BookDetails.css: -------------------------------------------------------------------------------- 1 | .cover { 2 | display: block; 3 | height: 305px; 4 | float: left; 5 | margin-right: 15px; 6 | } 7 | 8 | .segment::after { 9 | content: ''; 10 | display: block; 11 | clear: both; 12 | } 13 | 14 | .similar { 15 | margin-top: 29px; 16 | } 17 | 18 | .similar_cover { 19 | display: block; 20 | height: 125px; 21 | float: left; 22 | margin-right: 15px; 23 | } 24 | 25 | .segment .ui.button { 26 | display: block; 27 | margin: 15px 0; 28 | } 29 | -------------------------------------------------------------------------------- /10 - Redux Thunk Tricks/src/components/BookDetails.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Segment, Label, Button } from 'semantic-ui-react' 3 | import { connect } from 'react-redux'; 4 | import { requestBookAndSimilars } from '../actions/book'; 5 | import { addToCart } from '../actions/cart'; 6 | 7 | import './BookDetails.css'; 8 | 9 | class BookDetails extends Component { 10 | componentDidMount() { 11 | this.props.requestBookAndSimilars(this.props.id); 12 | } 13 | 14 | handleAddToCart = () => { 15 | this.props.addToCart(this.props.id); 16 | } 17 | 18 | render() { 19 | const { id, series, title, author, image, tags, similarBooks } = this.props; 20 | return ( 21 | 22 | {series} 23 | 24 | {name} 25 |

ID: {id} - {title}

26 |

By {author}

27 | { tags && tags.map(tag => ) } 28 | 17 | {zombies.map((zombie) => ( 18 | 24 | ))} 25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /15 - React.memo/src/Card.css: -------------------------------------------------------------------------------- 1 | .card { 2 | float: left; 3 | width: 100%; 4 | margin: 10px 0; 5 | padding: 16px 24px; 6 | color: #ededed; 7 | background-color: #282c35; 8 | border-radius: 4px; 9 | box-shadow: 0 2px 8px 0 #1a1c20; 10 | } 11 | 12 | .card.open .header::before { 13 | content: "▼ "; 14 | } 15 | 16 | .card.closed .header::before { 17 | content: "► "; 18 | } 19 | 20 | .header { 21 | font-weight: bold; 22 | cursor: pointer; 23 | } 24 | 25 | .card.closed .body { 26 | display: none; 27 | } 28 | 29 | .body > img { 30 | margin: 16px -24px 16px; 31 | width: calc(100% + 16px + 16px + 16px); 32 | } 33 | -------------------------------------------------------------------------------- /15 - React.memo/src/Card.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, memo } from "react"; 2 | import "./Card.css"; 3 | 4 | export default memo(function Card({ title, picture, description }) { 5 | const [opened, setOpened] = useState(true); 6 | useEffect(() => { 7 | console.log(`Card Rerendered: ${title}`); 8 | }); 9 | 10 | return ( 11 |
12 |
setOpened(!opened)}> 13 | {title} 14 |
15 |
16 | 17 |

{description}

18 |
19 |
20 | ); 21 | }); 22 | -------------------------------------------------------------------------------- /15 - React.memo/src/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | margin: 10px auto; 7 | max-width: 500px; 8 | background-color: #1d1f23; 9 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 10 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 11 | sans-serif; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | } 15 | 16 | code { 17 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 18 | monospace; 19 | } 20 | -------------------------------------------------------------------------------- /16 - Server components/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | /dist 14 | 15 | # notes 16 | notes/*.md 17 | 18 | # misc 19 | .DS_Store 20 | .eslintcache 21 | .env.local 22 | .env.development.local 23 | .env.test.local 24 | .env.production.local 25 | 26 | npm-debug.log* 27 | yarn-debug.log* 28 | yarn-error.log* 29 | 30 | # vscode 31 | .vscode -------------------------------------------------------------------------------- /16 - Server components/.prettierignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | /.pnp 4 | .pnp.js 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | /dist 12 | 13 | # misc 14 | .DS_Store 15 | .eslintcache 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | *.html 26 | *.json 27 | *.md 28 | -------------------------------------------------------------------------------- /16 - Server components/.prettierrc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | */ 8 | 9 | 'use strict'; 10 | 11 | module.exports = { 12 | arrowParens: 'always', 13 | bracketSpacing: false, 14 | singleQuote: true, 15 | jsxBracketSameLine: true, 16 | trailingComma: 'es5', 17 | printWidth: 80, 18 | }; 19 | -------------------------------------------------------------------------------- /16 - Server components/README.md: -------------------------------------------------------------------------------- 1 | # A glimpse at React Server Components 2 | 3 | Server Components is a proposal that might represent the future of how we create React Applications. 4 | 5 | Screencast video: 6 | [https://youtu.be/B7W0bAO5Hj4](https://youtu.be/B7W0bAO5Hj4) 7 | 8 | # How to setup this Demo 9 | 10 | ``` 11 | npm install 12 | npm start 13 | ``` 14 | 15 | (Or `npm run start:prod` for a production build.) 16 | 17 | Then open http://localhost:4000. 18 | 19 | The app won't work until you set up the database, as described below. 20 | 21 | ## DB Setup 22 | 23 | This demo uses Postgres. First, follow its [installation link](https://wiki.postgresql.org/wiki/Detailed_installation_guides) for your platform. 24 | 25 | The below example will setup the database for this app, assuming that you have a UNIX-like platform: 26 | 27 | ### Step 1. Create the Database 28 | 29 | ``` 30 | psql postgres 31 | 32 | CREATE DATABASE bookstore; 33 | CREATE ROLE bookstoreadmin WITH LOGIN PASSWORD 'password'; 34 | ALTER ROLE bookstoreadmin WITH SUPERUSER; 35 | ALTER DATABASE bookstore OWNER TO bookstoreadmin; 36 | \q 37 | ``` 38 | 39 | ### Step 2. Run the seed script 40 | 41 | Finally, run `npm run seed` to create the table and populate some data. 42 | 43 | # Notes about this app 44 | 45 | Based on the React Team's [Server component demo](https://github.com/reactjs/server-components-demo). It consists of a few major parts: 46 | 47 | - It uses a Webpack plugin (not defined in this repo) that allows us to only include client components in build artifacts 48 | - An Express server that: 49 | - Serves API endpoints used in the app 50 | - Renders Server Components into a special format that we can read on the client 51 | - A React app containing Server and Client components used to build React Notes 52 | -------------------------------------------------------------------------------- /16 - Server components/credentials.json: -------------------------------------------------------------------------------- 1 | { 2 | "host": "localhost", 3 | "database": "bookstore", 4 | "user": "bookstoreadmin", 5 | "password": "password", 6 | "port": "5432" 7 | } 8 | -------------------------------------------------------------------------------- /16 - Server components/public/books/gameofthrones.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/16 - Server components/public/books/gameofthrones.jpg -------------------------------------------------------------------------------- /16 - Server components/public/books/harrypotter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/16 - Server components/public/books/harrypotter.jpg -------------------------------------------------------------------------------- /16 - Server components/public/books/lotr.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/16 - Server components/public/books/lotr.jpg -------------------------------------------------------------------------------- /16 - Server components/public/books/murderorient.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/16 - Server components/public/books/murderorient.jpg -------------------------------------------------------------------------------- /16 - Server components/public/books/neuromancer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/16 - Server components/public/books/neuromancer.jpg -------------------------------------------------------------------------------- /16 - Server components/public/books/readyp1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/16 - Server components/public/books/readyp1.jpg -------------------------------------------------------------------------------- /16 - Server components/public/books/sherlockholmes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/16 - Server components/public/books/sherlockholmes.jpg -------------------------------------------------------------------------------- /16 - Server components/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/16 - Server components/public/favicon.ico -------------------------------------------------------------------------------- /16 - Server components/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | React Bookstore 8 | 9 | 10 |
11 | 12 | 13 | -------------------------------------------------------------------------------- /16 - Server components/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "commonjs", 3 | "main": "./api.server.js" 4 | } 5 | -------------------------------------------------------------------------------- /16 - Server components/src/App.server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | */ 8 | 9 | import {Suspense} from 'react'; 10 | import BookList from './BookList.server'; 11 | import BookDetails from './BookDetails.server'; 12 | 13 | export default function App({selectedId}) { 14 | return ( 15 |
16 |

📚React Bookstore (Server Components)

17 | 18 | Loading Book list...

}> 19 | 20 |
21 | 22 | Loading Book details...

}> 23 | {selectedId && } 24 |
25 |
26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /16 - Server components/src/BookDetails.server.js: -------------------------------------------------------------------------------- 1 | import {useState, useEffect} from 'react'; 2 | import SimilarBooks from './SimilarBooks.server'; 3 | import BuyButton from './BuyButton.client'; 4 | import {db} from './db.server'; 5 | 6 | export default function BookDetails({id}) { 7 | const book = db.query('select * from books where id = $1', [id]).rows[0]; 8 | return ( 9 |
10 | {book.title} 11 |

12 | {book.series && {book.series}: } 13 | {book.title} 14 |

15 |

By {book.author}

16 | {book.tags?.map((tag) => ( 17 |
18 | {tag} 19 |
20 | ))} 21 | 22 | 23 | 24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /16 - Server components/src/BookList.client.js: -------------------------------------------------------------------------------- 1 | import {useState, useRef, useEffect, unstable_useTransition} from 'react'; 2 | 3 | import {useSelectedBook} from './SelectedBookContext.client'; 4 | 5 | import SelectSearch from 'react-select-search'; 6 | 7 | export default function BookList({books}) { 8 | const [selectedBook, setSelectedBook] = useSelectedBook(); 9 | const [startTransition, isPending] = unstable_useTransition(); 10 | return ( 11 | { 15 | startTransition(() => { 16 | setSelectedBook(() => ({ 17 | selectedId: selected.value, 18 | })); 19 | }); 20 | }} 21 | search 22 | /> 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /16 - Server components/src/BookList.server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | */ 8 | 9 | import {db} from './db.server'; 10 | import ClientBookList from './BookList.client'; 11 | 12 | export default function BookList({selectedId, isEditing}) { 13 | const books = db.query(`select id, series, title from books order by id asc`) 14 | .rows; 15 | 16 | return ( 17 | ({ 19 | name: book.series || book.title, 20 | value: book.id, 21 | }))} 22 | /> 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /16 - Server components/src/BuyButton.client.js: -------------------------------------------------------------------------------- 1 | export default function BuyButton({id}) { 2 | return ( 3 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /16 - Server components/src/Cache.client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | */ 8 | 9 | import {unstable_getCacheForType, unstable_useCacheRefresh} from 'react'; 10 | import {createFromFetch} from 'react-server-dom-webpack'; 11 | 12 | function createResponseCache() { 13 | return new Map(); 14 | } 15 | 16 | export function useRefresh() { 17 | const refreshCache = unstable_useCacheRefresh(); 18 | return function refresh(key, seededResponse) { 19 | refreshCache(createResponseCache, new Map([[key, seededResponse]])); 20 | }; 21 | } 22 | 23 | export function useServerResponse(location) { 24 | const key = JSON.stringify(location); 25 | const cache = unstable_getCacheForType(createResponseCache); 26 | let response = cache.get(key); 27 | if (response) { 28 | return response; 29 | } 30 | response = createFromFetch( 31 | fetch('/react?location=' + encodeURIComponent(key)) 32 | ); 33 | cache.set(key, response); 34 | return response; 35 | } 36 | -------------------------------------------------------------------------------- /16 - Server components/src/Root.client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | */ 8 | 9 | import {useState, Suspense} from 'react'; 10 | import {ErrorBoundary} from 'react-error-boundary'; 11 | 12 | import {useServerResponse} from './Cache.client'; 13 | import {SelectedBookContext} from './SelectedBookContext.client'; 14 | 15 | export default function Root({initialCache}) { 16 | return ( 17 | 18 | 19 | 20 | 21 | 22 | ); 23 | } 24 | 25 | function Content() { 26 | const [selectedId, setSelectedId] = useState({selectedId: null}); 27 | const response = useServerResponse(selectedId); 28 | return ( 29 | 30 | {response.readRoot()} 31 | 32 | ); 33 | } 34 | 35 | function Error({error}) { 36 | return ( 37 |
38 |

Application Error

39 |
{error.stack}
40 |
41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /16 - Server components/src/SelectedBookContext.client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | */ 8 | 9 | import {createContext, useContext} from 'react'; 10 | 11 | export const SelectedBookContext = createContext(); 12 | export function useSelectedBook() { 13 | return useContext(SelectedBookContext); 14 | } 15 | -------------------------------------------------------------------------------- /16 - Server components/src/SimilarBooks.server.js: -------------------------------------------------------------------------------- 1 | import {useState, useEffect} from 'react'; 2 | import {db} from './db.server'; 3 | 4 | export default function BookDetails({tags, currentBook}) { 5 | const similarBooks = db.query( 6 | 'select * from books where tags && $1 AND NOT id = $2', 7 | [tags, currentBook] 8 | ).rows; 9 | return ( 10 |
11 |

You might also like:

12 | {similarBooks.map((similar) => ( 13 | {similar.name} 19 | ))} 20 |
21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /16 - Server components/src/db.server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | */ 8 | 9 | import {Pool} from 'react-pg'; 10 | import credentials from '../credentials.json'; 11 | 12 | // Don't keep credentials in the source tree in a real app! 13 | export const db = new Pool(credentials); 14 | -------------------------------------------------------------------------------- /16 - Server components/src/index.client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | * 7 | */ 8 | 9 | import {unstable_createRoot} from 'react-dom'; 10 | import Root from './Root.client'; 11 | 12 | import './index.client.css'; 13 | 14 | const initialCache = new Map(); 15 | const root = unstable_createRoot(document.getElementById('root')); 16 | root.render(); 17 | -------------------------------------------------------------------------------- /18 - Hook stale closures/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local -------------------------------------------------------------------------------- /18 - Hook stale closures/README.md: -------------------------------------------------------------------------------- 1 | # Hooks & Stale Closures 2 | 3 | I really like hooks: It has an elegant API that is simple to use, declarative, easy to abstract & Reuse. But still, you can get yourself in a trap in some cases: Stale closures are an example. They happen when your useEffect or other hook is seeing older versions of your props and state, usually when you're doing something asynchronous (like an event or a timer). 4 | 5 | In this video, I show three examples of stale closures and how to deal with them properly using: 6 | • The useEffect's dependency array 7 | • useState setter in a functional format 8 | • using a ref instead of a piece of state 9 | 10 | Screencast video: 11 | [https://youtu.be/eVRDqtTCd74](https://youtu.be/eVRDqtTCd74) 12 | -------------------------------------------------------------------------------- /18 - Hook stale closures/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Typing Test 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /18 - Hook stale closures/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typing-test", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "serve": "vite preview" 8 | }, 9 | "dependencies": { 10 | "react": "^17.0.0", 11 | "react-dom": "^17.0.0", 12 | "react-st-modal": "^1.1.3" 13 | }, 14 | "devDependencies": { 15 | "@vitejs/plugin-react-refresh": "^1.1.0", 16 | "vite": "^2.0.0-beta.48" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /18 - Hook stale closures/src/App.css: -------------------------------------------------------------------------------- 1 | .app { 2 | width: 100%; 3 | margin: 0; 4 | min-width: 0; 5 | padding: 50px 24px; 6 | background-color: #282c35; 7 | border-radius: 4px; 8 | box-shadow: 0 2px 8px 0 #1a1c20; 9 | } 10 | 11 | .header { 12 | display: flex; 13 | flex-direction: column; 14 | margin-bottom: 1rem; 15 | } 16 | 17 | .timer { 18 | align-self: flex-end; 19 | } 20 | 21 | .typing-input { 22 | resize: none; 23 | width: inherit; 24 | height: 10em; 25 | background-color: transparent; 26 | color: #ededed; 27 | font-family: inherit; 28 | font-size: inherit; 29 | } 30 | -------------------------------------------------------------------------------- /18 - Hook stale closures/src/App.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from "react"; 2 | import { Alert, Confirm } from "react-st-modal"; 3 | import "./App.css"; 4 | 5 | function App() { 6 | const input = useRef(""); 7 | const [timer, setTimer] = useState(0); 8 | 9 | // useEffect(() => { 10 | // const handleKeydown = (e) => { 11 | // if (e.key === "Escape") { 12 | // const doneRatio = Math.round((input.length / text.length) * 100); 13 | // Confirm(`You already typed ${doneRatio}% of the text`, "Are you sure?"); 14 | // } 15 | // }; 16 | // document.addEventListener("keydown", handleKeydown); 17 | // return () => document.removeEventListener("keydown", handleKeydown); 18 | // }, [input]); 19 | 20 | useEffect(() => { 21 | const intervalId = setInterval(() => { 22 | setTimer((t) => t + 1); 23 | }, 1000); 24 | return () => clearInterval(intervalId); 25 | }, []); 26 | 27 | useEffect(() => { 28 | const timerId = setTimeout(() => { 29 | const wpm = input.current.split(" ").length; 30 | Alert(`Your speed is ${wpm} WPM.`, "Your WPM"); 31 | }, 5000); 32 | return () => clearTimeout(timerId); 33 | }, []); 34 | 35 | const text = 36 | "It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. Now, I knew that it was not you who had brought it down, so there only remained your niece and the maids. But if it were the maids, why should your son allow himself to be accused in their place? There could be no possible reason."; 37 | 38 | return ( 39 |
40 |
41 |

{text}

42 | {timer} 43 |
44 | 48 |
49 | ); 50 | } 51 | 52 | export default App; 53 | -------------------------------------------------------------------------------- /18 - Hook stale closures/src/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | background-color: #1d1f23; 7 | color: #ededed; 8 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 9 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 10 | sans-serif; 11 | font-size: 18px; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | } 15 | 16 | code { 17 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 18 | monospace; 19 | } 20 | -------------------------------------------------------------------------------- /18 - Hook stale closures/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById("root") 11 | ); 12 | -------------------------------------------------------------------------------- /18 - Hook stale closures/vite.config.js: -------------------------------------------------------------------------------- 1 | import reactRefresh from '@vitejs/plugin-react-refresh' 2 | 3 | /** 4 | * @type { import('vite').UserConfig } 5 | */ 6 | export default { 7 | plugins: [reactRefresh()] 8 | } 9 | -------------------------------------------------------------------------------- /19 - State machines/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local -------------------------------------------------------------------------------- /19 - State machines/README.md: -------------------------------------------------------------------------------- 1 | # State Machines 2 | 3 | So I've been thinking about doing a state machines video for quite a long time and I think that now is the moment because state machines have seen some soaring popularity in frontend development (mostly because of the great work David K piano has been doing with the Xstate library). 4 | 5 | And it's funny because state machines are extremely popular in other software development areas such as game development and embedded systems. 6 | 7 | In this video you will learn why state machines are important, and a small example using the XState library. 8 | 9 | Screencast video: 10 | [https://youtu.be/N0OaRdJuVlc](https://youtu.be/N0OaRdJuVlc) 11 | -------------------------------------------------------------------------------- /19 - State machines/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | XState StopWatch 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /19 - State machines/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sm-stopwatch", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "serve": "vite preview" 8 | }, 9 | "dependencies": { 10 | "@xstate/react": "^1.3.1", 11 | "react": "^17.0.0", 12 | "react-dom": "^17.0.0", 13 | "xstate": "^4.16.2" 14 | }, 15 | "devDependencies": { 16 | "@vitejs/plugin-react-refresh": "^1.1.0", 17 | "vite": "^2.0.0-beta.70" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /19 - State machines/src/StopWatch.css: -------------------------------------------------------------------------------- 1 | .StopWatch { 2 | width: 10em; 3 | } 4 | 5 | .display { 6 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 7 | monospace; 8 | font-size: 2em; 9 | text-align: center; 10 | } 11 | 12 | .controls { 13 | display: flex; 14 | justify-content: space-around; 15 | margin-top: 1em; 16 | } 17 | 18 | button { 19 | position: relative; 20 | width: 5em; 21 | height: 5em; 22 | border-radius: 100%; 23 | background-color: #383b4c; 24 | color: #fff; 25 | border: none; 26 | outline: none; 27 | } 28 | button:focus { 29 | font-weight: bold; 30 | } 31 | button:after { 32 | content: ""; 33 | position: absolute; 34 | border-radius: 100%; 35 | top: -5px; 36 | left: -5px; 37 | right: -5px; 38 | bottom: -5px; 39 | border: #383b4c 2px solid; 40 | } 41 | -------------------------------------------------------------------------------- /19 - State machines/src/formatTime.js: -------------------------------------------------------------------------------- 1 | export default function formatTime(time) { 2 | const mins = Math.floor(time / 600); 3 | const secs = Math.floor(time / 10) % 60; 4 | const ms = Math.floor(time % 10); 5 | 6 | if (secs < 10) return `${mins}:0${secs}.${ms}`; 7 | 8 | return `${mins}:${secs}.${ms}`; 9 | } 10 | -------------------------------------------------------------------------------- /19 - State machines/src/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | background-color: #1d1f23; 7 | color: #ededed; 8 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 9 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 10 | sans-serif; 11 | font-size: 24px; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | } 15 | -------------------------------------------------------------------------------- /19 - State machines/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import StopWatch from "./StopWatch"; 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById("root") 11 | ); 12 | -------------------------------------------------------------------------------- /19 - State machines/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import reactRefresh from '@vitejs/plugin-react-refresh' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [reactRefresh()] 7 | }) 8 | -------------------------------------------------------------------------------- /2 - Render Props/README.md: -------------------------------------------------------------------------------- 1 | # Function as Child Components 2 | 3 | ReactCasts, episode 2. 4 | 5 | “Function as Child Component” is a pattern where the parent component accepts a function as child, instead of a nested React Node. This screencast shows how to create a FaCC and use it for making information available to wrapped components. 6 | 7 | Screencast video: 8 | https://www.youtube.com/watch?v=WE3XAt9P8Ek 9 | 10 | # Outline 11 | 12 | - What are FaCC 13 | - Example: hypothetical use cases 14 | - Basic structure of a function as child component 15 | - Example: Scroll Position FaCC 16 | - Example: Refactor and Possible effects with the Scroll Position FaCC 17 | 18 | # Build & Run Instructions 19 | 20 | 1. To build and run the code in this directory, ensure you have [npm](https://www.npmjs.com) installed 21 | 22 | 2. Install 23 | ``` 24 | npm install 25 | ``` 26 | 27 | 3. Start the application 28 | ``` 29 | npm start 30 | ``` 31 | -------------------------------------------------------------------------------- /2 - Render Props/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "scrollPosition", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.6.1" 7 | }, 8 | "dependencies": { 9 | "react": "^15.3.2", 10 | "react-dom": "^15.3.2" 11 | }, 12 | "scripts": { 13 | "start": "react-scripts start", 14 | "build": "react-scripts build", 15 | "test": "react-scripts test --env=jsdom", 16 | "eject": "react-scripts eject" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /2 - Render Props/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/2 - Render Props/public/favicon.ico -------------------------------------------------------------------------------- /2 - Render Props/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | React App 17 | 18 | 19 |
20 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /2 - Render Props/src/App.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | font-size: 120px; 3 | } 4 | 5 | .App { 6 | margin: 50px; 7 | } 8 | 9 | .spacer { 10 | height: 1000px; 11 | } 12 | -------------------------------------------------------------------------------- /2 - Render Props/src/App.js: -------------------------------------------------------------------------------- 1 | 2 | import React, { Component } from 'react'; 3 | import ScrollPos from './faac/ScrollPos'; 4 | import './App.css'; 5 | 6 | class App extends Component { 7 | render() { 8 | return ( 9 |
10 |
11 | 12 | 13 | { 14 | position =>

{'Awesome Text!!!'.substr(0,position*15)}

15 | } 16 |
17 | 18 |
19 |
20 | ); 21 | } 22 | } 23 | 24 | export default App; 25 | -------------------------------------------------------------------------------- /2 - Render Props/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /2 - Render Props/src/faac/ScrollPos.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | 3 | class ScrollPos extends Component { 4 | 5 | static proptypes = { 6 | children: PropTypes.func.isRequired, 7 | } 8 | 9 | state = { 10 | position: null 11 | } 12 | 13 | componentDidMount() { 14 | window.addEventListener('scroll', this.handleScroll); 15 | } 16 | 17 | componentWillUnmount() { 18 | window.removeEventListener('scroll', this.handleScroll); 19 | } 20 | 21 | getBounds = (element) => { 22 | if (this.bounds) return; 23 | this.bounds = element && element.getBoundingClientRect(); 24 | } 25 | 26 | handleScroll = (event) => { 27 | const windowSize = window.innerHeight; 28 | const scrollTop = event.srcElement.body.scrollTop; 29 | const visibleSpace = windowSize + (this.bounds.bottom - this.bounds.top); 30 | let visibleRatio = ((windowSize + scrollTop) - this.bounds.top + this.bounds.height)/visibleSpace; 31 | 32 | if (visibleRatio < 0) visibleRatio = 0; 33 | if (visibleRatio > 1) visibleRatio = 1; 34 | 35 | this.setState({position: visibleRatio}) 36 | } 37 | 38 | render() { 39 | return ( 40 |
{ 41 | this.props.children(this.state.position) 42 | }
43 | ) 44 | } 45 | } 46 | 47 | export default ScrollPos; 48 | -------------------------------------------------------------------------------- /2 - Render Props/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /2 - Render Props/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import './index.css'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /20 - Custom State machine Hook/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local -------------------------------------------------------------------------------- /20 - Custom State machine Hook/README.md: -------------------------------------------------------------------------------- 1 | # Custom State Machine Hook 2 | 3 | In this episode, we will implement a State Machine in React using useReducer and useEffect. We will follow the same basic configuration format used by XState and implement enough features to run the previous video sample code without changes. 4 | With that done, we will explore how we can make it follow more idiomatic react patterns (Since it's built on top of React, might as well...). 5 | 6 | Ah, and if you use TypeScript, there is a bonus at the end of the video. 7 | 8 | ## Screencast video: 9 | 10 | [https://youtu.be/jF1tO2hTdC0](https://youtu.be/jF1tO2hTdC0) 11 | -------------------------------------------------------------------------------- /20 - Custom State machine Hook/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | XState StopWatch 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /20 - Custom State machine Hook/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sm-stopwatch", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "serve": "vite preview" 8 | }, 9 | "dependencies": { 10 | "@xstate/react": "^1.3.1", 11 | "react": "^17.0.0", 12 | "react-dom": "^17.0.0", 13 | "xstate": "^4.16.2" 14 | }, 15 | "devDependencies": { 16 | "@types/react": "^17.0.3", 17 | "@vitejs/plugin-react-refresh": "^1.1.0", 18 | "vite": "^2.0.0-beta.70" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /20 - Custom State machine Hook/src/StopWatch.css: -------------------------------------------------------------------------------- 1 | .StopWatch { 2 | width: 10em; 3 | } 4 | 5 | .display { 6 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 7 | monospace; 8 | font-size: 2em; 9 | text-align: center; 10 | } 11 | 12 | .controls { 13 | display: flex; 14 | justify-content: space-around; 15 | margin-top: 1em; 16 | } 17 | 18 | button { 19 | position: relative; 20 | width: 5em; 21 | height: 5em; 22 | border-radius: 100%; 23 | background-color: #383b4c; 24 | color: #fff; 25 | border: none; 26 | outline: none; 27 | } 28 | button:focus { 29 | font-weight: bold; 30 | } 31 | button:after { 32 | content: ""; 33 | position: absolute; 34 | border-radius: 100%; 35 | top: -5px; 36 | left: -5px; 37 | right: -5px; 38 | bottom: -5px; 39 | border: #383b4c 2px solid; 40 | } 41 | -------------------------------------------------------------------------------- /20 - Custom State machine Hook/src/formatTime.js: -------------------------------------------------------------------------------- 1 | export default function formatTime(time) { 2 | const mins = Math.floor(time / 60000); 3 | const secs = Math.floor(time / 1000) % 60; 4 | const ms = Math.floor((time / 100) % 10); 5 | 6 | if (secs < 10) return `${mins}:0${secs}.${ms}`; 7 | 8 | return `${mins}:${secs}.${ms}`; 9 | } 10 | -------------------------------------------------------------------------------- /20 - Custom State machine Hook/src/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | background-color: #1d1f23; 7 | color: #ededed; 8 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 9 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 10 | sans-serif; 11 | font-size: 24px; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | } 15 | -------------------------------------------------------------------------------- /20 - Custom State machine Hook/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import "./index.css"; 4 | import StopWatch from "./StopWatch"; 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById("root") 11 | ); 12 | -------------------------------------------------------------------------------- /20 - Custom State machine Hook/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "allowSyntheticDefaultImports": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "noEmit": true, 16 | "jsx": "react", 17 | "declaration": true 18 | }, 19 | "include": ["src"], 20 | 21 | "exclude": ["node_modules", "build", "scripts", "jest"] 22 | } 23 | -------------------------------------------------------------------------------- /20 - Custom State machine Hook/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import reactRefresh from '@vitejs/plugin-react-refresh' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [reactRefresh()] 7 | }) 8 | -------------------------------------------------------------------------------- /3 - Children API/README.md: -------------------------------------------------------------------------------- 1 | # React's Children API 2 | 3 | ReactCasts, episode 3. 4 | 5 | We have been discussing component composition patterns. This screencast shows React.Children, which plays an important role on direct parent-to-child composition. 6 | 7 | Screencast video: 8 | https://www.youtube.com/watch?v=DJ53-G8EbxE 9 | 10 | # Outline 11 | 12 | - Review of allowed child types 13 | - The props.children prop structure 14 | - Example: React.Children.only 15 | - Example: React.Children.count 16 | - Example: React.Children.map 17 | - Building a SlideShow Component 18 | - Example: React.Children.toArray 19 | 20 | # Build & Run Instructions 21 | 22 | 1. To build and run the code in this directory, ensure you have [npm](https://www.npmjs.com) installed 23 | 24 | 2. Install 25 | ``` 26 | npm install 27 | ``` 28 | 29 | 3. Start the application 30 | ``` 31 | npm start 32 | ``` 33 | -------------------------------------------------------------------------------- /3 - Children API/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "children", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.7.0" 7 | }, 8 | "dependencies": { 9 | "react": "^15.3.2", 10 | "react-addons-css-transition-group": "^15.3.2", 11 | "react-dom": "^15.3.2" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test --env=jsdom", 17 | "eject": "react-scripts eject" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /3 - Children API/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/3 - Children API/public/favicon.ico -------------------------------------------------------------------------------- /3 - Children API/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | React App 17 | 18 | 19 |
20 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /3 - Children API/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | -------------------------------------------------------------------------------- /3 - Children API/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import SlideShow from './SlideShow'; 3 | import './App.css'; 4 | 5 | class App extends Component { 6 | render() { 7 | return ( 8 |
9 | 10 | Cat Pic 11 | Cat Pic 12 | Cat Pic 13 | 14 |
15 | ); 16 | } 17 | } 18 | 19 | export default App; 20 | -------------------------------------------------------------------------------- /3 - Children API/src/Parent.js: -------------------------------------------------------------------------------- 1 | import React, { Component, Children } from 'react'; 2 | 3 | class Parent extends Component { 4 | render() { 5 | const buttons = Children.map(this.props.children, child => ( 6 | 9 | )); 10 | return ( 11 |
12 |

Total Children: { Children.count(this.props.children) }

13 | {buttons} 14 |
15 | ) 16 | } 17 | } 18 | 19 | export default Parent; 20 | -------------------------------------------------------------------------------- /3 - Children API/src/SlideShow.css: -------------------------------------------------------------------------------- 1 | .example-enter { 2 | opacity: 0.01; 3 | } 4 | 5 | .example-enter.example-enter-active { 6 | opacity: 1; 7 | transition: opacity 800ms ease-in; 8 | } 9 | 10 | .example-leave { 11 | opacity: 1; 12 | } 13 | 14 | .example-leave.example-leave-active { 15 | opacity: 0.01; 16 | transition: opacity 800ms ease-in; 17 | } 18 | 19 | .group { 20 | display: block; 21 | position: relative; 22 | width: 100%; 23 | height: 100%; 24 | } 25 | 26 | .group > * { 27 | position: absolute; 28 | top: 10px; 29 | left:0; 30 | right:0; 31 | margin-left:auto; 32 | margin-right:auto; 33 | } 34 | -------------------------------------------------------------------------------- /3 - Children API/src/SlideShow.js: -------------------------------------------------------------------------------- 1 | import React, { Component, Children } from 'react'; 2 | import ReactCSSTransitionGroup from 'react-addons-css-transition-group'; 3 | import './SlideShow.css'; 4 | 5 | class SlideShow extends Component { 6 | state = { 7 | total: 0, 8 | current: 0, 9 | } 10 | 11 | componentDidMount() { 12 | const { children } = this.props; 13 | this.setState({ total: Children.count(children) }); 14 | this.interval = setInterval(this.showNext, 3000); 15 | } 16 | 17 | componentWillUnmount() { 18 | clearInterval(this.interval); 19 | } 20 | 21 | showNext = () => { 22 | const { total, current } = this.state; 23 | this.setState({ 24 | current: current + 1 === total? 0 : current + 1 25 | }); 26 | }; 27 | 28 | render() { 29 | const { children } = this.props; 30 | const bullets = Array(this.state.total).fill("○"); 31 | bullets[this.state.current] = "●"; 32 | return ( 33 |
34 |
{bullets}
35 | 41 | {Children.toArray(children)[this.state.current]} 42 | 43 |
44 | ) 45 | } 46 | } 47 | 48 | export default SlideShow; 49 | -------------------------------------------------------------------------------- /3 - Children API/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'helvetica neue', sans-serif; 3 | } 4 | -------------------------------------------------------------------------------- /3 - Children API/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import './index.css'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /4 - Context pt1 (Deprecated)/README.md: -------------------------------------------------------------------------------- 1 | # Contexts (Part 1) 2 | 3 | ReactCasts, episode 4. 4 | 5 | This is part one of a two-part screencast. Context makes it possible to pass data through the component hierarchy, without needing intermediate components to know about it. It can be useful for data that never (or rarely) change, such as theming and localization, and this screencast shows how to use Context using localization as example. 6 | 7 | Screencast video: 8 | https://www.youtube.com/watch?v=lxq938kqIss 9 | 10 | # Outline 11 | 12 | - What is Context 13 | - Example: The sample application 14 | - Comparing with props 15 | - Brief overview of potential problems 16 | - Example: Providing a Context 17 | - Example: Consuming a Context 18 | - Example: Updating Context 19 | 20 | # Build & Run Instructions 21 | 22 | 1. To build and run the code in this directory, ensure you have [npm](https://www.npmjs.com) installed 23 | 24 | 2. Install 25 | ``` 26 | npm install 27 | ``` 28 | 29 | 3. Start the application 30 | ``` 31 | npm start 32 | ``` 33 | -------------------------------------------------------------------------------- /4 - Context pt1 (Deprecated)/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "i18n", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.7.0" 7 | }, 8 | "dependencies": { 9 | "react": "^15.3.2", 10 | "react-dom": "^15.3.2" 11 | }, 12 | "scripts": { 13 | "start": "react-scripts start", 14 | "build": "react-scripts build", 15 | "test": "react-scripts test --env=jsdom", 16 | "eject": "react-scripts eject" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /4 - Context pt1 (Deprecated)/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/4 - Context pt1 (Deprecated)/public/favicon.ico -------------------------------------------------------------------------------- /4 - Context pt1 (Deprecated)/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | React App 17 | 18 | 19 |
20 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /4 - Context pt1 (Deprecated)/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import Panel from './components/Panel'; 3 | import './App.css'; 4 | 5 | import en from './locales/en.json'; 6 | import pt from './locales/pt.json'; 7 | 8 | const locales = {en, pt}; 9 | 10 | class App extends Component { 11 | static childContextTypes = { 12 | locale: PropTypes.object 13 | } 14 | 15 | state = { 16 | currentLocale: 'en' 17 | } 18 | 19 | getChildContext() { 20 | return {locale: locales[this.state.currentLocale]} 21 | } 22 | 23 | changeLocale(locale){ 24 | this.setState({currentLocale: locale}) 25 | } 26 | 27 | render() { 28 | return ( 29 | 36 | ); 37 | } 38 | } 39 | 40 | export default App; 41 | -------------------------------------------------------------------------------- /4 - Context pt1 (Deprecated)/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /4 - Context pt1 (Deprecated)/src/components/ContentPanel.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | 3 | class ContentPanel extends Component { 4 | static contextTypes = { 5 | locale: PropTypes.object 6 | } 7 | render() { 8 | const { locale } = this.context; 9 | return ( 10 |
11 |

{locale.header}

12 |

13 | {locale.text} 14 |

15 | 16 |
ContentPanel.js
17 |
18 | ); 19 | } 20 | } 21 | 22 | export default ContentPanel; 23 | -------------------------------------------------------------------------------- /4 - Context pt1 (Deprecated)/src/components/InternalPanel.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ContentPane from './ContentPanel'; 3 | 4 | class InternalPanel extends Component { 5 | render() { 6 | return ( 7 |
8 | 9 |
InternalPanel.js
10 |
11 | ); 12 | } 13 | } 14 | 15 | export default InternalPanel; 16 | -------------------------------------------------------------------------------- /4 - Context pt1 (Deprecated)/src/components/Panel.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import InternalPanel from './InternalPanel'; 3 | 4 | class Panel extends Component { 5 | render() { 6 | return ( 7 |
8 | 9 |
Panel.js
10 |
11 | ); 12 | } 13 | } 14 | 15 | export default Panel; 16 | -------------------------------------------------------------------------------- /4 - Context pt1 (Deprecated)/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /4 - Context pt1 (Deprecated)/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import './index.css'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /4 - Context pt1 (Deprecated)/src/locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "header": "Heading Text", 3 | "text": "This content is inside the third panel, and so it is also inside the internal panel and the parent panel.", 4 | "buttonLabel": "I'm a button" 5 | } 6 | -------------------------------------------------------------------------------- /4 - Context pt1 (Deprecated)/src/locales/pt.json: -------------------------------------------------------------------------------- 1 | { 2 | "header": "Texto de Cabeçalho", 3 | "text": "O Araketu, o Araketu quando toca, deixa todo mundo pulando que nem pipoca. O fogo é fogo! Esquenta...", 4 | "buttonLabel": "Sou um botão" 5 | } 6 | -------------------------------------------------------------------------------- /5 - Context pt2 (Deprecated)/README.md: -------------------------------------------------------------------------------- 1 | # Contexts (Part 2) 2 | 3 | ReactCasts, episode 5. 4 | 5 | On the last episode I talked about how context is a cool feature but also how it has 2 problems - the fact that the API is experimental (it will end up changing) and the fact that updates might not propagate if any component in the middle of the hierarchy implements ShouldComponentUpdate. On this episode, we will tackle this problems and show how to use Context in a safe way. 6 | 7 | Screencast video: 8 | https://www.youtube.com/watch?v=mwYHDXS6uSc 9 | 10 | # Outline 11 | 12 | - Review the problems: Experimental API that might change and change propagation 13 | - Problem 1: 14 | - Extract the context stuff from `ContentPanel` to `WithLocaleHOC` 15 | - Problem 2: 16 | - Example: Implement ShouldComponentUpdate on InternalPanel 17 | - Introduce the subscription mechanism 18 | 19 | 20 | ## Links: 21 | ShouldComponentUpdate Documentation: https://facebook.github.io/react/docs/react-component.html#shouldcomponentupdate 22 | 23 | How to safely use React context: https://medium.com/@mweststrate/how-to-safely-use-react-context-b7e343eff076#.wrea2wbqq 24 | 25 | # Build & Run Instructions 26 | 27 | 1. To build and run the code in this directory, ensure you have [npm](https://www.npmjs.com) installed 28 | 29 | 2. Install 30 | ``` 31 | npm install 32 | ``` 33 | 34 | 3. Start the application 35 | ``` 36 | npm start 37 | ``` 38 | -------------------------------------------------------------------------------- /5 - Context pt2 (Deprecated)/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "i18n", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.7.0" 7 | }, 8 | "dependencies": { 9 | "react": "^15.3.2", 10 | "react-dom": "^15.3.2" 11 | }, 12 | "scripts": { 13 | "start": "react-scripts start", 14 | "build": "react-scripts build", 15 | "test": "react-scripts test --env=jsdom", 16 | "eject": "react-scripts eject" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /5 - Context pt2 (Deprecated)/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/5 - Context pt2 (Deprecated)/public/favicon.ico -------------------------------------------------------------------------------- /5 - Context pt2 (Deprecated)/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | React App 17 | 18 | 19 |
20 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /5 - Context pt2 (Deprecated)/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import Panel from './components/Panel'; 3 | import Locale from './locales/Locale'; 4 | import './App.css'; 5 | 6 | class App extends Component { 7 | constructor(props, context) { 8 | super(props, context); 9 | this.locale = new Locale('en'); 10 | } 11 | static childContextTypes = { 12 | locale: PropTypes.object 13 | } 14 | 15 | state = { 16 | currentLocale: 'en' 17 | } 18 | 19 | componentWillUpdate(nextProps, nextState) { 20 | this.locale.setLanguage(nextState.currentLocale); 21 | } 22 | 23 | getChildContext() { 24 | return {locale: this.locale} 25 | } 26 | 27 | changeLocale(locale){ 28 | this.setState({currentLocale: locale}) 29 | } 30 | 31 | render() { 32 | return ( 33 | 40 | ); 41 | } 42 | } 43 | 44 | export default App; 45 | -------------------------------------------------------------------------------- /5 - Context pt2 (Deprecated)/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /5 - Context pt2 (Deprecated)/src/components/ContentPanel.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | import WithLocaleHOC from './WithLocaleHOC'; 3 | 4 | class ContentPanel extends Component { 5 | static propTypes = { 6 | locale: PropTypes.object 7 | } 8 | render() { 9 | const { locale } = this.props; 10 | return ( 11 |
12 |

{locale.strings.header}

13 |

14 | {locale.strings.text} 15 |

16 | 17 |
ContentPanel.js
18 |
19 | ); 20 | } 21 | } 22 | 23 | export default WithLocaleHOC(ContentPanel); 24 | -------------------------------------------------------------------------------- /5 - Context pt2 (Deprecated)/src/components/InternalPanel.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import ContentPane from './ContentPanel'; 3 | 4 | class InternalPanel extends Component { 5 | shouldComponentUpdate() { 6 | return false; 7 | } 8 | render() { 9 | return ( 10 |
11 | 12 |
InternalPanel.js
13 |
14 | ); 15 | } 16 | } 17 | 18 | export default InternalPanel; 19 | -------------------------------------------------------------------------------- /5 - Context pt2 (Deprecated)/src/components/Panel.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import InternalPanel from './InternalPanel'; 3 | import WithLocaleHOC from './WithLocaleHOC'; 4 | 5 | class Panel extends Component { 6 | render() { 7 | return ( 8 |
9 | 10 |
{this.props.locale.strings.footer} Panel.js
11 |
12 | ); 13 | } 14 | } 15 | 16 | export default WithLocaleHOC(Panel); 17 | -------------------------------------------------------------------------------- /5 - Context pt2 (Deprecated)/src/components/WithLocaleHOC.js: -------------------------------------------------------------------------------- 1 | import React, { Component, PropTypes } from 'react'; 2 | 3 | const WithLocaleHOC = (WrappedComponent) => { 4 | return class WithLocaleHOC extends Component { 5 | static contextTypes = { 6 | locale: PropTypes.object 7 | } 8 | 9 | componentDidMount() { 10 | this.context.locale.subscribe(() => this.forceUpdate()); 11 | } 12 | 13 | render() { 14 | const { locale } = this.context; 15 | return 16 | } 17 | } 18 | } 19 | 20 | export default WithLocaleHOC; 21 | -------------------------------------------------------------------------------- /5 - Context pt2 (Deprecated)/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /5 - Context pt2 (Deprecated)/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import './index.css'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /5 - Context pt2 (Deprecated)/src/locales/Locale.js: -------------------------------------------------------------------------------- 1 | import en from './en.json'; 2 | import pt from './pt.json'; 3 | 4 | const locales = {en, pt}; 5 | 6 | class Locale { 7 | constructor(language) { 8 | this.strings = locales[language]; 9 | this.subscriptions = []; 10 | } 11 | 12 | setLanguage(language) { 13 | this.strings = locales[language]; 14 | this.subscriptions.forEach(cb => cb()); 15 | } 16 | 17 | subscribe(callback) { 18 | this.subscriptions.push(callback); 19 | } 20 | } 21 | 22 | export default Locale; 23 | -------------------------------------------------------------------------------- /5 - Context pt2 (Deprecated)/src/locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "header": "Heading Text", 3 | "text": "This content is inside the third panel, and so it is also inside the internal panel and the parent panel.", 4 | "buttonLabel": "I'm a button", 5 | "footer": "Chubby Unicorns © 2016" 6 | } 7 | -------------------------------------------------------------------------------- /5 - Context pt2 (Deprecated)/src/locales/pt.json: -------------------------------------------------------------------------------- 1 | { 2 | "header": "Texto de Cabeçalho", 3 | "text": "O Araketu, o Araketu quando toca, deixa todo mundo pulando que nem pipoca. O fogo é fogo! Esquenta...", 4 | "buttonLabel": "Sou um botão", 5 | "footer": "Unicórnios Gorduchos © 2016" 6 | } 7 | -------------------------------------------------------------------------------- /6 - Redux Middleware/README.md: -------------------------------------------------------------------------------- 1 | # Redux Middleware 2 | 3 | ReactCasts, episode 6. 4 | 5 | Redux middleware provides an extension point between dispatching an action, and the moment it reaches the reducer. It's a very useful and elegant way of solving data-related issues in your Redux apps in a reusable way. 6 | 7 | Screencast video: 8 | https://youtu.be/T-qtHI1qHIg 9 | 10 | # Outline 11 | 12 | - How Middleware fits in Redux. 13 | - What is middleware for? 14 | - Example 1: Logger Middleware 15 | - Example 2: Confirmation Middleware 16 | - Example 3: Promise Middleware 17 | 18 | 19 | # Build & Run Instructions 20 | 21 | 1. To build and run the code in this directory, ensure you have [npm](https://www.npmjs.com) installed 22 | 23 | 2. Install 24 | ``` 25 | npm install 26 | ``` 27 | 28 | 3. Start the application 29 | ``` 30 | npm start 31 | ``` 32 | -------------------------------------------------------------------------------- /6 - Redux Middleware/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todos", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "enzyme": "^2.4.1", 7 | "react-addons-test-utils": "^15.3.0", 8 | "react-scripts": "^0.6.0" 9 | }, 10 | "dependencies": { 11 | "react": "^15.3.0", 12 | "react-dom": "^15.3.0", 13 | "react-redux": "^4.4.5", 14 | "redux": "^3.5.2" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "eject": "react-scripts eject", 20 | "test": "react-scripts test" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /6 - Redux Middleware/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Redux Todos Example 8 | 9 | 10 |
11 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /6 - Redux Middleware/src/actions/index.js: -------------------------------------------------------------------------------- 1 | import { ADD_TODO, REMOVE_TODO, TOGGLE_TODO, SET_VISIBILITY_FILTER } from '../constants'; 2 | 3 | let nextTodoId = 0 4 | export const addTodo = (title) => ({ 5 | type: ADD_TODO, 6 | id: String(nextTodoId++), 7 | title 8 | }) 9 | 10 | export const removeTodo = (id) => ({ 11 | type: REMOVE_TODO, 12 | shouldConfirm: true, 13 | id 14 | }) 15 | 16 | export const toggleTodo = (id) => ({ 17 | type: TOGGLE_TODO, 18 | id 19 | }) 20 | 21 | export const setVisibilityFilter = (filter) => ({ 22 | type: SET_VISIBILITY_FILTER, 23 | filter 24 | }) 25 | -------------------------------------------------------------------------------- /6 - Redux Middleware/src/components/Footer.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import FilterLink from '../containers/FilterLink' 3 | 4 | const Footer = () => ( 5 |

6 | Show: 7 | {" "} 8 | 9 | All 10 | 11 | {", "} 12 | 13 | Active 14 | 15 | {", "} 16 | 17 | Completed 18 | 19 |

20 | ) 21 | 22 | export default Footer 23 | -------------------------------------------------------------------------------- /6 - Redux Middleware/src/components/Link.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | 3 | const Link = ({ active, children, onClick }) => { 4 | if (active) { 5 | return {children} 6 | } 7 | 8 | return ( 9 | { 11 | e.preventDefault() 12 | onClick() 13 | }} 14 | > 15 | {children} 16 | 17 | ) 18 | } 19 | 20 | Link.propTypes = { 21 | active: PropTypes.bool.isRequired, 22 | children: PropTypes.node.isRequired, 23 | onClick: PropTypes.func.isRequired 24 | } 25 | 26 | export default Link 27 | -------------------------------------------------------------------------------- /6 - Redux Middleware/src/components/Todo.css: -------------------------------------------------------------------------------- 1 | input[type=checkbox] { 2 | visibility: hidden; 3 | } 4 | 5 | .checkBox { 6 | display: inline-block; 7 | margin-right: 5px; 8 | 9 | width: 22px; 10 | height: 22px; 11 | border-radius: 50px; 12 | background: #ccc; 13 | box-shadow: 0px 1px 1px rgba(0,0,0,0.5); 14 | position: relative; 15 | } 16 | 17 | .checkBox label { 18 | cursor: pointer; 19 | position: absolute; 20 | width: 18px; 21 | height: 18px; 22 | border-radius: 50px; 23 | left: 2px; 24 | top: 2px; 25 | background: linear-gradient(to bottom, #fcfff4 0%, #dfe5d7 100%); 26 | } 27 | 28 | .checkBox label:after { 29 | opacity: 0; 30 | content: ''; 31 | position: absolute; 32 | width: 8px; 33 | height: 4px; 34 | background: transparent; 35 | top: 5px; 36 | left: 4px; 37 | border: 3px solid #222; 38 | border-top: none; 39 | border-right: none; 40 | transform: rotate(-45deg); 41 | } 42 | 43 | .checkBox label:hover::after { 44 | opacity: 0.3; 45 | } 46 | 47 | .checkBox input[type=checkbox]:checked + label:after { 48 | opacity: 1; 49 | } 50 | 51 | .remove { 52 | color: #993333; 53 | margin-left: 5px; 54 | } 55 | -------------------------------------------------------------------------------- /6 - Redux Middleware/src/components/Todo.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import './Todo.css'; 3 | 4 | const Todo = ({ onToggle, onRemove, completed, title }) => { 5 | const key = title.replace(/[^\w\s]/gi, '') 6 | return ( 7 |
  • 8 |
    9 | 10 | 11 |
    12 | {title} 15 |
  • 16 | ); 17 | } 18 | 19 | Todo.propTypes = { 20 | onToggle: PropTypes.func.isRequired, 21 | onRemove: PropTypes.func.isRequired, 22 | completed: PropTypes.bool.isRequired, 23 | title: PropTypes.string.isRequired 24 | } 25 | 26 | export default Todo 27 | -------------------------------------------------------------------------------- /6 - Redux Middleware/src/components/TodoList.js: -------------------------------------------------------------------------------- 1 | import React, { PropTypes } from 'react' 2 | import Todo from './Todo' 3 | 4 | const TodoList = ({ todos, onTodoToggle, onTodoRemove }) => ( 5 |
      6 | {todos.map(todo => 7 | onTodoToggle(todo.id)} 11 | onRemove={() => onTodoRemove(todo.id)} 12 | /> 13 | )} 14 |
    15 | ) 16 | 17 | TodoList.propTypes = { 18 | todos: PropTypes.arrayOf(PropTypes.shape({ 19 | id: PropTypes.string.isRequired, 20 | completed: PropTypes.bool.isRequired, 21 | title: PropTypes.string.isRequired 22 | }).isRequired).isRequired, 23 | onTodoToggle: PropTypes.func.isRequired, 24 | onTodoRemove: PropTypes.func.isRequired, 25 | } 26 | 27 | export default TodoList 28 | -------------------------------------------------------------------------------- /6 - Redux Middleware/src/constants.js: -------------------------------------------------------------------------------- 1 | export const FETCH_TODO = "FETCH TODO"; 2 | export const ADD_TODO = "ADD TODO"; 3 | export const REMOVE_TODO = "REMOVE TODO"; 4 | export const TOGGLE_TODO = "TOGGLE TODO"; 5 | export const SET_VISIBILITY_FILTER = "SET VISIBILITY FILTER"; 6 | -------------------------------------------------------------------------------- /6 - Redux Middleware/src/containers/AddTodo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { connect } from 'react-redux' 3 | import { addTodo } from '../actions' 4 | 5 | let AddTodo = ({ dispatch }) => { 6 | let input 7 | 8 | return ( 9 |
    10 |
    { 11 | e.preventDefault() 12 | if (!input.value.trim()) { 13 | return 14 | } 15 | dispatch(addTodo(input.value)) 16 | input.value = '' 17 | }}> 18 | { 19 | input = node 20 | }} /> 21 | 24 |
    25 |
    26 | ) 27 | } 28 | AddTodo = connect()(AddTodo) 29 | 30 | export default AddTodo 31 | -------------------------------------------------------------------------------- /6 - Redux Middleware/src/containers/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import { connect } from 'react-redux' 3 | import Footer from '../components/Footer' 4 | import AddTodo from './AddTodo' 5 | import VisibleTodoList from './VisibleTodoList' 6 | import { FETCH_TODO } from '../constants'; 7 | 8 | class App extends Component { 9 | componentDidMount() { 10 | this.props.dispatch({ 11 | type: FETCH_TODO, 12 | promise: fetch('https://todo-hapi-postgres.herokuapp.com/') 13 | }) 14 | } 15 | render() { 16 | return ( 17 |
    18 |
    19 | 20 | 21 |
    22 |
    23 |
    24 |
    25 |
    26 | ); 27 | } 28 | } 29 | 30 | export default connect()(App); 31 | -------------------------------------------------------------------------------- /6 - Redux Middleware/src/containers/FilterLink.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { setVisibilityFilter } from '../actions' 3 | import Link from '../components/Link' 4 | 5 | const mapStateToProps = (state, ownProps) => ({ 6 | active: ownProps.filter === state.visibilityFilter 7 | }) 8 | 9 | const mapDispatchToProps = (dispatch, ownProps) => ({ 10 | onClick: () => { 11 | dispatch(setVisibilityFilter(ownProps.filter)) 12 | } 13 | }) 14 | 15 | const FilterLink = connect( 16 | mapStateToProps, 17 | mapDispatchToProps 18 | )(Link) 19 | 20 | export default FilterLink 21 | -------------------------------------------------------------------------------- /6 - Redux Middleware/src/containers/VisibleTodoList.js: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux' 2 | import { toggleTodo, removeTodo } from '../actions' 3 | import TodoList from '../components/TodoList' 4 | 5 | const getVisibleTodos = (todos, filter) => { 6 | switch (filter) { 7 | case 'SHOW_ALL': 8 | return todos 9 | case 'SHOW_COMPLETED': 10 | return todos.filter(t => t.completed) 11 | case 'SHOW_ACTIVE': 12 | return todos.filter(t => !t.completed) 13 | default: 14 | throw new Error('Unknown filter: ' + filter) 15 | } 16 | } 17 | 18 | const mapStateToProps = (state) => ({ 19 | todos: getVisibleTodos(state.todos, state.visibilityFilter) 20 | }) 21 | 22 | const mapDispatchToProps = ({ 23 | onTodoToggle: toggleTodo, 24 | onTodoRemove: removeTodo 25 | }) 26 | 27 | const VisibleTodoList = connect( 28 | mapStateToProps, 29 | mapDispatchToProps 30 | )(TodoList) 31 | 32 | export default VisibleTodoList 33 | -------------------------------------------------------------------------------- /6 - Redux Middleware/src/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box 3 | } 4 | 5 | html { 6 | margin: 0; 7 | } 8 | 9 | body { 10 | font-family: 'Lato', sans-serif; 11 | margin: 0; 12 | } 13 | 14 | ul { 15 | padding: 0; 16 | } 17 | 18 | li { 19 | list-style-type: none; 20 | font-size: 20px; 21 | margin: 15px 0; 22 | } 23 | 24 | a { 25 | text-decoration: none; 26 | cursor: pointer; 27 | color: #3333AA; 28 | } 29 | 30 | .content { 31 | min-height: calc(100vh - 60px); 32 | padding: 10px; 33 | } 34 | 35 | .footer { 36 | height: 60px; 37 | padding-top: 1px; 38 | padding-left: 4px; 39 | background-color: #ddd; 40 | } 41 | 42 | .input { 43 | font-size: 16px; 44 | padding: 3.5px; 45 | border: solid 1px #bfbfbf; 46 | width: 270px; 47 | outline: none; 48 | } 49 | 50 | .submit { 51 | margin-left: -1px; 52 | vertical-align: top; 53 | padding: 6px; 54 | background: linear-gradient(to bottom, #e8e8e8 0%, #d0d0d0 100%); 55 | border-image: none; 56 | border-top: solid 1px #bfbfbf; 57 | border-right: solid 1px #bfbfbf; 58 | border-left: none; 59 | border-bottom: solid 1px #bfbfbf; 60 | } 61 | -------------------------------------------------------------------------------- /6 - Redux Middleware/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'react-dom' 3 | import { createStore, applyMiddleware } from 'redux' 4 | import { Provider } from 'react-redux' 5 | import App from './containers/App' 6 | import reducer from './reducers' 7 | import './index.css'; 8 | 9 | const loggerMiddleware = store => next => action => { 10 | console.log('dispatching: ', action); 11 | next(action); 12 | } 13 | 14 | const confirmationMiddleware = store => next => action => { 15 | if(action.shouldConfirm){ 16 | if(confirm('Are you sure?')){ 17 | next(action); 18 | } 19 | } else { 20 | next(action); 21 | } 22 | } 23 | 24 | const promiseMiddleware = store => next => action => { 25 | if(action.promise){ 26 | action.promise 27 | .then(rawResponse => rawResponse.json()) 28 | .then(response => store.dispatch({type: action.type, payload: response})); 29 | } else { 30 | next(action); 31 | } 32 | } 33 | 34 | const store = createStore( 35 | reducer, 36 | applyMiddleware(loggerMiddleware, confirmationMiddleware, promiseMiddleware) 37 | ); 38 | 39 | render( 40 | 41 | 42 | , 43 | document.getElementById('root') 44 | ) 45 | -------------------------------------------------------------------------------- /6 - Redux Middleware/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux' 2 | import todos from './todos' 3 | import visibilityFilter from './visibilityFilter' 4 | 5 | const todoApp = combineReducers({ 6 | todos, 7 | visibilityFilter 8 | }) 9 | 10 | export default todoApp 11 | -------------------------------------------------------------------------------- /6 - Redux Middleware/src/reducers/todos.js: -------------------------------------------------------------------------------- 1 | import { FETCH_TODO, ADD_TODO, REMOVE_TODO, TOGGLE_TODO } from '../constants'; 2 | 3 | const todo = (state, action) => { 4 | switch (action.type) { 5 | case ADD_TODO: 6 | return { 7 | id: action.id, 8 | title: action.title, 9 | completed: false 10 | } 11 | case TOGGLE_TODO: 12 | if (state.id !== action.id) { 13 | return state 14 | } 15 | 16 | return { 17 | ...state, 18 | completed: !state.completed 19 | } 20 | 21 | case REMOVE_TODO: 22 | return state.id !== action.id; 23 | 24 | default: 25 | return state 26 | } 27 | } 28 | 29 | const todos = (state = [], action) => { 30 | switch (action.type) { 31 | case FETCH_TODO: 32 | return action.payload; 33 | case ADD_TODO: 34 | return [ 35 | ...state, 36 | todo(undefined, action) 37 | ] 38 | case TOGGLE_TODO: 39 | return state.map(t => 40 | todo(t, action) 41 | ) 42 | case REMOVE_TODO: 43 | return state.filter(t => 44 | todo(t, action) 45 | ) 46 | default: 47 | return state 48 | } 49 | } 50 | 51 | export default todos 52 | -------------------------------------------------------------------------------- /6 - Redux Middleware/src/reducers/visibilityFilter.js: -------------------------------------------------------------------------------- 1 | import { SET_VISIBILITY_FILTER } from '../constants'; 2 | 3 | const visibilityFilter = (state = 'SHOW_ALL', action) => { 4 | switch (action.type) { 5 | case SET_VISIBILITY_FILTER: 6 | return action.filter 7 | default: 8 | return state 9 | } 10 | } 11 | 12 | export default visibilityFilter 13 | -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/README.md: -------------------------------------------------------------------------------- 1 | # Hot Module Replacement in Create-React-App 2 | 3 | ReactCasts, episode 7. 4 | 5 | Hot Module Replacement (or HMR) is a Webpack feature that allows "on the fly" module updates for your app. 6 | Create-React-App uses webpack internally, but HMR working out-of-the-box only for for stylesheets (and not for React components). This episode discusses the resoning behind this and shows two ways you can add Hot Module Replacement to projects created with Create-React-App. 7 | 8 | Screencast video: 9 | https://youtu.be/f-ctxG2qEps 10 | 11 | # Outline 12 | 13 | - What is Hot Mdule Replacement (HMR). 14 | - Create-React-App and HMR 15 | - Example 1: Simple HMR without Eject 16 | - Example 2: Complete HMR (requires Eject) 17 | 18 | 19 | # Build & Run Instructions 20 | 21 | 1. To build and run the code in this directory, ensure you have [npm](https://www.npmjs.com) installed 22 | 23 | 2. Install 24 | ``` 25 | npm install 26 | ``` 27 | 28 | 3. Start the application 29 | ``` 30 | npm start 31 | ``` 32 | -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/hmr-quick/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log 16 | -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/hmr-quick/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hmr-quick", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.7.0" 7 | }, 8 | "dependencies": { 9 | "react": "^15.4.1", 10 | "react-dom": "^15.4.1" 11 | }, 12 | "scripts": { 13 | "start": "react-scripts start", 14 | "build": "react-scripts build", 15 | "test": "react-scripts test --env=jsdom", 16 | "eject": "react-scripts eject" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/hmr-quick/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/7 - HMR (Deprecated)/hmr-quick/public/favicon.ico -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/hmr-quick/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | React App 17 | 18 | 19 |
    20 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/hmr-quick/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-intro { 18 | font-size: large; 19 | } 20 | 21 | @keyframes App-logo-spin { 22 | from { transform: rotate(0deg); } 23 | to { transform: rotate(360deg); } 24 | } 25 | -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/hmr-quick/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import logo from './logo.svg'; 3 | import './App.css'; 4 | 5 | class App extends Component { 6 | state = { 7 | counter: 0 8 | } 9 | 10 | render() { 11 | return ( 12 |
    13 |
    14 | logo 15 |

    Cool Counter: {this.state.counter}

    16 |
    17 |

    18 | 23 |

    24 |
    25 | ); 26 | } 27 | } 28 | 29 | export default App; 30 | -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/hmr-quick/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/hmr-quick/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/hmr-quick/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import './index.css'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | 11 | if (module.hot) { 12 | module.hot.accept('./App', () => { 13 | const NextApp = require('./App').default; 14 | ReactDOM.render( 15 | , 16 | document.getElementById('root') 17 | ) 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/hrm-with-react-hot-loader/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # testing 7 | coverage 8 | 9 | # production 10 | build 11 | 12 | # misc 13 | .DS_Store 14 | .env 15 | npm-debug.log 16 | -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/hrm-with-react-hot-loader/config/env.js: -------------------------------------------------------------------------------- 1 | // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be 2 | // injected into the application via DefinePlugin in Webpack configuration. 3 | 4 | var REACT_APP = /^REACT_APP_/i; 5 | 6 | function getClientEnvironment(publicUrl) { 7 | var processEnv = Object 8 | .keys(process.env) 9 | .filter(key => REACT_APP.test(key)) 10 | .reduce((env, key) => { 11 | env[key] = JSON.stringify(process.env[key]); 12 | return env; 13 | }, { 14 | // Useful for determining whether we’re running in production mode. 15 | // Most importantly, it switches React into the correct mode. 16 | 'NODE_ENV': JSON.stringify( 17 | process.env.NODE_ENV || 'development' 18 | ), 19 | // Useful for resolving the correct path to static assets in `public`. 20 | // For example, . 21 | // This should only be used as an escape hatch. Normally you would put 22 | // images into the `src` and `import` them in code to get their paths. 23 | 'PUBLIC_URL': JSON.stringify(publicUrl) 24 | }); 25 | return {'process.env': processEnv}; 26 | } 27 | 28 | module.exports = getClientEnvironment; 29 | -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/hrm-with-react-hot-loader/config/jest/CSSStub.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/hrm-with-react-hot-loader/config/jest/FileStub.js: -------------------------------------------------------------------------------- 1 | module.exports = "test-file-stub"; 2 | -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/hrm-with-react-hot-loader/config/polyfills.js: -------------------------------------------------------------------------------- 1 | if (typeof Promise === 'undefined') { 2 | // Rejection tracking prevents a common issue where React gets into an 3 | // inconsistent state due to an error, but it gets swallowed by a Promise, 4 | // and the user has no idea what causes React's erratic future behavior. 5 | require('promise/lib/rejection-tracking').enable(); 6 | window.Promise = require('promise/lib/es6-extensions.js'); 7 | } 8 | 9 | // fetch() polyfill for making API calls. 10 | require('whatwg-fetch'); 11 | 12 | // Object.assign() is commonly used with React. 13 | // It will use the native implementation if it's present and isn't buggy. 14 | Object.assign = require('object-assign'); 15 | -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/hrm-with-react-hot-loader/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/7 - HMR (Deprecated)/hrm-with-react-hot-loader/public/favicon.ico -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/hrm-with-react-hot-loader/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | React App 17 | 18 | 19 |
    20 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/hrm-with-react-hot-loader/scripts/test.js: -------------------------------------------------------------------------------- 1 | process.env.NODE_ENV = 'test'; 2 | process.env.PUBLIC_URL = ''; 3 | 4 | // Load environment variables from .env file. Suppress warnings using silent 5 | // if this file is missing. dotenv will never modify any environment variables 6 | // that have already been set. 7 | // https://github.com/motdotla/dotenv 8 | require('dotenv').config({silent: true}); 9 | 10 | const jest = require('jest'); 11 | const argv = process.argv.slice(2); 12 | 13 | // Watch unless on CI 14 | if (!process.env.CI) { 15 | argv.push('--watch'); 16 | } 17 | 18 | 19 | jest.run(argv); 20 | -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/hrm-with-react-hot-loader/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | animation: App-logo-spin infinite 20s linear; 7 | height: 80px; 8 | } 9 | 10 | .App-header { 11 | background-color: #222; 12 | height: 150px; 13 | padding: 20px; 14 | color: white; 15 | } 16 | 17 | .App-intro { 18 | font-size: large; 19 | } 20 | 21 | @keyframes App-logo-spin { 22 | from { transform: rotate(0deg); } 23 | to { transform: rotate(360deg); } 24 | } 25 | -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/hrm-with-react-hot-loader/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import logo from './logo.svg'; 3 | import './App.css'; 4 | 5 | class App extends Component { 6 | state = { 7 | counter: 0 8 | } 9 | render() { 10 | return ( 11 |
    12 |
    13 | logo 14 |

    Cool Counter: {this.state.counter} !!

    15 |
    16 |

    17 | 22 |

    23 |
    24 | ); 25 | } 26 | } 27 | 28 | export default App; 29 | -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/hrm-with-react-hot-loader/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/hrm-with-react-hot-loader/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /7 - HMR (Deprecated)/hrm-with-react-hot-loader/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import './index.css'; 5 | 6 | ReactDOM.render( 7 | , 8 | document.getElementById('root') 9 | ); 10 | -------------------------------------------------------------------------------- /8 - Selectors in Redux/README.md: -------------------------------------------------------------------------------- 1 | # Selectors in Redux 2 | 3 | ReactCasts, episode 8. 4 | 5 | Selectors are neither something you import from Redux nor an external library you must install. But they are present in many Redux projects nonetheless. 6 | Technically selectors are just convenient functions used for looking up and retrieving snippets of data from the Redux store, into your components... But they play an important role as they help cope with derived and related data - allowing Redux to store the minimal possible state. 7 | 8 | 9 | Screencast video: 10 | https://youtu.be/frT3to2ACCw 11 | 12 | # Outline 13 | 14 | - Brief introduction to Selectors. 15 | - Sample application highlight 16 | - Example 1: Using Selectors for computing Derived Data 17 | - Example 2: Making selectors more practical by centralizing them all on the index reducer file. 18 | - Example 3: Using Selectors for retrieving relational data 19 | - Memoization and Reselect 20 | 21 | 22 | # Build & Run Instructions 23 | 24 | 1. To build and run the code in this directory, ensure you have [npm](https://www.npmjs.com) installed 25 | 26 | 2. Install 27 | ``` 28 | npm install 29 | ``` 30 | 31 | 3. Start the application 32 | ``` 33 | npm start 34 | ``` 35 | -------------------------------------------------------------------------------- /8 - Selectors in Redux/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hotel", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "react-scripts": "0.8.3" 7 | }, 8 | "dependencies": { 9 | "react": "^15.4.1", 10 | "react-dom": "^15.4.1", 11 | "react-redux": "^4.4.6", 12 | "redux": "^3.6.0", 13 | "redux-thunk": "^2.1.0", 14 | "reselect": "^2.5.4" 15 | }, 16 | "scripts": { 17 | "start": "react-scripts start", 18 | "build": "react-scripts build", 19 | "test": "react-scripts test --env=jsdom", 20 | "eject": "react-scripts eject" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /8 - Selectors in Redux/public/fakeserver_images/deluxe-city.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/8 - Selectors in Redux/public/fakeserver_images/deluxe-city.jpg -------------------------------------------------------------------------------- /8 - Selectors in Redux/public/fakeserver_images/deluxe-ocean.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/8 - Selectors in Redux/public/fakeserver_images/deluxe-ocean.jpg -------------------------------------------------------------------------------- /8 - Selectors in Redux/public/fakeserver_images/grandlux.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/8 - Selectors in Redux/public/fakeserver_images/grandlux.jpg -------------------------------------------------------------------------------- /8 - Selectors in Redux/public/fakeserver_images/royal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/8 - Selectors in Redux/public/fakeserver_images/royal.jpg -------------------------------------------------------------------------------- /8 - Selectors in Redux/public/fakeserver_images/standard-city.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/8 - Selectors in Redux/public/fakeserver_images/standard-city.jpg -------------------------------------------------------------------------------- /8 - Selectors in Redux/public/fakeserver_images/standard-ocean.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/8 - Selectors in Redux/public/fakeserver_images/standard-ocean.jpg -------------------------------------------------------------------------------- /8 - Selectors in Redux/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/8 - Selectors in Redux/public/favicon.ico -------------------------------------------------------------------------------- /8 - Selectors in Redux/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 17 | React App 18 | 19 | 20 |
    21 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /8 - Selectors in Redux/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | display: flex; 3 | height: 100%; 4 | } 5 | 6 | .main { 7 | height: 100%; 8 | width: 50%; 9 | padding: 20px 120px 20px 20px; 10 | text-align: right; 11 | } 12 | -------------------------------------------------------------------------------- /8 - Selectors in Redux/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import SlideShow from './components/SlideShow'; 4 | import { login } from './actions/auth'; 5 | import { fetchRooms } from './actions/rooms'; 6 | import { selectUserName, selectUserRoom } from './reducers'; 7 | import logo from './logo.png'; 8 | import './App.css'; 9 | 10 | class App extends Component { 11 | componentDidMount() { 12 | const { login, fetchRooms } = this.props; 13 | login(); 14 | fetchRooms(); 15 | } 16 | 17 | render() { 18 | const { isFetching, userName, accomodation } = this.props; 19 | if (isFetching || isFetching === undefined) return
    ; 20 | return ( 21 |
    22 |
    23 | Redux Hotel 24 |

    Your Reservation

    25 |

    Name: {userName}

    26 |

    Accomodation

    27 |

    {accomodation.name}

    28 |

    accomodation

    29 |
    30 | 31 |
    32 | ); 33 | } 34 | } 35 | 36 | const mapStateToProps = (state) => { 37 | const { auth, rooms } = state; 38 | const isFetching = auth.isFetching || rooms.isFetching; 39 | 40 | 41 | return { 42 | isFetching, 43 | userName: selectUserName(state), 44 | accomodation: selectUserRoom(state) 45 | }; 46 | }; 47 | 48 | const mapDispatchToProps = { login, fetchRooms }; 49 | 50 | export default connect(mapStateToProps, mapDispatchToProps)(App); 51 | -------------------------------------------------------------------------------- /8 - Selectors in Redux/src/actions/auth.js: -------------------------------------------------------------------------------- 1 | import api from '../api/fakeApi'; 2 | import { AUTH_REQUEST, AUTH_SUCCESS, AUTH_FAILURE } from '../constants'; 3 | 4 | function authenticating() { 5 | return { 6 | type: AUTH_REQUEST, 7 | }; 8 | } 9 | function authenticate(userData) { 10 | return { 11 | type: AUTH_SUCCESS, 12 | payload: userData, 13 | }; 14 | } 15 | function authError(error) { 16 | return { 17 | type: AUTH_FAILURE, 18 | error: 'Failed to authenticate', 19 | payload: error, 20 | }; 21 | } 22 | 23 | export function login() { 24 | return (dispatch) => { 25 | dispatch(authenticating()); 26 | return api.login() 27 | .then((user) => dispatch(authenticate(user))); 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /8 - Selectors in Redux/src/actions/rooms.js: -------------------------------------------------------------------------------- 1 | import api from '../api/fakeApi'; 2 | import { ROOMS_REQUEST, ROOMS_SUCCESS, ROOMS_FAILURE } from '../constants'; 3 | 4 | function fethingRooms() { 5 | return { 6 | type: ROOMS_REQUEST, 7 | }; 8 | } 9 | function roomsSuccess(roomsList) { 10 | return { 11 | type: ROOMS_SUCCESS, 12 | payload: roomsList, 13 | }; 14 | } 15 | function roomsError(error) { 16 | return { 17 | type: ROOMS_FAILURE, 18 | error: 'Failed to fetch rooms', 19 | payload: error, 20 | }; 21 | } 22 | 23 | export function fetchRooms() { 24 | return (dispatch) => { 25 | dispatch(fethingRooms()); 26 | return api.fetchRooms() 27 | .then((rooms) => dispatch(roomsSuccess(rooms))); 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /8 - Selectors in Redux/src/components/SlideShow.css: -------------------------------------------------------------------------------- 1 | .slideshow { 2 | height: 100%; 3 | width: 50%; 4 | background-size: cover; 5 | background-position: top left; 6 | } 7 | -------------------------------------------------------------------------------- /8 - Selectors in Redux/src/components/SlideShow.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import background from './background.jpg'; 3 | import './SlideShow.css'; 4 | 5 | export default function SlideShow(){ 6 | return ( 7 |
    8 |
    9 | ); 10 | }; 11 | -------------------------------------------------------------------------------- /8 - Selectors in Redux/src/components/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/8 - Selectors in Redux/src/components/background.jpg -------------------------------------------------------------------------------- /8 - Selectors in Redux/src/constants.js: -------------------------------------------------------------------------------- 1 | export const AUTH_REQUEST = 'AUTH_REQUEST'; 2 | export const AUTH_SUCCESS = 'AUTH_SUCCESS'; 3 | export const AUTH_FAILURE = 'AUTH_FAILURE'; 4 | 5 | export const ROOMS_REQUEST = 'ROOMS_REQUEST'; 6 | export const ROOMS_SUCCESS = 'ROOMS_SUCCESS'; 7 | export const ROOMS_FAILURE = 'ROOMS_FAILURE'; 8 | -------------------------------------------------------------------------------- /8 - Selectors in Redux/src/index.css: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | text-size-adjust: 100%; 4 | height: 100%; 5 | } 6 | 7 | *, *:before, *:after { 8 | box-sizing: inherit; 9 | } 10 | 11 | #root { 12 | height: 100%; 13 | } 14 | 15 | body { 16 | margin: 0; 17 | font-family: Georgia, "Times new Roman", serif; 18 | height: 100%; 19 | background-color: #222126; 20 | color: #ccc; 21 | } 22 | 23 | h1, h2, h3, h4, h5, h6 { 24 | font-weight: 300; 25 | } 26 | 27 | h1 { 28 | font-size: 1.6em; 29 | } 30 | 31 | h2 { 32 | font-size: 1.5em; 33 | } 34 | 35 | h3 { 36 | font-size: 1.4em; 37 | } 38 | 39 | h4 { 40 | font-size: 1.3em; 41 | } 42 | 43 | h5 { 44 | font-size: 1.2em; 45 | } 46 | 47 | h6 { 48 | font-size: 1.1em; 49 | } 50 | -------------------------------------------------------------------------------- /8 - Selectors in Redux/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import { compose, createStore, applyMiddleware } from 'redux' 4 | import { Provider } from 'react-redux'; 5 | import thunkMiddleware from 'redux-thunk'; 6 | import App from './App'; 7 | import reducer from './reducers'; 8 | import './index.css'; 9 | 10 | const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; 11 | 12 | const store = createStore( 13 | reducer, composeEnhancers( 14 | applyMiddleware(thunkMiddleware) 15 | ) 16 | ); 17 | 18 | render( 19 | 20 | 21 | , 22 | document.getElementById('root') 23 | ); 24 | -------------------------------------------------------------------------------- /8 - Selectors in Redux/src/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cassiozen/ReactCasts/947567bfa8af59d9f82fe4b2c03a6fb4d998d74b/8 - Selectors in Redux/src/logo.png -------------------------------------------------------------------------------- /8 - Selectors in Redux/src/reducers/auth.js: -------------------------------------------------------------------------------- 1 | import { AUTH_REQUEST, AUTH_SUCCESS } from '../constants'; 2 | 3 | const auth = (state = { user: {} }, action) => { 4 | switch (action.type) { 5 | case AUTH_REQUEST: 6 | return Object.assign({ isFetching: true } ,state); 7 | case AUTH_SUCCESS: 8 | return { 9 | user: action.payload, 10 | isFetching: false, 11 | }; 12 | default: 13 | return state; 14 | } 15 | }; 16 | 17 | export const selectUserName = (state) => { 18 | 19 | let userTitle; 20 | if (state.user) { 21 | if (state.user.gender === "Male") { 22 | userTitle = "Mr."; 23 | } else if (state.user.maritalStatus === "Married") { 24 | userTitle = "Mrs."; 25 | } else { 26 | userTitle = "Miss"; 27 | } 28 | } 29 | 30 | return `${userTitle} ${state.user.firstName} ${state.user.lastName}`; 31 | } 32 | 33 | export default auth; 34 | -------------------------------------------------------------------------------- /8 - Selectors in Redux/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import { createSelector } from 'reselect'; 3 | import auth, * as fromAuth from './auth'; 4 | import rooms from './rooms'; 5 | 6 | const reducer = combineReducers({ 7 | auth, 8 | rooms 9 | }); 10 | 11 | export const selectUserName = (state) => fromAuth.selectUserName(state.auth); 12 | 13 | export const selectUserReservation = (state) => state.auth.user.reservation; 14 | export const selectRoomList = (state) => state.rooms.list; 15 | export const selectUserRoom = createSelector( 16 | selectUserReservation, selectRoomList, 17 | (userReservation, roomList) => roomList.find(room => room.id === userReservation.roomType) 18 | ); 19 | 20 | export default reducer; 21 | -------------------------------------------------------------------------------- /8 - Selectors in Redux/src/reducers/rooms.js: -------------------------------------------------------------------------------- 1 | import { ROOMS_REQUEST, ROOMS_SUCCESS } from '../constants'; 2 | 3 | const auth = (state = { list:[] }, action) => { 4 | switch (action.type) { 5 | case ROOMS_REQUEST: 6 | return Object.assign({ isFetching: true } ,state); 7 | case ROOMS_SUCCESS: 8 | return { 9 | list: action.payload, 10 | isFetching: false 11 | } 12 | default: 13 | return state; 14 | } 15 | }; 16 | 17 | export default auth; 18 | -------------------------------------------------------------------------------- /9 - Immutability in JS/README.md: -------------------------------------------------------------------------------- 1 | # Immutability in JavaScript 2 | 3 | ReactCasts, episode 9. 4 | 5 | In this episode I'll talk about why immutability is important and how it can benefit you. I will draw some comparisons between JavaScript (which doesn't treat data as immutable by default) and programming languages that have immutability built in. Finally, I will show how to make immutable operations in plain Javascript. 6 | 7 | Screencast video: 8 | https://youtu.be/4LzcQyZ9JOU 9 | 10 | # Outline 11 | 12 | - What is Immutability and why it's important. 13 | - JavaScript non-destructive Array methods 14 | - Spread Operator 15 | - External Libraries 16 | 17 | 18 | # Build & Run Instructions 19 | 20 | 1. To build and run the code in this directory, ensure you have [npm](https://www.npmjs.com) installed 21 | 22 | 2. Install 23 | ``` 24 | npm install 25 | ``` 26 | 27 | 3. Start the application 28 | ``` 29 | npm start 30 | ``` 31 | -------------------------------------------------------------------------------- /9 - Immutability in JS/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactcasts-episode9", 3 | "version": "0.1.0", 4 | "description": "", 5 | "main": "index.js", 6 | "quokka": { 7 | "babel": { 8 | "env": "development" 9 | } 10 | }, 11 | "scripts": { 12 | "start": "cross-env NODE_ENV=development babel-node index.js", 13 | "test": "echo \"Error: no test specified\" && exit 1" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/wallabyjs/quokka-babel-import-sample.git" 18 | }, 19 | "author": "Cassio Zen", 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://github.com/wallabyjs/quokka-babel-import-sample/issues" 23 | }, 24 | "homepage": "https://github.com/wallabyjs/quokka-babel-import-sample#readme", 25 | "devDependencies": { 26 | "cross-env": "^5.1.3", 27 | "babel-cli": "^6.24.0", 28 | "babel-preset-react-app": "^2.2.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016-2021 Cássio Souza 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ReactCasts 2 | 3 | This repository has code and outlines for [ReactCasts](https://www.youtube.com/channel/UCZkjWyyLvzWeoVWEpRemrDQ). 4 | 5 | Full videos at the YouTube Channel: [youtube.com/c/reactcasts](https://www.youtube.com/c/ReactCasts). 6 | 7 | # Request A Screencast 8 | 9 | I have some ideas for topics to cover, but if you want to see some topics covered in a future screencast, I'd like to hear from you. 10 | 11 | Please [submit an issue](https://github.com/cassiozen/ReactCasts/issues) with the `request a screencast` label and make sure to include a good description on what you want to see! 12 | 13 | You can also participate by adding your reactions and contributing to the discussion on suggested topics: https://github.com/cassiozen/ReactCasts/issues/labels/request%20a%20screencast. 14 | 15 | # Issues With Code, Documentation, etc... 16 | 17 | If you see any problems with code, documentation, or anything else in this repository, please [submit an issue](https://github.com/cassiozen/ReactCasts/issues) with the `bug` label and I'll fix it as soon as I can. Pull requests are also welcome. 18 | --------------------------------------------------------------------------------