From 1c2f4fafd05b02e94104b288f2c98bb5a2a8b65a Mon Sep 17 00:00:00 2001 From: Angelos Chalaris Date: Tue, 3 Dec 2019 11:45:54 +0200 Subject: [PATCH] Update for integration tools --- .travis.yml | 3 +- .travis/push.sh | 2 +- README.md | 2516 +-- _headers | 4 - assets/30s-icon.png | Bin 23213 -> 0 bytes assets/NotoSans-Italic.woff2 | Bin 119928 -> 0 bytes assets/NotoSans-Light.woff2 | Bin 120552 -> 0 bytes assets/NotoSans-LightItalic.woff2 | Bin 126060 -> 0 bytes assets/NotoSans-Medium.woff2 | Bin 121020 -> 0 bytes assets/NotoSans-MediumItalic.woff2 | Bin 123444 -> 0 bytes assets/NotoSans-Regular.woff2 | Bin 118412 -> 0 bytes assets/NotoSans-SemiBold.woff2 | Bin 120852 -> 0 bytes assets/NotoSans-SemiBoldItalic.woff2 | Bin 122740 -> 0 bytes assets/RobotoMono-Italic.woff2 | Bin 17672 -> 0 bytes assets/RobotoMono-Medium.woff2 | Bin 16380 -> 0 bytes assets/RobotoMono-Regular.woff2 | Bin 16328 -> 0 bytes assets/logo.png | Bin 35387 -> 0 bytes gatsby-browser.js | 29 - gatsby-config.js | 83 - gatsby-node.js | 93 - gatsby-ssr.js | 8 - netlify.toml | 6 +- package-lock.json | 17913 +--------------- package.json | 44 +- scripts/build.js | 118 - scripts/extract.js | 75 - scripts/util/environmentCheck.js | 11 - scripts/util/helpers.js | 54 - scripts/util/index.js | 33 - scripts/util/snippetParser.js | 127 - src/docs/components/Meta.js | 78 - src/docs/components/SVGs/BackArrowIcon.js | 22 - src/docs/components/SVGs/ClipboardIcon.js | 22 - .../components/SVGs/CollapseClosedIcon.js | 23 - src/docs/components/SVGs/CollapseOpenIcon.js | 22 - src/docs/components/SVGs/DarkModeIcon.js | 21 - src/docs/components/SVGs/GithubIcon.js | 21 - src/docs/components/SVGs/LightModeIcon.js | 29 - src/docs/components/SVGs/ListIcon.js | 26 - src/docs/components/SVGs/SearchIcon.js | 24 - src/docs/components/SVGs/ShareIcon.js | 25 - src/docs/components/Search.js | 28 - src/docs/components/Shell.js | 136 - src/docs/components/SimpleCard.js | 21 - src/docs/components/SnippetCard.js | 175 - src/docs/pages/404.js | 58 - src/docs/pages/about.js | 104 - src/docs/pages/index.js | 177 - src/docs/pages/list.js | 140 - src/docs/pages/search.js | 144 - src/docs/state/ReduxWrapper.js | 13 - src/docs/state/app.js | 49 - src/docs/state/index.js | 4 - src/docs/styles/_button.scss | 86 - src/docs/styles/_card.scss | 182 - src/docs/styles/_code.scss | 121 - src/docs/styles/_colors.scss | 140 - src/docs/styles/_fonts.scss | 89 - src/docs/styles/_layout.scss | 346 - src/docs/styles/_menu.scss | 65 - src/docs/styles/_reset.scss | 135 - src/docs/styles/_search.scss | 57 - src/docs/styles/_toast.scss | 33 - src/docs/styles/index.scss | 14 - src/docs/templates/SnippetPage.js | 103 - src/docs/templates/TagPage.js | 84 - src/docs/util/index.js | 121 - src/static-parts/README-end.md | 5 - src/static-parts/README-start.md | 41 - static/_redirects | 45 + static/robots.txt | 2 - yarn.lock | 11536 ---------- 72 files changed, 287 insertions(+), 35399 deletions(-) delete mode 100644 _headers delete mode 100644 assets/30s-icon.png delete mode 100644 assets/NotoSans-Italic.woff2 delete mode 100644 assets/NotoSans-Light.woff2 delete mode 100644 assets/NotoSans-LightItalic.woff2 delete mode 100644 assets/NotoSans-Medium.woff2 delete mode 100644 assets/NotoSans-MediumItalic.woff2 delete mode 100644 assets/NotoSans-Regular.woff2 delete mode 100644 assets/NotoSans-SemiBold.woff2 delete mode 100644 assets/NotoSans-SemiBoldItalic.woff2 delete mode 100644 assets/RobotoMono-Italic.woff2 delete mode 100644 assets/RobotoMono-Medium.woff2 delete mode 100644 assets/RobotoMono-Regular.woff2 delete mode 100644 assets/logo.png delete mode 100644 gatsby-browser.js delete mode 100644 gatsby-config.js delete mode 100644 gatsby-node.js delete mode 100644 gatsby-ssr.js delete mode 100644 scripts/build.js delete mode 100644 scripts/extract.js delete mode 100644 scripts/util/environmentCheck.js delete mode 100644 scripts/util/helpers.js delete mode 100644 scripts/util/index.js delete mode 100644 scripts/util/snippetParser.js delete mode 100644 src/docs/components/Meta.js delete mode 100644 src/docs/components/SVGs/BackArrowIcon.js delete mode 100644 src/docs/components/SVGs/ClipboardIcon.js delete mode 100644 src/docs/components/SVGs/CollapseClosedIcon.js delete mode 100644 src/docs/components/SVGs/CollapseOpenIcon.js delete mode 100644 src/docs/components/SVGs/DarkModeIcon.js delete mode 100644 src/docs/components/SVGs/GithubIcon.js delete mode 100644 src/docs/components/SVGs/LightModeIcon.js delete mode 100644 src/docs/components/SVGs/ListIcon.js delete mode 100644 src/docs/components/SVGs/SearchIcon.js delete mode 100644 src/docs/components/SVGs/ShareIcon.js delete mode 100644 src/docs/components/Search.js delete mode 100644 src/docs/components/Shell.js delete mode 100644 src/docs/components/SimpleCard.js delete mode 100644 src/docs/components/SnippetCard.js delete mode 100644 src/docs/pages/404.js delete mode 100644 src/docs/pages/about.js delete mode 100644 src/docs/pages/index.js delete mode 100644 src/docs/pages/list.js delete mode 100644 src/docs/pages/search.js delete mode 100644 src/docs/state/ReduxWrapper.js delete mode 100644 src/docs/state/app.js delete mode 100644 src/docs/state/index.js delete mode 100644 src/docs/styles/_button.scss delete mode 100644 src/docs/styles/_card.scss delete mode 100644 src/docs/styles/_code.scss delete mode 100644 src/docs/styles/_colors.scss delete mode 100644 src/docs/styles/_fonts.scss delete mode 100644 src/docs/styles/_layout.scss delete mode 100644 src/docs/styles/_menu.scss delete mode 100644 src/docs/styles/_reset.scss delete mode 100644 src/docs/styles/_search.scss delete mode 100644 src/docs/styles/_toast.scss delete mode 100644 src/docs/styles/index.scss delete mode 100644 src/docs/templates/SnippetPage.js delete mode 100644 src/docs/templates/TagPage.js delete mode 100644 src/docs/util/index.js delete mode 100644 src/static-parts/README-end.md delete mode 100644 src/static-parts/README-start.md create mode 100644 static/_redirects delete mode 100644 static/robots.txt delete mode 100644 yarn.lock diff --git a/.travis.yml b/.travis.yml index ad4d6e410..c19d800e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,6 @@ node_js: - lts/* script: - npm run extractor -- npm run builder after_success: - chmod +x .travis/push.sh -- .travis/push.sh \ No newline at end of file +- .travis/push.sh diff --git a/.travis/push.sh b/.travis/push.sh index f60647fd8..b3546d61f 100644 --- a/.travis/push.sh +++ b/.travis/push.sh @@ -25,7 +25,7 @@ upload_files() { if [ $TRAVIS_EVENT_TYPE != "pull_request" ]; then if [ $TRAVIS_BRANCH == "master" ]; then echo "Pushing to master branch..." - git push --force --quiet "https://${GH_TOKEN}@github.com/30-seconds/30-seconds-of-react.git" master > /dev/null 2>&1 + git push --quiet "https://${GH_TOKEN}@github.com/30-seconds/30-seconds-of-react.git" master > /dev/null 2>&1 fi fi } diff --git a/README.md b/README.md index 674d9e04f..369616e24 100644 --- a/README.md +++ b/README.md @@ -1,2506 +1,26 @@ -![Logo](/logo.png) +[![Logo](/logo.png)](https://30secondsofcode.org/react/p/1) # 30 seconds of React -> Curated collection of useful React snippets that you can understand in 30 seconds or less. +> Short React code snippets for all your development needs -- Use Ctrl + F or command + F to search for a snippet. -- Contributions welcome, please read the [contribution guide](CONTRIBUTING.md). -- Snippets are written in React 16.8+, using hooks. +* Visit [our website](https://30secondsofcode.org) to view our snippet collection. +* Use the [Search page](https://30secondsofcode.org/search) to find snippets that suit your needs. You can search by name, tag, language or using a snippet's description. Just start typing a term and see what comes up. +* Browse the [React Snippet List](https://30secondsofcode.org/react/p/1) to see all the snippets in this project or click individual tags at the top of the same page to narrow down your search to a specific tag. +* Click on each snippet card to view the whole snippet, including code, explanation and examples. +* You can use the button on the right side of a snippet card to copy the code to clipboard. +* If you like the project, give it a star. It means a lot to the people maintaining it. -### Prerequisites +## Want to contribute? -To import a snippet into your project, you must import `React` and copy-paste the component's JavaScript code like this: +* If you want to help us improve, take a minute to read the [Contribution Guidelines](/CONTRIBUTING.md) first. +* Use the [Snippet Template](/snippet-template.md) to add new snippets to the collection. +* If you find a problem with a specific snippet, please [open an issue](https://github.com/30-seconds/30-seconds-of-react/issues/new). +* If you find a problem with the website, please [report it in the web repository](https://github.com/30-seconds/30-seconds-web/issues/new). -```js -import React from 'react'; - -function MyComponent(props) { - /* ... */ -} -``` - -If there is any CSS related to your component, copy-paste it to a new file with the same name and the appropriate extension, then import it like this: - -```js -import './MyComponent.css'; -``` - -To render your component, make sure there is a node with and id of `"root"` present in your element (preferrably a `
`) and that you have imported `ReactDOM`, like this: - -```js -import ReactDOM from 'react-dom'; -``` - -#### Related projects - -- [30 Seconds of Code](https://30secondsofcode.org) -- [30 Seconds of CSS](https://30-seconds.github.io/30-seconds-of-css/) -- [30 Seconds of Interviews](https://30secondsofinterviews.org/) - -## Table of Contents - -### Array - -
-View contents - -* [`DataList`](#datalist) -* [`DataTable`](#datatable) -* [`MappedTable`](#mappedtable) - -
- -### Hooks - -
-View contents - -* [`useClickInside`](#useclickinside) -* [`useClickOutside`](#useclickoutside) -* [`useFetch`](#usefetch) -* [`useInterval`](#useinterval) -* [`useNavigatorOnLine`](#usenavigatoronline) -* [`useSSR`](#usessr) -* [`useTimeout`](#usetimeout) - -
- -### Input - -
-View contents - -* [`ControlledInput`](#controlledinput) -* [`LimitedTextarea`](#limitedtextarea) -* [`LimitedWordTextarea`](#limitedwordtextarea) -* [`MultiselectCheckbox`](#multiselectcheckbox) -* [`PasswordRevealer`](#passwordrevealer) -* [`Select`](#select) -* [`Slider`](#slider) -* [`TagInput`](#taginput) -* [`TextArea`](#textarea) -* [`UncontrolledInput`](#uncontrolledinput) - -
- -### Visual - -
-View contents - -* [`Accordion`](#accordion-) -* [`Alert`](#alert) -* [`AutoLink`](#autolink-) -* [`Carousel`](#carousel) -* [`Collapse`](#collapse) -* [`CountDown`](#countdown-) -* [`FileDrop`](#filedrop) -* [`Loader`](#loader) -* [`Mailto`](#mailto) -* [`Modal`](#modal) -* [`RippleButton`](#ripplebutton) -* [`StarRating`](#starrating) -* [`Tabs`](#tabs) -* [`Ticker`](#ticker) -* [`Toggle`](#toggle) -* [`Tooltip`](#tooltip) -* [`TreeView`](#treeview-) - -
- - ---- - -## Array - - -### DataList - -Renders a list of elements from an array of primitives. - -- Use the value of the `isOrdered` prop to conditionally render a `
    ` or `
      ` list. -- Use `Array.prototype.map` to render every item in `data` as a `
    • ` element, give it a `key` produced from the concatenation of the its index and value. -- Omit the `isOrdered` prop to render a `
        ` list by default. - -```jsx -function DataList({ isOrdered, data }) { - const list = data.map((val, i) =>
      • {val}
      • ); - return isOrdered ?
          {list}
        :
          {list}
        ; -} -``` - -
        -Examples - -```jsx -const names = ['John', 'Paul', 'Mary']; -ReactDOM.render(, document.getElementById('root')); -ReactDOM.render(, document.getElementById('root')); -``` -
        - -
        [⬆ Back to top](#contents) - -### DataTable - -Renders a table with rows dynamically created from an array of primitives. - -- Render a `` element with two columns (`ID` and `Value`). -- Use `Array.prototype.map` to render every item in `data` as a `` element, consisting of its index and value, give it a `key` produced from the concatenation of the two. - -```jsx -function DataTable({ data }) { - return ( -
        - - - - - - - - {data.map((val, i) => ( - - - - - ))} - -
        IDValue
        {i}{val}
        - ); -} -``` - -
        -Examples - -```jsx -const people = ['John', 'Jesse']; -ReactDOM.render(, document.getElementById('root')); -``` -
        - -
        [⬆ Back to top](#contents) - -### MappedTable - -Renders a table with rows dynamically created from an array of objects and a list of property names. - -- Use `Object.keys()`, `Array.prototype.filter()`, `Array.prototype.includes()` and `Array.prototype.reduce()` to produce a `filteredData` array, containing all objects with the keys specified in `propertyNames`. -- Render a `` element with a set of columns equal to the amount of values in `propertyNames`. -- Use `Array.prototype.map` to render each value in the `propertyNames` array as a `` element, containing a `
        ` element. -- Use `Array.prototype.map` to render each object in the `filteredData` array as a `
        ` for each key in the object. - -_This component does not work with nested objects and will break if there are nested objects inside any of the properties specified in `propertyNames`_ - -```jsx -function MappedTable({ data, propertyNames }) { - let filteredData = data.map(v => - Object.keys(v) - .filter(k => propertyNames.includes(k)) - .reduce((acc, key) => ((acc[key] = v[key]), acc), {}) - ); - return ( - - - - {propertyNames.map(val => ( - - ))} - - - - {filteredData.map((val, i) => ( - - {propertyNames.map(p => ( - - ))} - - ))} - -
        {val}
        {val[p]}
        - ); -} -``` - -
        -Examples - -```jsx -const people = [ - { name: 'John', surname: 'Smith', age: 42 }, - { name: 'Adam', surname: 'Smith', gender: 'male' } -]; -const propertyNames = ['name', 'surname', 'age']; -ReactDOM.render( - , - document.getElementById('root') -); -``` -
        - -
        [⬆ Back to top](#contents) - ---- - -## Hooks - - -### useClickInside - -A hook that handles the event of clicking inside the wrapped component. - -- Create a custom hook that takes a `ref` and a `callback` to handle the `click` event. -- Use the `React.useEffect()` hook to append and clean up the `click` event. -- Use the `React.useRef()` hook to create a `ref` for your click component and pass it to the `useClickInside` hook. - -```jsx -const useClickInside = (ref, callback) => { - const handleClick = e => { - if (ref.current && ref.current.contains(e.target)) { - callback(); - } - }; - React.useEffect(() => { - document.addEventListener('click', handleClick); - return () => { - document.removeEventListener('click', handleClick); - }; - }); -}; -``` - -
        -Examples - -```jsx -const ClickBox = ({ onClickInside }) => { - const clickRef = React.useRef(); - useClickInside(clickRef, onClickInside); - return ( -
        -

        Click inside this element

        -
        - ); -}; - -ReactDOM.render( - alert('click inside')} />, - document.getElementById('root') -); -``` -
        - -
        [⬆ Back to top](#contents) - -### useClickOutside - -A hook that handles the event of clicking outside of the wrapped component. - -- Create a custom hook that takes a `ref` and a `callback` to handle the `click` event. -- Use the `React.useEffect()` hook to append and clean up the `click` event. -- Use the `React.useRef()` hook to create a `ref` for your click component and pass it to the `useClickOutside` hook. - -```jsx -const useClickOutside = (ref, callback) => { - const handleClick = e => { - if (ref.current && !ref.current.contains(e.target)) { - callback(); - } - }; - React.useEffect(() => { - document.addEventListener('click', handleClick); - return () => { - document.removeEventListener('click', handleClick); - }; - }); -}; -``` - -
        -Examples - -```jsx -const ClickBox = ({ onClickOutside }) => { - const clickRef = React.useRef(); - useClickOutside(clickRef, onClickOutside); - return ( -
        -

        Click out of this element

        -
        - ); -}; - -ReactDOM.render( - alert('click outside')} />, - document.getElementById('root') -); -``` -
        - -
        [⬆ Back to top](#contents) - -### useFetch - -A hook that implements `fetch` in a declarative manner. - -- Create a custom hook that takes a `url` and `options`. -- Use the `React.useState()` hook to initialize the `response` and `error` state variables. -- Use the `React.useEffect()` hook to anychronously call `fetch()` and update the state varaibles accordingly. -- Return an object containting the `response` and `error` state variables. - -```jsx -const useFetch = (url, options) => { - const [response, setResponse] = React.useState(null); - const [error, setError] = React.useState(null); - - React.useEffect(() => { - const fetchData = async () => { - try { - const res = await fetch(url, options); - const json = await res.json(); - setResponse(json); - } catch (error) { - setError(error); - } - }; - fetchData(); - }, []); - - return { response, error }; -}; -``` - -
        -Examples - -```jsx -const ImageFetch = props => { - const res = useFetch('https://dog.ceo/api/breeds/image/random', {}); - if (!res.response) { - return
        Loading...
        ; - } - const imageUrl = res.response.message; - return ( -
        - avatar -
        - ); -}; - -ReactDOM.render(, document.getElementById('root')); -``` -
        - -
        [⬆ Back to top](#contents) - -### useInterval - -A hook that implements `setInterval` in a declarative manner. - -- Create a custom hook that takes a `callback` and a `delay`. -- Use the `React.useRef()` hook to create a `ref` for the callback function. -- Use the `React.useEffect()` hook to remember the latest callback. -- Use the `React.useEffect()` hook to set up the interval and clean up. - -```jsx -const useInterval = (callback, delay) => { - const savedCallback = React.useRef(); - - React.useEffect(() => { - savedCallback.current = callback; - }, [callback]); - - React.useEffect(() => { - function tick() { - savedCallback.current(); - } - if (delay !== null) { - let id = setInterval(tick, delay); - return () => clearInterval(id); - } - }, [delay]); -}; -``` - -
        -Examples - -```jsx -const Timer = props => { - const [seconds, setSeconds] = React.useState(0); - useInterval(() => { - setSeconds(seconds + 1); - }, 1000); - - return

        {seconds}

        ; -}; - -ReactDOM.render(, document.getElementById('root')); -``` -
        - -
        [⬆ Back to top](#contents) - -### useNavigatorOnLine - -A hook that returns if the client is online or offline. - -- Create a function, `getOnLineStatus`, that uses the `NavigatorOnLine` web API to get the online status of the client. -- Use the `React.useState()` hook to create an appropriate state variable, `status`, and setter. -- Use the `React.useEffect()` hook to add listeners for appropriate events, updating state, and cleanup those listeners when unmounting. -- Finally return the `status` state variable. - -```jsx -const getOnLineStatus = () => - typeof navigator !== "undefined" && typeof navigator.onLine === "boolean" - ? navigator.onLine - : true; - -const useNavigatorOnLine = () => { - const [status, setStatus] = React.useState(getOnLineStatus()); - - const setOnline = () => setStatus(true); - const setOffline = () => setStatus(false); - - React.useEffect(() => { - window.addEventListener("online", setOnline); - window.addEventListener("offline", setOffline); - - return () => { - window.removeEventListener("online", setOnline); - window.removeEventListener("offline", setOffline); - }; - }, []); - - return status; -}; -``` - -
        -Examples - -```jsx -const StatusIndicator = () => { - const isOnline = useNavigatorOnLine(); - - return You are {isOnline ? "online" : "offline"}.; -}; - -ReactDOM.render(, document.getElementById("root")); -``` -
        - -
        [⬆ Back to top](#contents) - -### useSSR - -A hook that checks if the code is running on the browser or the server. - -- Create a custom hook that returns an appropriate object. -- Use `typeof window`, `window.document` and `window.document.createElement` to check if the code is running on the browser. -- Use the `React.useState()` hook to define the `inBrowser` state variable. -- Use the `React.useEffect()` hook to update the `inBrowser` state variable and clean up at the end. -- Use the `React.useMemo()` to memoize the return values of the custom hook. - -```jsx -const isDOMavailable = !!( - typeof window !== 'undefined' && - window.document && - window.document.createElement -); - -const useSSR = (callback, delay) => { - const [inBrowser, setInBrowser] = React.useState(isDOMavailable); - - React.useEffect(() => { - setInBrowser(isDOMavailable); - return () => { - setInBrowser(false); - } - }, []); - - const useSSRObject = React.useMemo(() => ({ - isBrowser: inBrowser, - isServer: !inBrowser, - canUseWorkers: typeof Worker !== 'undefined', - canUseEventListeners: inBrowser && !!window.addEventListener, - canUseViewport: inBrowser && !!window.screen - }), [inBrowser]); - - return React.useMemo(() => Object.assign(Object.values(useSSRObject), useSSRObject), [inBrowser]); -}; -``` - -
        -Examples - -```jsx -const SSRChecker = props => { - let { isBrowser, isServer } = useSSR(); - - return

        { isBrowser ? 'Running on browser' : 'Running on server' }

        ; -}; - -ReactDOM.render(, document.getElementById('root')); -``` -
        - -
        [⬆ Back to top](#contents) - -### useTimeout - -A hook that implements `setTimeout` in a declarative manner. - -- Create a custom hook that takes a `callback` and a `delay`. -- Use the `React.useRef()` hook to create a `ref` for the callback function. -- Use the `React.useEffect()` hook to remember the latest callback. -- Use the `React.useEffect()` hook to set up the timeout and clean up. - -```jsx -const useTimeout = (callback, delay) => { - const savedCallback = React.useRef(); - - React.useEffect(() => { - savedCallback.current = callback; - }, [callback]); - - React.useEffect(() => { - function tick() { - savedCallback.current(); - } - if (delay !== null) { - let id = setTimeout(tick, delay); - return () => clearTimeout(id); - } - }, [delay]); -}; -``` - -
        -Examples - -```jsx -const OneSecondTimer = props => { - const [seconds, setSeconds] = React.useState(0); - useTimeout(() => { - setSeconds(seconds + 1); - }, 1000); - - return

        {seconds}

        ; -}; - -ReactDOM.render(, document.getElementById('root')); -``` -
        - -
        [⬆ Back to top](#contents) - ---- - -## Input - - -### ControlledInput - -Renders an `` element with internal state, that uses a callback function to pass its value to the parent component. - -- Use object destructuring to set defaults for certain attributes of the `` element. -- Use the `React.setState()` hook to create the `value` state variable and give it a value of equal to the `defaultValue` prop. -- Use the `React.useEffect()` hook with a second parameter set to the `value` state variable to call the `callback` function every time `value` is updated. -- Render an `` element with the appropriate attributes and use the the `onChange` event to upda the `value` state variable. - -```jsx -function ControlledInput({ - callback, - type = 'text', - disabled = false, - readOnly = false, - defaultValue, - placeholder = '' -}) { - const [value, setValue] = React.useState(defaultValue); - - React.useEffect(() => { - callback(value); - }, [value]); - - return ( - setValue(value)} - /> - ); -} -``` - -
        -Examples - -```jsx -ReactDOM.render( - console.log(val)} - />, - document.getElementById('root') -); -``` -
        - -
        [⬆ Back to top](#contents) - -### LimitedTextarea - -Renders a textarea component with a character limit. - -- Use the `React.useState()` hook to create the `content` state variable and set its value to `value`. - Create a method `setFormattedContent`, which trims the content of the input if it's longer than `limit`. -- Use the `React.useEffect()` hook to call the `setFormattedContent` method on the value of the `content` state variable. -- Use a`
        ` to wrap both the`