{"id":22799101,"url":"https://github.com/thomd/on-react","last_synced_at":"2026-05-11T05:48:42.068Z","repository":{"id":66830093,"uuid":"160968850","full_name":"thomd/on-react","owner":"thomd","description":"Notes on React","archived":false,"fork":false,"pushed_at":"2019-02-25T22:22:34.000Z","size":65,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-05T21:42:47.137Z","etag":null,"topics":["javascript","react"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thomd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-12-08T18:48:55.000Z","updated_at":"2019-02-25T22:22:35.000Z","dependencies_parsed_at":"2023-05-10T18:00:46.825Z","dependency_job_id":null,"html_url":"https://github.com/thomd/on-react","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomd%2Fon-react","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomd%2Fon-react/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomd%2Fon-react/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thomd%2Fon-react/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thomd","download_url":"https://codeload.github.com/thomd/on-react/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246365651,"owners_count":20765549,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","react"],"created_at":"2024-12-12T07:07:54.545Z","updated_at":"2026-05-11T05:48:42.027Z","avatar_url":"https://github.com/thomd.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# On React\n\nSome **unstructured notes** and **example code** on React\n\n# Basic Setup with Babel \u0026 Webpack 4\n\n    npm i react react-dom\n    npm i -D @babel/core @babel/preset-env @babel/preset-react\n    npm i -D babel-loader webpack webpack-cli webpack-dev-server\n\nCreate `webpack.config.js`:\n\n    module.exports = {\n      entry: './src/index.js',\n      output: {\n        path: __dirname + '/dist',\n        publicPath: '/',\n        filename: 'bundle.js'\n      },\n      module: {\n        rules: [\n          {\n            test: /\\.js$/,\n            exclude: /node_modules/,\n            use: ['babel-loader']\n          }\n        ]\n      },\n      resolve: {\n        extensions: ['*', '.js']\n      },\n      devServer: {\n        contentBase: './dist'\n      }\n    };\n\nCreate an entry file `src/index.js`:\n\n    import React from 'react'\n    import ReactDOM from 'react-dom'\n    import App from './app'\n\n    ReactDOM.render(\u003cReact.StrictMode\u003e\u003cApp /\u003e\u003c/React.StrictMode\u003e, document.getElementById('app'))\n\nCreate `.babelrc`:\n\n    {\n      \"presets\": [\n        \"@babel/preset-env\",\n        \"@babel/preset-react\"\n      ]\n    }\n\nIn order to support ES6/7/8 features, add further babel plugins, like e.g.\n\n    npm i -D @babel/plugin-proposal-class-properties\n\n```diff\n    {\n      \"presets\": [\n        \"@babel/preset-env\",\n        \"@babel/preset-react\"\n      ],\n+     plugins: [\n+       '@babel/plugin-proposal-class-properties'\n+     ]\n    }\n```\n\nStart Development Server\n\n    webpack-dev-server --mode development\n\nor\n\n    npm start\n\n## ESLint\n\n    npm i -D babel-eslint eslint eslint-loader eslint-plugin-react\n\nAdd `eslint-loader` to Webpack Config:\n\n```diff\n      module: {\n        rules: [\n          {\n            test: /\\.js$/,\n            exclude: /node_modules/,\n-            use: ['babel-loader']\n+            use: ['babel-loader', 'eslint-loader']\n          }\n        ]\n      },\n```\n\nand add a ESLint configuration `.eslintrc`:\n\n    {\n      parser: \"babel-eslint\",\n      plugins: [\"react\"],\n      rules: {\n        \"react/prop-types\": [\"warn\"]\n      },\n      extends: [\"eslint:recommended\", \"plugin:react/recommended\"],\n      env: {\n        browser: true,\n        node: true,\n        es6: true\n      }\n    }\n\n## Accessibility (a11y) in React\n\nLint and audit for Accessibility issues using **ESLint** and **React-axe**.\n\nFirst install\n\n    npm i -D eslint-plugin-jsx-a11y react-axe\n\nthen extend `.exlintrc` configuration\n\n```diff\n    {\n      parser: \"babel-eslint\",\n-     plugins: [\"react\"],\n+     plugins: [\"react\", \"jsx-a11y\"],\n      rules: {\n        \"react/prop-types\": [\"warn\"]\n      },\n-     extends: [\"eslint:recommended\", \"plugin:react/recommended\"],\n+     extends: [\"eslint:recommended\", \"plugin:react/recommended\", \"plugin:jsx-a11y/recommended\"],\n      env: {\n        browser: true,\n        node: true,\n        es6: true\n      }\n    }\n```\n\nand add in your **entry file** a call to `axe()` for **development only**. Audit runs in the browser and results will show in the Chrome DevTools console.\n\n```diff\n    import React from 'react'\n    import ReactDOM from 'react-dom'\n    import App from './App'\n\n+   if (process.env.NODE_ENV === 'development') {\n+     const axe = require('react-axe')\n+     axe(React, ReactDOM, 1000)\n+   }\n    ReactDOM.render(\u003cApp /\u003e, document.getElementById('app'))\n```\n\n# React Concepts\n\n## Elements\n\nReact Elements are the **smallest building blocks** of React apps. Elements are used to build **components** and are rendered into the DOM using `ReactDOM.render()`.\n\nElements are **immutable**, are **plain objects**, and are **cheap to create**.\n\n## Components\n\nConceptually, components are **pure functions**. They accept read-only **props** and return **elements** describing what should appear on the screen.\n\nAlways prefer **composition over inheritance** for components.\n\n**Container components** don’t know their children ahead of time, for example generic boxes like dialogs:\n\n```\n    const Dialog = (props) =\u003e {\n      return (\n        \u003cdiv\u003e\n          \u003cdiv style={{background:\"#eee\"}} {...props} /\u003e\n        \u003c/div\u003e\n      )\n    }\n\n    function App() {\n      return (\n        \u003cdiv\u003e\n          \u003cDialog\u003eHello Dialog\u003c/Dialog\u003e\n        \u003c/div\u003e\n      )\n    }\n```\n\n**Specialization** is achieved by composition, where a more *specific* component renders a more *generic* one and configures it with `props`:\n\n```diff\n-   const Dialog = (props) =\u003e {\n+   const Dialog = ({title, ...props}) =\u003e {\n      return (\n        \u003cdiv\u003e\n+         {title ? \u003ch3\u003e{title}\u003c/h3\u003e : null}\n          \u003cdiv style={{background:\"#eee\"}} {...props} /\u003e\n        \u003c/div\u003e\n      )\n    }\n\n+   const WelcomeDialog = props =\u003e {\n+     return \u003cDialog title=\"Welcome\" {...props} /\u003e\n+   }\n\n    function App() {\n      return (\n        \u003cdiv\u003e\n-         \u003cDialog\u003eHello Dialog\u003c/Dialog\u003e\n+         \u003cWelcomeDialog\u003eHello Welcome Dialog\u003c/WelcomeDialog\u003e\n        \u003c/div\u003e\n      )\n    }\n```\n\n## Props\n\nThe difference between `state` and `props` is, that `state` is **owned by the component** itself while `props` is something that is passed down to the component by it's **parent**.\n\nAnd the similarity (sort of) is that React **automatically re-renders** your component when either the component's `state` changes or when the component's `props` changes.\n\n## JSX\n\nBabel compiles JSX down to `React.createElement()` calls:\n\n```jsx\nconst element = (\n  \u003ch1 className=\"greeting\"\u003eHello World\u003c/h1\u003e\n)\n```\n\nis identical to:\n\n```javascript\nconst element = React.createElement('h1', {className: 'greeting'}, 'Hello World')\n```\n\n## State\n\n**Always** use `setState` function to change state and **never** mutate it directly (except in the constructor).\n\nAs updates of `setState` may be asynchronous, do not rely on it to update the state immediately.\n\nIf your new state does **not depend** on the old state, then you can use `this.setState(object)` like for\nexample:\n\n```javascript\n    this.setState({\n      active: true\n    })\n```\n\nIf your new state **depends** on the old state then use a callback `this.setState( (state, props) =\u003e {...})` like for example:\n\n```javascript\n    this.setState(state =\u003e ({\n      counter: state.counter + 1\n    }))\n```\n\nState updates are **merged**. If your state contains several independent variables, then you can update them independently with separate `setState()` calls.\n\n### Form State\n\nIn HTML, form elements such as `\u003cinput\u003e`, `\u003ctextarea\u003e`, and `\u003cselect\u003e` typically maintain their own state and update it based on user input. In React, mutable state is typically kept in the state property of components, and only updated with `setState()`.\n\nBest practice is to make the React state be the **single source of truth**.\n\n### Lifting State Up\n\nIf several components need to reflect the same changing data, it is recommended to **lift the shared state up** to their **closest common ancestor**.\n\nFor example a parent component can provide a **setState-callback-function** as a prop to a child component which sets state of the parent.\n\n## Lifecycle Methods\n\nStateful class components inherit from `React.Component` the following methods:\n\n1. The `componentWillMount()` method is only called one time, which is before the initial render. This method\n   **is deprecated** since React 16.3.\n\n2. The `componentDidMount()` method runs after the component output has been rendered to the DOM.\n\n3. The `componentWillUnmount()` method runs before the component output has been removed from the DOM.\n\n# Error Boundaries\n\nA JavaScript error in a part of the UI shouldn’t break the whole app. `try / catch` only works for **imperative** code, however React components are **declarative** and specify **what** should be rendered.\n\n**Error boundaries** preserve the declarative nature of React.\n\nFirst create a Error boundary component `./src/`\n\n    import React from 'react'\n    export default class DefaultErrorBoundary extends React.Component {\n      state = { hasError: false }\n      static getDerivedStateFromError() {\n        return { hasError: true }\n      }\n      render() {\n        const { hasError } = this.state\n        const { children } = this.props\n        return hasError ? \u003cdiv\u003eSomething went wrong\u003c/div\u003e : children\n      }\n    }\n\nThen wrap your component or app with it, e.g. like this:\n\n```diff\n    import React from 'react'\n    import ReactDOM from 'react-dom'\n+   import DefaultErrorBoundary from './DefaultErrorBoundary'\n    import App from './App'\n\n    ReactDOM.render(\n      \u003cReact.StrictMode\u003e\n+       \u003cDefaultErrorBoundary\u003e\n          \u003cApp /\u003e\n+       \u003c/DefaultErrorBoundary\u003e\n      \u003c/React.StrictMode\u003e,\n      document.getElementById('app'))\n```\n\nReact doesn’t need error boundaries to recover from errors in **event handlers**. Unlike the render method and lifecycle methods, the event handlers don’t happen during rendering. So if they throw, React still knows what to display on the screen. \nIf you need to catch an error inside event handler, use the regular JavaScript `try / catch` statements.\n\n# Testing React with Jest\n\nFirst install **jest** test runner\n\n    npm i -D jest\n    npm i -D react-testing-library jest-dom\n    npm i -D babel-jest babel-core@bridge\n\nthen add a jest configuration `jest.config.js` for **testing globals**:\n\n    module.exports = {\n      setupTestFrameworkScriptFile: '\u003crootDir\u003e/testSetup.js'\n    }\n\nand create this referenced setup script `testSetup.js`:\n\n    import 'jest-dom/extend-expect'\n    import 'react-testing-library/cleanup-after-each'\n\nand then create a **test file** matching this filename pattern `**/__tests__/**/*.js?(x),**/?(*.)+(spec|test).js?(x)`, e.g. `./src/App.test.js`\n\n    import React from 'react'\n    import { render } from 'react-testing-library'\n    import App from './App'\n\n    describe('App', () =\u003e {\n      it('should render', () =\u003e {\n        render(\u003cApp/\u003e)\n      })\n    })\n\nand run test with \n\n    nps test jest\n    mpn t\n\n## Tests with Dynamic Imports\n\nFor **dynamic imports**, we need **babel-plugin-dynamic-import-node**:\n\n    npm i -D babel-plugin-dynamic-import-node\n\nand a environment specific plugin setting in `.babelrc`:\n\n```diff\n    {\n      presets: [\n        \"@babel/preset-env\",\n        \"@babel/preset-react\"\n      ],\n      plugins: [\n        '@babel/plugin-proposal-class-properties',\n        '@babel/plugin-syntax-dynamic-import'\n      ],\n+     env: {\n+       test: {\n+         plugins: [\n+           'dynamic-import-node'\n+         ]\n+       }\n+     }\n    }\n```\n\n# Main Concepts\n\nTODO\n\n# Refs\n\nRefs provide a way to **access DOM nodes or React elements** created in the render method.\n\nRefs are attached to React elements via the `ref` attribute.\n\nRefs are either created by using `React.createRef()` and referenced with the `current` attribute of the ref or by simply using a callback pattern `el =\u003e this.myRef = el` and a referrenc of `myRef`.\n\nThe value of the ref depends on the type of node:\n\n1. When the ref attribute is used on an **HTML element**, the ref receives the underlying **DOM element** as its current property. You can use the `ref` attribute inside a function component as long as you refer a DOM element.\n\n2. When the ref attribute is used on a **custom class component**, the ref object receives the **mounted instance of the component** as its current. You can not use the `ref` attribute on function components.\n\n### Example using the callback pattern\n\n```jsx\nclass TextInput extends Component {\n  componentDidMount() {\n    this.textInput.focus()\n  }\n  render() {\n    return (\n      \u003cinput type=\"text\" ref={el =\u003e this.textInput = el} /\u003e\n    )\n  }\n}\n```\n\n### Example using createRef\n\n```diff\n    class TextInput extends Component {\n+     textInput = React.createRef()\n      componentDidMount() {\n-       this.textInput.focus()\n+       this.textInput.current.focus()\n      }\n      render() {\n        return (\n-         \u003cinput type=\"text\" ref={el =\u003e this.textInput = el} /\u003e\n+         \u003cinput type=\"text\" ref={this.textInput} /\u003e\n        )\n      }\n    }\n```\n\n# Render Props\n\nThe term **render prop** refers to a pattern for sharing code between React components using a **prop** whose value is a **function**.\n\n### Example\n\nHaving a stateful component `WithState.jsx` which expects a function rendering this state\n\n```jsx\nconst WithState = props =\u003e {\n  const name = 'foobar'\n  return (\n    \u003cdiv\u003e\n      {props.children(name)}\n    \u003c/div\u003e\n  )\n}\n```\n\nyou can then use this component and implement non-dependent components which use this state like this\n\n```jsx\nconst App = () =\u003e {\n  return (\n    \u003cdiv\u003e\n      \u003cWithState\u003e\n        {value =\u003e \u003cHeadline value={value}/\u003e}\n      \u003c/WithState\u003e\n      \u003cWithState\u003e\n        {value =\u003e \u003cParagraph value={value}/\u003e}\n      \u003c/WithState\u003e\n    \u003c/div\u003e\n  )\n}\n\nconst Headline = ({value}) =\u003e \u003ch1\u003e{value}\u003c/h1\u003e\nconst Paragraph = ({value}) =\u003e \u003cp\u003e{value}\u003c/p\u003e\n```\n\nThis is analogue to a closure:\n\n```javascript\nconst withState = fn =\u003e {\n  const state = 'foobar'\n  fn(state)\n}\n\nconst app = () =\u003e {\n  withState(value =\u003e {\n    // do something with value\n  })\n}\n\n```\n\n# React Ecosystem\n\n## React Router\n\n**React Router** provides browser features like\n\n1. browser should **change the URL** when you navigate to a different screen.\n\n2. **Deep linking** should work.\n\n3. The **browser back (and forward) button** should work like expected.\n\n**React Router** provides two different kind of routes using the **History API**:\n\n1. `BrowserRouter` which builds classic URLs like `https://application.com/dashboard`\n\n2. `HashRouter` which builds URLs like `https://application.com/#/dashboard`\n\n### Install\n\n    npm install react-router-dom\n\n### Components\n\n1. `BrowserRouter`, usually aliased as `Router` wraps all your Route components\n\n2. `Link` generate links to your routes\n\n3. `Route` show - or hide - the components they contain\n\n### BrowserRouter\n\nA `\u003cBrowserRouter\u003e` component can only have one child element, hence wrapps the complete application.\n\n```diff\n    import React from 'react'\n+   import { BrowserRouter as Router } from 'react-router-dom'\n\n    export default () =\u003e {\n      return (\n+       \u003cRouter\u003e\n          \u003cdiv\u003e\n            // Application\n          \u003c/div\u003e\n+       \u003c/Router\u003e\n      )\n    }\n```\n\n### Link\n\nThe `\u003cLink\u003e` component is used to trigger new routes.\n\n```diff\n    import React from 'react'\n-   import { BrowserRouter as Router } from 'react-router-dom'\n+   import { BrowserRouter as Router, Link } from 'react-router-dom'\n\n    export default () =\u003e {\n      return (\n        \u003cRouter\u003e\n          \u003cdiv\u003e\n            // Application\n+           \u003cnav\u003e\n+             \u003cLink to={'/home'}\u003eHome\u003c/Link\u003e\n+             \u003cLink to={'/about'}\u003eAbout\u003c/Link\u003e\n+           \u003c/nav\u003e\n          \u003c/div\u003e\n        \u003c/Router\u003e\n      )\n    }\n```\n\n### Route\n\nAnywhere that you want to only render content based on the location’s pathname, you should use a `\u003cRoute\u003e` element.\n\n```diff\n    import React from 'react'\n-   import { BrowserRouter as Router, Link } from 'react-router-dom'\n+   import { BrowserRouter as Router, Link, Route } from 'react-router-dom'\n+   import { Home, About } from './Pages'\n\n    export default () =\u003e {\n      return (\n        \u003cRouter\u003e\n          \u003cdiv\u003e\n-           // Application\n+           \u003cmain\u003e\n+             \u003cRoute exact path='/' component={Home} /\u003e\n+             \u003cRoute path='/about' component={About} /\u003e\n+           \u003c/main\u003e\n            \u003cnav\u003e\n              \u003cLink to={'/home'}\u003eHome\u003c/Link\u003e\n              \u003cLink to={'/about'}\u003eAbout\u003c/Link\u003e\n            \u003c/nav\u003e\n          \u003c/div\u003e\n        \u003c/Router\u003e\n      )\n    }\n```\n\nWithout the `exact` attribute, `path='/'` would also match `/about`, since `/` is contained in the route.\n\nRoutes have three props that can be used to define what should be rendered when the route’s path matches. **Only one** should be provided to a `\u003cRoute\u003e` element.\n\n1. `component`: a React component.\n\n2. `render`: a function that returns a React element.\n\n3. `children`: a function that returns a React element. Unlike the prior two props, this will always be rendered, regardless of whether the route’s path matches the current location.\n\n```jsx\n\u003cRoute path='/page' component={Page} /\u003e\n\n\u003cRoute path='/page' render={ props =\u003e (\n  \u003cPage {...props} data={extraProps}/\u003e\n)}/\u003e\n\n\u003cRoute path='/page' children={ props =\u003e (\n  props.match ? \u003cPage {...props}/\u003e : \u003cEmptyPage {...props}/\u003e\n)}/\u003e\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthomd%2Fon-react","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthomd%2Fon-react","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthomd%2Fon-react/lists"}