Update fork to match original repo

Update fork to match original repo
This commit is contained in:
le-mahf
2019-02-22 17:29:45 +00:00
committed by GitHub
28 changed files with 1543 additions and 1254 deletions

81
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,81 @@
# Contribution Guidelines
**30 seconds of React** is a community effort, so feel free to contribute in any way you can. Every contribution helps!
Here's what you can do to help:
- Submit [pull requests](https://github.com/30-seconds/30-seconds-of-react/pulls) with snippets that you have created (see below for guidelines).
- [Open issues](https://github.com/30-seconds/30-seconds-of-react/issues/new) for things you want to see added or modified.
- Be part of the discussion by helping out with [existing issues](https://github.com/30-seconds/30-seconds-of-react/issues).
- Fix typos in existing snippets, improve snippet descriptions and explanations or provide better examples.
### Snippet submission and Pull request guidelines
- **DO NOT MODIFY THE README.md or index.html FILES!** Make changes to individual snippet files. **Travis CI** will automatically build the `README.md` and `index.html` files when your pull request is merged.
- **Snippet filenames** must correspond to the title of the snippet. For example, if your snippet is titled `### AwesomeComponent` the filename should be `AwesomeComponent.md`.
- Use `TitleCase`, not `camelCase`, `kebab-case` or `snake_case` when naming components.
- Avoid capitalization of words, except if the whole word is capitalized (e.g. `URL` should be capitalized in the filename and the snippet title).
- **Snippet titles** should be the same as the name of the component that is present in the snippet.
- All snippet titles must be prefixed with `###` and be at the very first line of your snippet.
- Snippet titles must be unique (although if you cannot find a better title, just add some placeholder at the end of the filename and title and we will figure it out).
- Follow snippet titles with an empty line.
- **Snippet descriptions** must be short and to the point. Try to explain *what* the snippet does and *how* the snippet works and what Javascript/React features are used. Remember to include what functions you are using and why.
- Follow snippet descriptions with an empty line.
- **Snippet code** must be enclosed inside ` ```jsx ` and ` ``` `.
- If your snippet is accompanied by CSS code, enclose it inside ` ```css ` and ` ``` ` and present it before the JS/JSX code.
- Remember to start your snippet's code on a new line below the opening backticks.
- Use standard function notation to define your component. For example `function MyComponent(props) { ... }`.
- Do not write components using classes, use [React Hooks](https://reactjs.org/docs/hooks-intro.html) instead.
- Please use Javascript [Semi-Standard Style](https://github.com/Flet/semistandard).
- Try to keep your snippets' code short and to the point. Use modern techniques and features. Make sure to test your code before submitting.
- All snippets must be followed by one (more if necessary) test case after the code, in a new block enclosed inside ` ```jsx ` and ` ``` `. The syntax for this is `ReactDOM.render(<MyComponent />, document.getElementById("root"));`. Use multiline examples only if necessary.
- Try to make your component name unique, so that it does not conflict with existing snippets.
- Snippets should be as brief as possible, without sacrificing functionality. If your snippet seems longer than most, you can still submit it, and we can help you shorten it or figure out ways to improve it.
- Snippets *should* solve real-world problems, no matter how simple.
- Snippets *should* be abstract enough to be applied to different scenarios.
- You can start creating a new snippet, by using the [snippet template](snippet-template.md) to format your snippets.
### Additional guidelines and conventions regarding snippets
- When describing snippets, refer to methods, using their full name. For example, use `Array.prototype.reduce()`, instead of `reduce()`.
- When using React Hooks, refer to the specific hooks with their full names, such as `React.useState()` and `React.useEffect()`.
- When using `React.useState()`, try matching the name of the state variable to the function that sets it. For example, use `[isShown, setIsShown]` instead of `[isShown, setShown]`.
- When using `React.useEffect()`, only return a function if you have to clean up. In that case, name that function `cleanup()`.
- Destructure your component's `props` whenever possible. If any of your props take default parameters, specify their default values in the destructured object.
- If your snippet uses recursion, explain the base cases.
- Always use `function MyComponent(props)` or `function MyComponent({ ... })` for function definitions.
- Use variables only when necessary. Prefer `const` when the values are not altered after assignment, otherwise, use `let`. Avoid using `var`.
- Use `camelCase` for function and variable names, if they consist of more than one word.
- Use `TitleCase` for component names.
- Try to give meaningful names to variables. For example use `letter`, instead of `lt`. Some exceptions to convention are:
- `arr` for arrays (usually as the snippet function's argument).
- `str` for strings.
- `num` or `n` for a numeric value (usually as the snippet function's argument).
- `el` for DOM elements (usually as the snippet function's argument).
- `val` or `v` for value (usually when iterating a list, mapping, sorting etc.).
- `acc` for accumulators in `Array.prototype.reduce()`.
- `(a,b)` for the two values compared when using `Array.prototype.sort()`.
- `i` for indexes.
- `fn` for function arguments.
- `nums` for arrays of numbers.
- Use `()` if your function takes no arguments.
- Use `_` if an argument inside some function (e.g. `Array.prototype.reduce()`) is not used anywhere in your code.
- Specify default parameters for arguments, if necessary. It is preferred to put default parameters last unless you have a pretty good reason not to.
- If your snippet's function takes variadic arguments, use `...args` or `...rest` (although in certain cases, it might be needed to use a different name).
- Always use soft tabs (2 spaces), never hard tabs.
- Omit curly braces (`{` and `}`) whenever possible.
- Always use single quotes for string literals. Use template literals, instead, if necessary.
- When rendering JSX, use double quotes, instead of single quotes.
- Prefer using `Array` methods whenever possible.
- Prefer `Array.prototype.concat()` instead of `Array.prototype.push()` when working with `Array.prototype.reduce()`.
- Use strict equality checking (`===` and `!==` instead of `==` and `!=`), unless you specifically have reason not to.
- Prefer using the ternary operator (`condition ? trueResult : falseResult`) instead of `if else` statements whenever possible.
- Avoid nesting ternary operators (but you can do it if you feel like you should).
- You should define multiple variables (e.g. `const x = 0, y = 0`) on the same line whenever possible.
- Do not use trailing or leading underscores in variable names.
- Use dot notation (`object.property`) for object properties, when possible. Use bracket notation (`object[variable]`) when accessing object properties using a variable.
- Use arrow functions as much as possible, except when you can't.
- Use semicolons whenever necessary.
- Leave a single space after a comma (`,`) character.
- Try to strike a balance between readability, brevity, and performance.
- Never use `eval()`. Your snippet will be disqualified immediately.

955
README.md

File diff suppressed because it is too large Load Diff

View File

@ -11,16 +11,16 @@
"tags": [
"string",
"fragment",
"functional"
"regexp"
],
"notes": []
},
{
"name": "Carousel.md",
"title": "Carousel",
"text": "Renders a carousel component.\n\nInitially set `state.active` to `0` (index of the first item).\nUse an object, `style`, to hold the styles for the individual components.\nDefine a method, `setActiveItem`, which uses `this.setState` to change the state's `active` property to the index of the next item.\nDefine another method, `changeItem`, which is called by `setActiveItem` after updating the state each time and also when the component\nfirst renders (on `ComponentDidMount`).\nIn the `render()` method, destructure `state`, `style` and `props`, compute if visibility style should be set to `visible` or not for each carousel item while mapping over and applying the combined style to the carousel item component accordingly.\nRender the carousel items using [React.cloneElement](https://reactjs.org/docs/react-api.html#cloneelement) and pass down rest\n`props` along with the computed styles.\n\n",
"text": "Renders a carousel component.\n\nUse the `React.setState()` hook to create the `active` state variable and give it a value of `0` (index of the first item).\nUse an object, `style`, to hold the styles for the individual components.\nUse the `React.setEffect()` hook to update the value of `active` to the index of the next item, using `setTimeout`.\nDestructure `props`, compute if visibility style should be set to `visible` or not for each carousel item while mapping over and applying the combined style to the carousel item component accordingly.\nRender the carousel items using `React.cloneElement()` and pass down rest `props` along with the computed styles.\n\n",
"codeBlocks": [
"```jsx\nclass Carousel extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n active: 0\n };\n this.scrollInterval = null;\n this.style = {\n carousel: {\n position: \"relative\"\n },\n carouselItem: {\n position: \"absolute\",\n visibility: \"hidden\"\n },\n visible: {\n visibility: \"visible\"\n }\n };\n }\n componentDidMount() {\n this.changeItem();\n }\n setActiveItem = () => {\n const { carouselItems } = this.props;\n this.setState(\n prevState => ({\n active: (prevState.active + 1) % carouselItems.length\n }),\n this.changeItem\n );\n };\n changeItem = () => {\n this.scrollInterval = setTimeout(this.setActiveItem, 2000);\n };\n render() {\n const { carouselItems, ...rest } = this.props;\n const { active } = this.state;\n const { visible, carousel, carouselItem } = this.style;\n return (\n <div style={carousel}>\n {carouselItems.map((item, index) => {\n const activeStyle = active === index ? visible : {};\n return React.cloneElement(item, {\n ...rest,\n style: {\n ...carouselItem,\n ...activeStyle\n }\n });\n })}\n </div>\n );\n }\n}\n```",
"```jsx\nfunction Carousel(props) {\n const [active, setActive] = React.useState(0);\n let scrollInterval = null;\n const style = {\n carousel: {\n position: \"relative\"\n },\n carouselItem: {\n position: \"absolute\",\n visibility: \"hidden\"\n },\n visible: {\n visibility: \"visible\"\n }\n };\n React.useEffect(() => {\n scrollInterval = setTimeout(() => {\n const { carouselItems } = props;\n setActive((active + 1) % carouselItems.length);\n }, 2000);\n });\n const { carouselItems, ...rest } = props;\n return (\n <div style={style.carousel}>\n {carouselItems.map((item, index) => {\n const activeStyle = active === index ? style.visible : {};\n return React.cloneElement(item, {\n ...rest,\n style: {\n ...style.carouselItem,\n ...activeStyle\n }\n });\n })}\n </div>\n );\n}\n```",
"```jsx\nReactDOM.render(\n <Carousel\n carouselItems={[\n <div>carousel item 1</div>,\n <div>carousel item 2</div>,\n <div>carousel item 3</div>\n ]}\n />,\n document.getElementById(\"root\")\n);\n ```"
],
"expertise": 2,
@ -28,24 +28,23 @@
"visual",
"children",
"state",
"class"
"effect"
],
"notes": []
},
{
"name": "Collapse.md",
"title": "Collapse",
"text": "Renders a component with collapsible content.\n\nUse the value of the `collapsed` prop to determine the initial state of the content (collapsed/expanded).\nSet the `state` of the component to the value of the `collapsed` prop (cast to a boolean value) and bind the `toggleCollapse` method to the component's context.\nUse an object, `style`, to hold the styles for individual components and their states.\nCreate a method, `toggleCollapse`, which uses `Component.prototype.setState` to change the component's `state` from collapsed to expanded and vice versa.\nIn the `render()` method, use a `<div>` to wrap both the `<button>` that alters the component's `state` and the content of the component, passed down via `props.children`.\nDetermine the appearance of the content, based on `state.collapsed` and apply the appropriate CSS rules from the `style` object.\nFinally, update the value of the `aria-expanded` attribute based on `state.collapsed` to make the component accessible.\n\n",
"text": "Renders a component with collapsible content.\n\nUse the `React.setState()` hook to create the `isCollapsed` state variable with an initial value of `props.collapsed`.\nUse an object, `style`, to hold the styles for individual components and their states.\nUse a `<div>` to wrap both the `<button>` that alters the component's `isCollapsed` state and the content of the component, passed down via `props.children`.\nDetermine the appearance of the content, based on `isCollapsed` and apply the appropriate CSS rules from the `style` object.\nFinally, update the value of the `aria-expanded` attribute based on `isCollapsed` to make the component accessible.\n\n",
"codeBlocks": [
"```jsx\nclass Collapse extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n collapsed: !!props.collapsed\n };\n this.style = {\n collapsed: {\n display: 'none'\n },\n expanded: {\n display: 'block'\n },\n buttonStyle: {\n display: 'block',\n width: '100%'\n }\n };\n this.toggleCollapse = this.toggleCollapse.bind(this);\n }\n \n toggleCollapse() {\n this.setState(state => ({ collapsed: !state.collapsed }));\n }\n \n render() {\n return (\n <div>\n <button style={this.style.buttonStyle} onClick={this.toggleCollapse}>\n {this.state.collapsed ? 'Show' : 'Hide'} content\n </button>\n <div \n style= {this.state.collapsed ? this.style.collapsed : this.style.expanded} \n aria-expanded = {this.state.collapsed}\n >\n {this.props.children}\n </div>\n </div>\n );\n }\n}\n```",
"```jsx\nfunction Collapse(props) {\n const [isCollapsed, setIsCollapsed] = React.useState(props.collapsed);\n\n const style = {\n collapsed: {\n display: \"none\"\n },\n expanded: {\n display: \"block\"\n },\n buttonStyle: {\n display: \"block\",\n width: \"100%\"\n }\n };\n\n return (\n <div>\n <button\n style={style.buttonStyle}\n onClick={() => setIsCollapsed(!isCollapsed)}\n >\n {isCollapsed ? \"Show\" : \"Hide\"} content\n </button>\n <div\n className=\"collapse-content\"\n style={isCollapsed ? style.collapsed : style.expanded}\n aria-expanded={isCollapsed}\n >\n {props.children}\n </div>\n </div>\n );\n}\n```",
"```jsx\nReactDOM.render(\n <Collapse>\n <h1>This is a collapse</h1>\n <p>Hello world!</p>\n </Collapse>,\n document.getElementById('root')\n);\n```"
],
"expertise": 2,
"tags": [
"visual",
"children",
"state",
"class"
"state"
],
"notes": []
},
@ -59,8 +58,7 @@
],
"expertise": 0,
"tags": [
"array",
"functional"
"array"
],
"notes": []
},
@ -74,8 +72,25 @@
],
"expertise": 0,
"tags": [
"array",
"functional"
"array"
],
"notes": []
},
{
"name": "FileDrop.md",
"title": "FileDrop",
"text": "Renders a file drag and drop component for a single file.\n\nCreate a ref called `dropRef` for this component.\nUse the `React.useState()` hook to create the `drag` and `filename` variables, initialized to `false` and `''` respectively.\nThe variables `dragCounter` and `drag` are used to determine if a file is being dragged, while `filename` is used to store the dropped file's name.\n\nCreate the `handleDrag`, `handleDragIn`, `handleDragOut` and `handleDrop` methods to handle drag and drop functionality, bind them to the component's context.\nEach of the methods will handle a specific event, the listeners for which are created and removed in the `React.useEffect()` hook and its attached `cleanup()` method.\n`handleDrag` prevents the browser from opening the dragged file, `handleDragIn` and `handleDragOut` handle the dragged file entering and exiting the component, while `handleDrop` handles the file being dropped and passes it to `props.handleDrop`.\nReturn an appropriately styled `<div>` and use `drag` and `filename` to determine its contents and style. \nFinally, bind the `ref` of the created `<div>` to `dropRef`.\n\n\n",
"codeBlocks": [
"```css\n.filedrop {\n min-height: 120px;\n border: 3px solid #D3D3D3;\n text-align: center;\n font-size: 24px;\n padding: 32px;\n border-radius: 4px;\n}\n\n.filedrop.drag {\n border: 3px dashed #1E90FF;\n}\n\n.filedrop.ready {\n border: 3px solid #32CD32;\n}\n```",
"```jsx\nfunction FileDrop(props) {\n const [drag, setDrag] = React.useState(false);\n const [filename, setFilename] = React.useState('');\n let dropRef = React.createRef();\n let dragCounter = 0;\n\n const handleDrag = e => {\n e.preventDefault();\n e.stopPropagation();\n };\n\n const handleDragIn = e => {\n e.preventDefault();\n e.stopPropagation();\n dragCounter++;\n if (e.dataTransfer.items && e.dataTransfer.items.length > 0) setDrag(true);\n };\n\n const handleDragOut = e => {\n e.preventDefault();\n e.stopPropagation();\n dragCounter--;\n if (dragCounter === 0) setDrag(false);\n };\n\n const handleDrop = e => {\n e.preventDefault();\n e.stopPropagation();\n setDrag(false);\n if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {\n props.handleDrop(e.dataTransfer.files[0]);\n setFilename(e.dataTransfer.files[0].name);\n e.dataTransfer.clearData();\n dragCounter = 0;\n }\n };\n\n React.useEffect(() => {\n let div = dropRef.current;\n div.addEventListener(\"dragenter\", handleDragIn);\n div.addEventListener(\"dragleave\", handleDragOut);\n div.addEventListener(\"dragover\", handleDrag);\n div.addEventListener(\"drop\", handleDrop);\n return function cleanup() {\n div.removeEventListener(\"dragenter\", handleDragIn);\n div.removeEventListener(\"dragleave\", handleDragOut);\n div.removeEventListener(\"dragover\", handleDrag);\n div.removeEventListener(\"drop\", handleDrop);\n };\n });\n\n return (\n <div\n ref={dropRef}\n className={\n drag ? \"filedrop drag\" : filename ? \"filedrop ready\" : \"filedrop\"\n }\n >\n {filename && !drag ? <div>{filename}</div> : <div>Drop files here!</div>}\n </div>\n );\n}\n```",
"```jsx\nReactDOM.render(<FileDrop handleDrop={console.log}/>, document.getElementById('root'));\n```"
],
"expertise": 2,
"tags": [
"visual",
"input",
"state",
"effect"
],
"notes": []
},
@ -89,24 +104,39 @@
],
"expertise": 0,
"tags": [
"input",
"functional"
"input"
],
"notes": []
},
{
"name": "LimitedTextarea.md",
"title": "LimitedTextarea",
"text": "Renders a textarea component with a character limit.\n\nUse the value of the `value` prop to determine the initial `state.content` and `state.characterCount` and the value of the `limit` props to determine the value of `state.limit`.\nCreate a method, `handleChange`, which trims the `event.target.value` data if necessary and uses `Component.prototype.setState` to update `state.content` and `state.characterCount`, and bind it to the component's context.\nIn the`render()` method, use a`<div>` to wrap both the`<textarea>` and the `<p>` element that displays the character count and bind the `onChange` event of the `<textarea>` to the `handleChange` method.\n\n",
"text": "Renders a textarea component with a character limit.\n\nUse the `React.useState()` hook to create the `content` state variable and set its value to `value`.\nCreate a method `setFormattedContent`, which trims the content of the input if it's longer than `limit`.\nUse the `React.useEffect()` hook to call the `setFormattedContent` method on the value of the `content` state variable.\nUse a`<div>` to wrap both the`<textarea>` and the `<p>` element that displays the character count and bind the `onChange` event of the `<textarea>` to call `setFormattedContent` with the value of `event.target.value`.\n\n",
"codeBlocks": [
"```jsx\nclass LimitedTextarea extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n content: props.value,\n characterCount: props.value.length,\n limit: props.limit\n };\n this.handleChange = this.handleChange.bind(this);\n }\n \n handleChange(event) {\n let newContent = event.target.value;\n if(newContent.length >= this.state.limit) newContent = newContent.slice(0, this.state.limit);\n this.setState(state => ({ content: newContent, characterCount: newContent.length }));\n }\n render() {\n return (\n <div>\n <textarea \n rows={this.props.rows} \n cols={this.props.cols} \n onChange={this.handleChange} \n value={this.state.content}\n >\n </textarea>\n <p>{this.state.characterCount}/{this.props.limit}</p>\n </div>\n );\n }\n}\n```",
"```jsx\nfunction LimitedTextarea({ rows, cols, value, limit }) {\n const [content, setContent] = React.useState(value);\n\n const setFormattedContent = text => {\n text.length > limit ? setContent(text.slice(0, limit)) : setContent(text);\n };\n\n React.useEffect(() => {\n setFormattedContent(content);\n }, []);\n\n return (\n <div>\n <textarea\n rows={rows}\n cols={cols}\n onChange={event => setFormattedContent(event.target.value)}\n value={content}\n />\n <p>\n {content.length}/{limit}\n </p>\n </div>\n );\n}\n```",
"```jsx\nReactDOM.render(\n <LimitedTextarea limit={32} value='Hello!' />,\n document.getElementById('root')\n);\n```"
],
"expertise": 0,
"tags": [
"input",
"state",
"class"
"effect"
],
"notes": []
},
{
"name": "LimitedWordTextarea.md",
"title": "LimitedWordTextarea",
"text": "Renders a textarea component with a word limit.\n\nUse the `React.useState()` hook to create the `content` and `wordCount` state variables and set their values to `value` and `0` respectively.\nCreate a method `setFormattedContent`, which uses `String.prototype.split(' ')` to turn the input into an array of words and check if the result of applying `Array.prototype.filter(Boolean)` has a `length` longer than `limit`.\nIf the afforementioned `length` exceeds the `limit`, trim the input, otherwise return the raw input, updating `content` and `wordCount` accordingly in both cases.\nUse the `React.useEffect()` hook to call the `setFormattedContent` method on the value of the `content` state variable.\nUse a`<div>` to wrap both the`<textarea>` and the `<p>` element that displays the character count and bind the `onChange` event of the `<textarea>` to call `setFormattedContent` with the value of `event.target.value`.\n\n",
"codeBlocks": [
"```jsx\nfunction LimitedWordTextarea({ rows, cols, value, limit }) {\n const [content, setContent] = React.useState(value);\n const [wordCount, setWordCount] = React.useState(0);\n\n const setFormattedContent = text => {\n let words = text.split(\" \");\n if (words.filter(Boolean).length > limit) {\n setContent(\n text\n .split(\" \")\n .slice(0, limit)\n .join(\" \")\n );\n setWordCount(limit);\n } else {\n setContent(text);\n setWordCount(words.filter(Boolean).length);\n }\n };\n\n React.useEffect(() => {\n setFormattedContent(content);\n }, []);\n\n return (\n <div>\n <textarea\n rows={rows}\n cols={cols}\n onChange={event => setFormattedContent(event.target.value)}\n value={content}\n />\n <p>\n {wordCount}/{limit}\n </p>\n </div>\n );\n}\n```",
"```jsx\nReactDOM.render(\n <LimitedWordTextArea limit={5} value='Hello there!' />,\n document.getElementById('root')\n);\n```"
],
"expertise": 0,
"tags": [
"input",
"state",
"effect"
],
"notes": []
},
@ -120,8 +150,7 @@
],
"expertise": 0,
"tags": [
"visual",
"functional"
"visual"
],
"notes": []
},
@ -136,52 +165,26 @@
"expertise": 1,
"tags": [
"array",
"object",
"functional"
"object"
],
"notes": [
"This component does not work with nested objects and will break if there are nested objects inside any of the properties specified in `propertyNames`.",
"<!-tags: array,object,functional -->",
"<!-expertise: 1 -->"
]
},
{
"name": "ModalDialog.md",
"title": "ModalDialog",
"text": "Renders a dialog component in a modal, controllable through events. \nTo use the component, import `ModalDialog` only once and then display it using `ModalDialog.show()`, passing the JSX templates and data as parameters.\n\nDefine `modalHandler`, a method that will handle showing the modal dialog, set `state` to the default values initially and bind the `close` and `modalClick` methods to the component's context.\nDefine `close` and `modalClick` to toggle the visibility of the modal dialog, based on `state.closeOnClick`.\nUse the CustomEvent API to listen for `modal` events, that can be dispatched from the `static` `show()` method, handle listeners appropriately from `componentDidMount` and `componentWillUnmount`.\n\nThe `show()` method accepts an argument, that should contain three parameters:\n* `title`, a string for the dialog's title\n* `closeOnClick`, `true` if the modal should close on click or `false` if it should only close when clicking the *X* button\n* `content`, which is the JSX content to be rendered inside the dialog\n\nFinally, in the `render()` method, use a `<div>` to wrap everything and render the modal dialog with the content passed to `show()`.\n\n",
"codeBlocks": [
"```css\n .modal {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.6);\n z-index: 9998;\n display: flex;\n justify-content: center;\n align-items: center;\n }\n .dialog {\n background-color: white;\n border-radius: 5px;\n overflow: hidden;\n }\n .dialog-title {\n box-sizing: border-box;\n width: 100%;\n height: 48px;\n padding: 0 16px;\n border-bottom: 0.5px solid #c3c3c3;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n .dialog-close {\n font-size: 32px;\n color: #c3c3c3;\n cursor: pointer;\n transform: rotate(45deg);\n user-select: none;\n }\n .dialog-close:hover {\n color: red;\n }\n .dialog-content {\n min-width: 300px;\n }\n```",
"```jsx\nclass ModalDialog extends React.Component {\n constructor() {\n super();\n this.modalHandler = (e) => {\n this.setState({\n data: e.detail.data,\n visible: true\n });\n };\n this.state = {\n data: {\n title: '',\n closeOnClick: false,\n content: ''\n },\n visible: false\n };\n this.close = this.close.bind(this);\n this.modalClick = this.modalClick.bind(this);\n }\n render() {\n return !this.state.visible ? null : <div className=\"modal\" onClick={this.modalClick}>\n <div className=\"dialog\">\n <div className=\"dialog-title\">{ this.state.data.title }<span className=\"dialog-close\" onClick={this.close}>+</span></div>\n <div className=\"dialog-content\">\n {\n this.state.data.content\n }\n </div>\n </div>\n </div>\n }\n componentDidMount() {\n document.addEventListener('modal', this.modalHandler);\n }\n componentWillUnmount() {\n document.removeEventListener('modal', this.modalHandler);\n }\n close() {\n this.setState({\n visible: false,\n data: {\n title: '',\n closeOnClick: false,\n content: ''\n }\n });\n }\n static show(data) {\n document.dispatchEvent(new CustomEvent('modal', {\n detail: {\n data\n }\n }));\n }\n modalClick() {\n if (this.state.data.closeOnClick) this.close();\n }\n}\n```",
"```jsx\n// add to render function\n<ModalDialog />\n\n// every time you wanna call the dialog\n// content is a jsx element\nModalDialog.show({\n title: 'Hello, world!',\n closeOnClick: true,\n content: <img src=\"https://github.com/30-seconds/30-seconds-of-react/blob/master/logo.png\"/>\n}); \n```"
],
"expertise": 1,
"tags": [
"visual",
"static",
"children",
"state",
"class"
],
"notes": [
"This component includes a lot of CSS, which might conflict with other CSS in your project. It is recomended for the modal to be a direct child of the body tag.",
"A more up-to-date method with lower compatibility is to use [Portals](https://reactjs.org/docs/portals.html) in React 16+.",
"<!-tags: visual,static,children,state,class -->",
"<!-tags: array,object -->",
"<!-expertise: 1 -->"
]
},
{
"name": "PasswordRevealer.md",
"title": "PasswordRevealer",
"text": "Renders a password input field with a reveal button.\n\nInitially set `state.shown` to `false` to ensure that the password is not shown by default.\nCreate a method, `toggleShown`, which uses `Component.prototype.setState` to change the input's state from shown to hidden and vice versa, bind it to the component's context.\nIn the`render()` method, use a`<div>` to wrap both the`<input>` and the `<button>` element that toggles the type of the input field.\nFinally, bind the `<button>`'s `onClick` event to the `toggleShown` method.\n\n",
"text": "Renders a password input field with a reveal button.\n\nUse the `React.useState()` hook to create the `shown` state variable and set its value to `false`.\nUse a`<div>` to wrap both the`<input>` and the `<button>` element that toggles the type of the input field between `\"text\"` and `\"password\"`.\n\n",
"codeBlocks": [
"```jsx\nclass PasswordRevealer extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n shown: false\n };\n this.toggleShown = this.toggleShown.bind(this);\n }\n\n toggleShown() {\n this.setState(state => ({ shown: !state.shown }));\n }\n\n render() {\n return (\n <div>\n <input\n type={this.state.shown ? 'text' : 'password'}\n value={this.props.value}\n />\n <button onClick={this.toggleShown}>Show/Hide</button>\n </div>\n );\n }\n}\n```",
"```jsx\nfunction PasswordRevealer({ value }) {\n const [shown, setShown] = React.useState(false);\n\n return (\n <div>\n <input\n type={shown ? \"text\" : \"password\"}\n value={value}\n onChange={() => {}}\n />\n <button onClick={() => setShown(!shown)}>Show/Hide</button>\n </div>\n );\n}\n```",
"```jsx\nReactDOM.render(<PasswordRevealer />, document.getElementById('root'));\n```"
],
"expertise": 0,
"tags": [
"input",
"state",
"class"
"state"
],
"notes": []
},
@ -195,17 +198,16 @@
],
"expertise": 0,
"tags": [
"input",
"functional"
"input"
],
"notes": []
},
{
"name": "StarRating.md",
"title": "StarRating",
"text": "Renders a star rating component.\n\nUse and IIFE to define a functional component, called `Star` that will render each individual star with the appropriate appearance, based on the parent component's `state` and return the class component `StarRating`.\nUse the value of the `rating` prop to determine if a valid rating is supplied and store it in `state.rating` (or `0` if invalid or not supplied).\nInitialize `state.selection` to `0`.\nCreate two methods, `hoverOver` and `setRating`, that take an event as argument and update `state.selected` and `state.rating` according to it, bind them both to the component's context.\nIn the `render()` method, create a `<div>` to wrap the `<Star>` components, which are created using `Array.prototype.map` on an array of 5 elements, created using `Array.from`, and handle the `onMouseLeave` event to set `state.selection` to `0`, the `onClick` event to set\nthe `state.rating` and the `onMouseOver` event to set `state.selection` to the `star-id` attribute of the `event.target` respectively. \nFinally, pass the appropriate values to each `<Star>` component (`starId` and `marked`).\n\n",
"text": "Renders a star rating component.\n\nDefine a component, called `Star` that will render each individual star with the appropriate appearance, based on the parent component's state.\nIn the `StarRating` component, use the `React.setState()` hook to define the `rating` and `selection` state variables with the initial values of `props.rating` (or `0` if invalid or not supplied) and `0`.\nCreate a method, `hoverOver`, that updates `selected` and `rating` according to the provided `event`.\nCreate a `<div>` to wrap the `<Star>` components, which are created using `Array.prototype.map` on an array of 5 elements, created using `Array.from`, and handle the `onMouseLeave` event to set `selection` to `0`, the `onClick` event to set the `rating` and the `onMouseOver` event to set `selection` to the `star-id` attribute of the `event.target` respectively. \nFinally, pass the appropriate values to each `<Star>` component (`starId` and `marked`).\n\n",
"codeBlocks": [
"```jsx\nconst StarRating = (function() {\n function Star({ marked, starId }) {\n return (\n <span star-id={starId} style={{ color: '#ff9933' }} role='button'>\n {marked ? '\\u2605' : '\\u2606'}\n </span>\n );\n }\n\n return class StarRating extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n rating: typeof props.rating == 'number' ? props.rating : 0,\n selection: 0\n };\n this.hoverOver = this.hoverOver.bind(this);\n this.hoverOut = this.hoverOver.bind(this, null);\n this.handleClick = this.handleClick.bind(this);\n }\n hoverOver(event) {\n let val = 0;\n if (event && event.target && event.target.getAttribute('star-id'))\n val = event.target.getAttribute('star-id');\n this.setState(state => ({ selection: val }));\n }\n handleClick(event) {\n const val = event.target.getAttribute('star-id') || this.state.rating;\n this.setState(state => ({ rating: val }));\n }\n render() {\n return (\n <div\n onMouseOut={this.hoverOut}\n onClick={this.handleClick}\n onMouseOver={this.hoverOver}\n >\n {Array.from({ length: 5 }, (v, i) => (\n <Star\n starId={i+1}\n key={`star_${i+1} `}\n marked={\n this.state.selection\n ? this.state.selection >= i+1\n : this.state.rating >= i+1\n }\n />\n ))}\n </div>\n );\n }\n };\n})();\n```",
"```jsx\nfunction Star({ marked, starId }) {\n return (\n <span star-id={starId} style={{ color: \"#ff9933\" }} role=\"button\">\n {marked ? \"\\u2605\" : \"\\u2606\"}\n </span>\n );\n}\n\nfunction StarRating(props) {\n const [rating, setRating] = React.useState(\n typeof props.rating == \"number\" ? props.rating : 0\n );\n const [selection, setSelection] = React.useState(0);\n const hoverOver = event => {\n let val = 0;\n if (event && event.target && event.target.getAttribute(\"star-id\"))\n val = event.target.getAttribute(\"star-id\");\n setSelection(val);\n };\n return (\n <div\n onMouseOut={() => hoverOver(null)}\n onClick={() =>\n setRating(event.target.getAttribute(\"star-id\") || this.state.rating)\n }\n onMouseOver={hoverOver}\n >\n {Array.from({ length: 5 }, (v, i) => (\n <Star\n starId={i + 1}\n key={`star_${i + 1} `}\n marked={selection ? selection >= i + 1 : rating >= i + 1}\n />\n ))}\n </div>\n );\n}\n```",
"```jsx\nReactDOM.render(<StarRating/>, document.getElementById('root'));\nReactDOM.render(<StarRating rating={2} />, document.getElementById('root'));\n```"
],
"expertise": 2,
@ -213,25 +215,24 @@
"visual",
"children",
"input",
"state",
"class"
"state"
],
"notes": []
},
{
"name": "Tabs.md",
"title": "Tab",
"text": "Renders a tabbed menu and view component.\n\nDefine `TabItem` as a middleware, pass it to the `Tab` and remove unnecessary nodes expect for `TabItem` by identifying the function's name in `props.children`.\nUse `Array.prototype.map` on the collected nodes to render the `tab-menu` and `tab-view`. \nDefine `changeTab`, which will be executed when clicking a `<button>` from the `tab-menu`.\n`changeTab` executes the passed callback, `onTabClick` and updates `state.bindIndex`, which in turn causes a re-render, evaluating the `style` and `className` of the `tab-view` items and `tab-menu` buttons according to their `index`.\n\n",
"text": "Renders a tabbed menu and view component.\n\nDefine a `TabItem` component, pass it to the `Tab` and remove unnecessary nodes expect for `TabItem` by identifying the function's name in `props.children`.\nUse the `React.useState()` hook to initialize the value of the `bindIndex` state variable to `props.defaultIndex`. \nUse `Array.prototype.map` on the collected nodes to render the `tab-menu` and `tab-view`. \nDefine `changeTab`, which will be executed when clicking a `<button>` from the `tab-menu`.\n`changeTab` executes the passed callback, `onTabClick` and updates `bindIndex`, which in turn causes a re-render, evaluating the `style` and `className` of the `tab-view` items and `tab-menu` buttons according to their `index`.\n\n",
"codeBlocks": [
"```css\n.tab-menu > button {\n cursor: pointer;\n padding: 8px 16px;\n border: 0;\n border-bottom: 2px solid transparent;\n background: none;\n}\n.tab-menu > button.focus {\n border-bottom: 2px solid #007BEF;\n}\n.tab-menu > button:hover {\n border-bottom: 2px solid #007BEF;\n}\n```",
"```jsx\nclass Tab extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n bindIndex: props.defaultIndex\n };\n }\n changeTab(newIndex) {\n if (typeof this.props.onTabClick === \"function\")\n this.props.onTabClick(newIndex);\n this.setState({\n bindIndex: newIndex\n });\n }\n buttonClass(index) {\n return this.state.bindIndex === index ? \"focus\" : \"\";\n }\n itemStyle(index) {\n return {\n display: this.state.bindIndex === index ? \"block\" : \"none\"\n };\n }\n render() {\n const items = this.props.children.filter(\n item => item.type.name === \"TabItem\"\n );\n return (\n <div className=\"wrapper\">\n <div className=\"tab-menu\">\n {items.map(({ props: { index, label } }) => (\n <button\n onClick={() => this.changeTab(index)}\n className={this.buttonClass(index)}\n >\n {label}\n </button>\n ))}\n </div>\n <div className=\"tab-view\">\n {items.map(({ props }) => (\n <div\n {...props}\n className=\"tab-view_item\"\n key={props.index}\n style={this.itemStyle(props.index)}\n />\n ))}\n </div>\n </div>\n );\n }\n}\nfunction TabItem(props) {\n return <div {...props} />;\n}\n```",
"```jsx\nReactDOM.render(\n <Tab defaultIndex=\"1\" onTabClick={console.log}>\n <TabItem label=\"A\" index=\"1\">\n Lorem ipsum\n </TabItem>\n <TabItem label=\"B\" index=\"2\">\n Dolor sit amet\n </TabItem>\n </Tab>,\n document.getElementById(\"root\")\n);\n\n```"
"```jsx\nfunction TabItem(props) {\n return <div {...props} />;\n}\n\nfunction Tabs(props) {\n const [bindIndex, setBindIndex] = React.useState(props.defaultIndex);\n const changeTab = newIndex => {\n if (typeof props.onTabClick === \"function\") props.onTabClick(newIndex);\n setBindIndex(newIndex);\n };\n const items = props.children.filter(item => item.type.name === \"TabItem\");\n\n return (\n <div className=\"wrapper\">\n <div className=\"tab-menu\">\n {items.map(({ props: { index, label } }) => (\n <button\n onClick={() => changeTab(index)}\n className={bindIndex === index ? \"focus\" : \"\"}\n >\n {label}\n </button>\n ))}\n </div>\n <div className=\"tab-view\">\n {items.map(({ props }) => (\n <div\n {...props}\n className=\"tab-view_item\"\n key={props.index}\n style={{ display: bindIndex === props.index ? \"block\" : \"none\" }}\n />\n ))}\n </div>\n </div>\n );\n}\n```",
"```jsx\nReactDOM.render(\n <Tabs defaultIndex=\"1\" onTabClick={console.log}>\n <TabItem label=\"A\" index=\"1\">\n Lorem ipsum\n </TabItem>\n <TabItem label=\"B\" index=\"2\">\n Dolor sit amet\n </TabItem>\n </Tabs>,\n document.getElementById(\"root\")\n);\n\n```"
],
"expertise": 1,
"tags": [
"visual",
"children",
"class"
"state",
"children"
],
"notes": []
},
@ -245,57 +246,72 @@
],
"expertise": 0,
"tags": [
"input",
"functional"
"input"
],
"notes": []
},
{
"name": "Ticker.md",
"title": "Ticker",
"text": "Renders a ticker component.\n\n- The ticker state is initially set to zero \n- When the `Tick!` button is clicked, `timer` is incremented periodically at the given `interval`\n- When the `Reset` button is clicked, the value of the timer is set to zero and the `setInterval` is cleared\n- The `setInterval` is cleared once the desired `time` is reached\n- `time` and `interval` are the required props\n\n",
"text": "Renders a ticker component.\n\nUse the `React.useState()` hook to initialize the `ticker` state variable to `0`.\nDefine two methods, `tick` and `reset`, that will periodically increment `timer` based on `interval` and reset `interval` respectively.\nReturn a `<div>` with two `<button>` elements, each of which calls `tick` and `reset` respectively.\n\n",
"codeBlocks": [
"```jsx\nclass Ticker extends Component {\n\tconstructor(props) {\n\t\tsuper(props);\n\t\tthis.state = {ticker: 0}\n\t\tthis.interval = null\n\t}\n\n\ttick = () => {\n\t\tthis.reset()\n\t\tthis.interval = setInterval(() => {\n\t\t\tif (this.state.ticker < this.props.times) {\n\t\t\t\tthis.setState(({ ticker }) => ({ticker: ticker + 1}))\n\t\t\t}else{\n\t\t\t\tclearInterval(this.interval)\n\t\t\t}\n\t\t}, this.props.interval)\n\t}\n\n\treset = () => {\n\t\tthis.setState({ticker: 0})\n\t\tclearInterval(this.interval)\n\t}\n\n\trender() {\n\t\treturn (\n\t\t\t<div>\n\t\t\t\t<span style={{fontSize: 100}}>{this.state.ticker}</span> \n\t\t\t\t<button onClick={this.tick}>Tick!</button>\n\t\t\t\t<button onClick={this.reset}>Reset</button>\n\t\t\t</div>\n\t\t);\n\t}\n}\n```",
"```jsx\nfunction Ticker(props) {\n const [ticker, setTicker] = React.useState(0);\n let interval = null;\n\n const tick = () => {\n reset();\n interval = setInterval(() => {\n if (ticker < props.times) \n setTicker(ticker + 1);\n else \n clearInterval(interval);\n }, props.interval);\n }\n\n const reset = () => {\n setTicker(0);\n clearInterval(interval);\n }\n\n return (\n <div>\n <span style={{ fontSize: 100 }}>{this.state.ticker}</span>\n <button onClick={this.tick}>Tick!</button>\n <button onClick={this.reset}>Reset</button>\n </div>\n );\n}\n```",
"```jsx\nReactDOM.render(<Ticker times={5} interval={1000} />, document.getElementById('root'));\n```"
],
"expertise": 1,
"tags": [
"visual",
"state",
"class"
"state"
],
"notes": []
},
{
"name": "Toggle.md",
"title": "Toggle",
"text": "Renders a toggle component.\n\nInitialize `state.isToggleOn` to `false`, bind the `handleClick` method to the component's context.\nUse an object, `style`, to hold the styles for individual components and their states.\nCreate a method, `handleClick`, which uses `Component.prototype.setState` to change the component's `state.toggleOn`.\nIn the `render()` method, destructure `state` and `style`, create a `<button>` that alters the component's `state` and determine the appearance of the content based on `state.isToggleOn`, applying the appropriate CSS rules from the `style` object.\n\n",
"text": "Renders a toggle component.\n\nUse the `React.useState()` to initialize the `isToggleOn` state variable to `false`.\nUse an object, `style`, to hold the styles for individual components and their states.\nReturn a `<button>` that alters the component's `isToggledOn` when its `onClick` event is fired and determine the appearance of the content based on `isToggleOn`, applying the appropriate CSS rules from the `style` object.\n\n",
"codeBlocks": [
"```jsx\nclass Toggle extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n isToggleOn: false\n };\n this.style = {\n on: {\n backgroundColor: 'green'\n },\n off: {\n backgroundColor: 'grey'\n }\n };\n\n this.handleClick = this.handleClick.bind(this);\n }\n\n handleClick() {\n this.setState(state => ({\n isToggleOn: !state.isToggleOn\n }));\n }\n\n render() {\n const { isToggleOn } = this.state;\n const { on, off } = this.style;\n\n return (\n <button\n onClick={this.handleClick}\n style={isToggleOn ? on : off}\n >\n {isToggleOn ? 'ON' : 'OFF'}\n </button>\n );\n }\n}\n```",
"```jsx\nfunction Toggle(props) {\n const [isToggleOn, setIsToggleOn] = React.useState(false);\n style = {\n on: {\n backgroundColor: \"green\"\n },\n off: {\n backgroundColor: \"grey\"\n }\n };\n\n return (\n <button\n onClick={() => setIsToggleOn(!isToggleOn)}\n style={isToggleOn ? style.on : style.off}\n >\n {isToggleOn ? \"ON\" : \"OFF\"}\n </button>\n );\n}\n```",
"```jsx\nReactDOM.render(<Toggle />, document.getElementById('root'));\n```"
],
"expertise": 0,
"tags": [
"visual",
"state",
"class"
"state"
],
"notes": []
},
{
"name": "Tooltip.md",
"title": "Tooltip",
"text": "Renders a tooltip component.\n\nSet the `state` of the component to `show: false` initially, define an object, `style`, to hold the styles for individual components and their states.\nCreate a method, `toggleTooltip`, which uses `this.setState` to change the state's `show` property from `true` to `false` and vice versa.\nBind `showTooltip` and `hideTooltip` to the component's context with the respective values of `true` and `false`.\nIn the `render()` method, compute if the tooltip should be shown or hidden, render the content of the tooltip and bind the `onMouseEnter` and `onMouseLeave` events to `showTooltip` and `hideTooltip` respectively.\n \n",
"text": "Renders a tooltip component.\n\nUse the `React.useState()` hook to create the `show` variable and initialize it to `false`.\nReturn a `<div>` element that contains the `<div>` that will be the tooltip and the `children` passed to the component.\nHandle the `onMouseEnter` and `onMouseLeave` methods, by altering the value of the `show` variable.\n \n",
"codeBlocks": [
"```jsx\nclass Tooltip extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n show: false\n };\n this.style = {\n tooltip: {\n position: 'relative',\n backgroundColor: \"rgba(0,0,0,0.7)\",\n color: \"white\",\n visibility: \"hidden\",\n width: \"fit-content\",\n padding: 5,\n borderRadius: 5\n },\n tooltipArrow: {\n position: 'absolute',\n top: '100%',\n left: '50%',\n borderWidth: 5,\n borderStyle: 'solid',\n borderColor: \"rgba(0,0,0,0.7) transparent transparent\",\n },\n visible: {\n visibility: \"visible\"\n },\n };\n this.showTooltip = this.toggleTooltip.bind(this, true);\n this.hideTooltip = this.toggleTooltip.bind(this, false);\n }\n\n toggleTooltip = tooltipState => {\n this.setState({\n show: tooltipState\n });\n };\n\n render() {\n const { children, text, ...rest } = this.props;\n const { show } = this.state;\n const { visible, tooltip, tooltipArrow } = this.style;\n const showTooltip = show ? visible : {};\n return (\n <div>\n <div style={{ ...tooltip, ...showTooltip }}>\n {text}\n <span style={tooltipArrow}/>\n </div>\n <div {...rest} onMouseEnter={this.showTooltip} onMouseLeave={this.hideTooltip}>\n {children}\n </div>\n </div>\n );\n }\n}\n```",
"```css\n.tooltip {\n position: relative;\n background: rgba(0, 0, 0, 0.7);\n color: white;\n visibility: hidden;\n padding: 5px;\n border-radius: 5px;\n}\n.tooltip-arrow {\n position: absolute;\n top: 100%;\n left: 50%;\n border-width: 5px;\n border-style: solid;\n border-color: rgba(0, 0, 0, 0.7) transparent transparent;\n}\n```",
"```jsx\nfunction Tooltip({ children, text, ...rest }) {\n const [show, setShow] = React.useState(false);\n\n return (\n <div>\n <div className=\"tooltip\" style={show ? { visibility: \"visible\" } : {}}>\n {text}\n <span className=\"tooltip-arrow\" />\n </div>\n <div\n {...rest}\n onMouseEnter={() => setShow(true)}\n onMouseLeave={() => setShow(false)}\n >\n {children}\n </div>\n </div>\n );\n}\n```",
"```jsx\n ReactDOM.render(\n <Tooltip text='Simple tooltip'>\n <button>Hover me!</button>\n </Tooltip>,\n document.getElementById('root')\n );\n```"
],
"expertise": 1,
"tags": [
"visual",
"state",
"children",
"class"
"children"
],
"notes": []
},
{
"name": "TreeView.md",
"title": "TreeView",
"text": "Renders a tree view of a JSON object or array with collapsible content.\n\nUse object destructuring to set defaults for certain props. \nUse the value of the `toggled` prop to determine the initial state of the content (collapsed/expanded).\nUse the `React.setState()` hook to create the `isToggled` state variable and give it the value of the `toggled` prop initially.\nReturn a `<div>` to wrap the contents of the component and the `<span>` element, used to alter the component's `isToggled` state.\nDetermine the appearance of the component, based on `isParentToggled`, `isToggled`, `name` and `Array.isArray()` on `data`. \nFor each child in `data`, determine if it is an object or array and recursively render a sub-tree.\nOtherwise, render a `<p>` element with the appropriate style.\n\n",
"codeBlocks": [
"```css\n.tree-element {\n margin: 0;\n position: relative;\n}\n\ndiv.tree-element:before {\n content: '';\n position: absolute;\n top: 24px;\n left: 1px;\n height: calc(100% - 48px);\n border-left: 1px solid gray;\n}\n\n.toggler {\n position: absolute;\n top: 10px;\n left: 0px;\n width: 0; \n height: 0; \n border-top: 4px solid transparent;\n border-bottom: 4px solid transparent;\n border-left: 5px solid gray;\n cursor: pointer;\n}\n\n.toggler.closed {\n transform: rotate(90deg);\n}\n\n.collapsed {\n display: none;\n}\n```",
"```jsx\nfunction TreeView({\n data,\n toggled = true,\n name = null,\n isLast = true,\n isChildElement = false,\n isParentToggled = true\n}) {\n const [isToggled, setIsToggled] = React.useState(toggled);\n\n return (\n <div\n style={{ marginLeft: isChildElement ? 16 : 4 + \"px\" }}\n className={isParentToggled ? \"tree-element\" : \"tree-element collapsed\"}\n >\n <span\n className={isToggled ? \"toggler\" : \"toggler closed\"}\n onClick={() => setIsToggled(!isToggled)}\n />\n {name ? <strong>&nbsp;&nbsp;{name}: </strong> : <span>&nbsp;&nbsp;</span>}\n {Array.isArray(data) ? \"[\" : \"{\"}\n {!isToggled && \"...\"}\n {Object.keys(data).map(\n (v, i, a) =>\n typeof data[v] == \"object\" ? (\n <TreeView\n data={data[v]}\n isLast={i === a.length - 1}\n name={Array.isArray(data) ? null : v}\n isChildElement\n isParentToggled={isParentToggled && isToggled}\n />\n ) : (\n <p\n style={{ marginLeft: 16 + \"px\" }}\n className={isToggled ? \"tree-element\" : \"tree-element collapsed\"}\n >\n {Array.isArray(data) ? \"\" : <strong>{v}: </strong>}\n {data[v]}\n {i === a.length - 1 ? \"\" : \",\"}\n </p>\n )\n )}\n {Array.isArray(data) ? \"]\" : \"}\"}\n {!isLast ? \",\" : \"\"}\n </div>\n );\n}\n```",
"```jsx\nlet data = {\n lorem: {\n ipsum: \"dolor sit\",\n amet: {\n consectetur: \"adipiscing\",\n elit: [\n \"duis\",\n \"vitae\",\n {\n semper: \"orci\"\n },\n {\n est: \"sed ornare\"\n },\n \"etiam\",\n [\"laoreet\", \"tincidunt\"],\n [\"vestibulum\", \"ante\"]\n ]\n },\n ipsum: \"primis\"\n }\n};\nReactDOM.render(<TreeView data={data} name='data'/>, document.getElementById(\"root\"));\n```"
],
"expertise": 2,
"tags": [
"object",
"visual",
"state",
"recursion"
],
"notes": []
}

292
package-lock.json generated
View File

@ -8,63 +8,72 @@
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dev": true,
"requires": {
"color-convert": "^1.9.0"
"color-convert": "1.9.3"
}
},
"argparse": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
"integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"dev": true,
"requires": {
"sprintf-js": "~1.0.2"
"sprintf-js": "1.0.3"
}
},
"builtin-modules": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
"integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8="
"integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
"dev": true
},
"caller-callsite": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
"integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
"dev": true,
"requires": {
"callsites": "^2.0.0"
"callsites": "2.0.0"
}
},
"caller-path": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
"integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
"dev": true,
"requires": {
"caller-callsite": "^2.0.0"
"caller-callsite": "2.0.0"
}
},
"callsites": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
"integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA="
"integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=",
"dev": true
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dev": true,
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
"ansi-styles": "3.2.1",
"escape-string-regexp": "1.0.5",
"supports-color": "5.5.0"
}
},
"ci-info": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
"integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="
"integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
"dev": true
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dev": true,
"requires": {
"color-name": "1.1.3"
}
@ -72,450 +81,513 @@
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
"dev": true
},
"cosmiconfig": {
"version": "5.0.7",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.0.7.tgz",
"integrity": "sha512-PcLqxTKiDmNT6pSpy4N6KtuPwb53W+2tzNvwOZw0WH9N6O0vLIBq0x8aj8Oj75ere4YcGi48bDFCL+3fRJdlNA==",
"dev": true,
"requires": {
"import-fresh": "^2.0.0",
"is-directory": "^0.3.1",
"js-yaml": "^3.9.0",
"parse-json": "^4.0.0"
"import-fresh": "2.0.0",
"is-directory": "0.3.1",
"js-yaml": "3.12.1",
"parse-json": "4.0.0"
}
},
"cross-spawn": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
"integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
"dev": true,
"requires": {
"nice-try": "^1.0.4",
"path-key": "^2.0.1",
"semver": "^5.5.0",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
"nice-try": "1.0.5",
"path-key": "2.0.1",
"semver": "5.6.0",
"shebang-command": "1.2.0",
"which": "1.3.1"
}
},
"end-of-stream": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
"integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
"dev": true,
"requires": {
"once": "^1.4.0"
"once": "1.4.0"
}
},
"error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
"dev": true,
"requires": {
"is-arrayish": "^0.2.1"
"is-arrayish": "0.2.1"
}
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
"dev": true
},
"execa": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
"integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
"dev": true,
"requires": {
"cross-spawn": "^6.0.0",
"get-stream": "^4.0.0",
"is-stream": "^1.1.0",
"npm-run-path": "^2.0.0",
"p-finally": "^1.0.0",
"signal-exit": "^3.0.0",
"strip-eof": "^1.0.0"
"cross-spawn": "6.0.5",
"get-stream": "4.1.0",
"is-stream": "1.1.0",
"npm-run-path": "2.0.2",
"p-finally": "1.0.0",
"signal-exit": "3.0.2",
"strip-eof": "1.0.0"
}
},
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"dev": true,
"requires": {
"locate-path": "^3.0.0"
"locate-path": "3.0.0"
}
},
"fs-extra": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
"graceful-fs": "4.1.15",
"jsonfile": "4.0.0",
"universalify": "0.1.2"
}
},
"get-stdin": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz",
"integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g=="
"integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==",
"dev": true
},
"get-stream": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
"integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
"dev": true,
"requires": {
"pump": "^3.0.0"
"pump": "3.0.0"
}
},
"graceful-fs": {
"version": "4.1.15",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
"integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA=="
"integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
"dev": true
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"hosted-git-info": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz",
"integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w=="
"integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==",
"dev": true
},
"husky": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/husky/-/husky-1.3.1.tgz",
"integrity": "sha512-86U6sVVVf4b5NYSZ0yvv88dRgBSSXXmHaiq5pP4KDj5JVzdwKgBjEtUPOm8hcoytezFwbU+7gotXNhpHdystlg==",
"dev": true,
"requires": {
"cosmiconfig": "^5.0.7",
"execa": "^1.0.0",
"find-up": "^3.0.0",
"get-stdin": "^6.0.0",
"is-ci": "^2.0.0",
"pkg-dir": "^3.0.0",
"please-upgrade-node": "^3.1.1",
"read-pkg": "^4.0.1",
"run-node": "^1.0.0",
"slash": "^2.0.0"
"cosmiconfig": "5.0.7",
"execa": "1.0.0",
"find-up": "3.0.0",
"get-stdin": "6.0.0",
"is-ci": "2.0.0",
"pkg-dir": "3.0.0",
"please-upgrade-node": "3.1.1",
"read-pkg": "4.0.1",
"run-node": "1.0.0",
"slash": "2.0.0"
}
},
"import-fresh": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
"integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
"dev": true,
"requires": {
"caller-path": "^2.0.0",
"resolve-from": "^3.0.0"
"caller-path": "2.0.0",
"resolve-from": "3.0.0"
}
},
"is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
"dev": true
},
"is-builtin-module": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
"integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
"dev": true,
"requires": {
"builtin-modules": "^1.0.0"
"builtin-modules": "1.1.1"
}
},
"is-ci": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
"integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
"dev": true,
"requires": {
"ci-info": "^2.0.0"
"ci-info": "2.0.0"
}
},
"is-directory": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
"integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE="
"integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
"dev": true
},
"is-stream": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
"dev": true
},
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
"dev": true
},
"js-yaml": {
"version": "3.12.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz",
"integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==",
"dev": true,
"requires": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
"argparse": "1.0.10",
"esprima": "4.0.1"
}
},
"json-parse-better-errors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw=="
"integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
"dev": true
},
"jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
"dev": true,
"requires": {
"graceful-fs": "^4.1.6"
"graceful-fs": "4.1.15"
}
},
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"dev": true,
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
"p-locate": "3.0.0",
"path-exists": "3.0.0"
}
},
"markdown-builder": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/markdown-builder/-/markdown-builder-0.9.0.tgz",
"integrity": "sha512-UovCyEEzMeKE7l88fbOk9SIJkOG7KXkg+TdudN8rvOtCtBO5uu1X27HSnM7LS/xH+vaShJLGpkBcYYcojWNx/g==",
"dev": true,
"requires": {
"husky": "^1.0.0-rc.14"
"husky": "1.3.1"
}
},
"nice-try": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
"dev": true
},
"normalize-package-data": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.2.tgz",
"integrity": "sha512-YcMnjqeoUckXTPKZSAsPjUPLxH85XotbpqK3w4RyCwdFQSU5FxxBys8buehkSfg0j9fKvV1hn7O0+8reEgkAiw==",
"dev": true,
"requires": {
"hosted-git-info": "^2.1.4",
"is-builtin-module": "^1.0.0",
"semver": "2 || 3 || 4 || 5",
"validate-npm-package-license": "^3.0.1"
"hosted-git-info": "2.7.1",
"is-builtin-module": "1.0.0",
"semver": "5.6.0",
"validate-npm-package-license": "3.0.4"
}
},
"npm-run-path": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
"integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
"dev": true,
"requires": {
"path-key": "^2.0.0"
"path-key": "2.0.1"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dev": true,
"requires": {
"wrappy": "1"
"wrappy": "1.0.2"
}
},
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
"dev": true
},
"p-limit": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz",
"integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==",
"dev": true,
"requires": {
"p-try": "^2.0.0"
"p-try": "2.0.0"
}
},
"p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"dev": true,
"requires": {
"p-limit": "^2.0.0"
"p-limit": "2.1.0"
}
},
"p-try": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz",
"integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ=="
"integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==",
"dev": true
},
"parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
"dev": true,
"requires": {
"error-ex": "^1.3.1",
"json-parse-better-errors": "^1.0.1"
"error-ex": "1.3.2",
"json-parse-better-errors": "1.0.2"
}
},
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
"dev": true
},
"path-key": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
"integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
"integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
"dev": true
},
"pify": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
"dev": true
},
"pkg-dir": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
"integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
"dev": true,
"requires": {
"find-up": "^3.0.0"
"find-up": "3.0.0"
}
},
"please-upgrade-node": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz",
"integrity": "sha512-KY1uHnQ2NlQHqIJQpnh/i54rKkuxCEBx+voJIS/Mvb+L2iYd2NMotwduhKTMjfC1uKoX3VXOxLjIYG66dfJTVQ==",
"dev": true,
"requires": {
"semver-compare": "^1.0.0"
"semver-compare": "1.0.0"
}
},
"pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
"integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
"dev": true,
"requires": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
"end-of-stream": "1.4.1",
"once": "1.4.0"
}
},
"read-pkg": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-4.0.1.tgz",
"integrity": "sha1-ljYlN48+HE1IyFhytabsfV0JMjc=",
"dev": true,
"requires": {
"normalize-package-data": "^2.3.2",
"parse-json": "^4.0.0",
"pify": "^3.0.0"
"normalize-package-data": "2.4.2",
"parse-json": "4.0.0",
"pify": "3.0.0"
}
},
"resolve-from": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
"dev": true
},
"run-node": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/run-node/-/run-node-1.0.0.tgz",
"integrity": "sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A=="
"integrity": "sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A==",
"dev": true
},
"semver": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
"integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg=="
"integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
"dev": true
},
"semver-compare": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
"integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w="
"integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=",
"dev": true
},
"shebang-command": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
"integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
"dev": true,
"requires": {
"shebang-regex": "^1.0.0"
"shebang-regex": "1.0.0"
}
},
"shebang-regex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM="
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
"dev": true
},
"signal-exit": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
"integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
"dev": true
},
"slash": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz",
"integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A=="
"integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==",
"dev": true
},
"spdx-correct": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz",
"integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==",
"dev": true,
"requires": {
"spdx-expression-parse": "^3.0.0",
"spdx-license-ids": "^3.0.0"
"spdx-expression-parse": "3.0.0",
"spdx-license-ids": "3.0.3"
}
},
"spdx-exceptions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
"integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA=="
"integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
"dev": true
},
"spdx-expression-parse": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
"integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
"dev": true,
"requires": {
"spdx-exceptions": "^2.1.0",
"spdx-license-ids": "^3.0.0"
"spdx-exceptions": "2.2.0",
"spdx-license-ids": "3.0.3"
}
},
"spdx-license-ids": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.3.tgz",
"integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g=="
"integrity": "sha512-uBIcIl3Ih6Phe3XHK1NqboJLdGfwr1UN3k6wSD1dZpmPsIkb8AGNbZYJ1fOBk834+Gxy8rpfDxrS6XLEMZMY2g==",
"dev": true
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
"dev": true
},
"strip-eof": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
"integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="
"integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
"dev": true
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
"has-flag": "3.0.0"
}
},
"universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true
},
"validate-npm-package-license": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
"integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
"dev": true,
"requires": {
"spdx-correct": "^3.0.0",
"spdx-expression-parse": "^3.0.0"
"spdx-correct": "3.1.0",
"spdx-expression-parse": "3.0.0"
}
},
"which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
"dev": true,
"requires": {
"isexe": "^2.0.0"
"isexe": "2.0.0"
}
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
}
}
}

View File

@ -21,22 +21,6 @@ attempt("snippet_data.json generation", () => {
.split("\n")
.map(v => v.replace(/[*-] /g, ""))
.filter(v => v.trim() !== "")
// const answer = getSection("#### Answer", contents)
// const goodToHear = getSection("#### Good to hear", contents, false)
// .split("\n")
// .map(v => v.replace(/[*-] /g, ""))
// .filter(v => v.trim() !== "")
// const links = getSection("##### Additional links", contents)
// .split("\n")
// .filter(v =>
// /(\/\*[\w\'\s\r\n\*]*\*\/)|(\/\/[\w\s\']*)|(\<![\-\-\s\w\>\/]*\>)/.test(
// v
// )
// )
// .map(v => v.replace(/[*-] /g, ""))
// .filter(v => v.trim() !== "" && !v.includes("tags"))
return {
name,

View File

@ -6,21 +6,16 @@ Explain briefly how the snippet works.
```jsx
function ComponentName(props) {
const [state, setState] = React.useState(null);
React.useEffect(() => {
setState(0);
});
return <div>{props}</div>;
};
```
<!-- OR -->
```jsx
class ComponentName extends React.Component {
constructor(props){}
render(){
return <div>{props}</div>;
}
}
```
```jsx
ReactDOM.render(<ComponentName />, mountNode);
ReactDOM.render(<ComponentName />, document.getElementById("root"));
```
<!-- OPTIONAL -->

View File

@ -33,6 +33,6 @@ ReactDOM.render(
);
```
<!-- tags: string,fragment,functional -->
<!-- tags: string,fragment,regexp -->
<!-- expertise: 2 -->

View File

@ -2,24 +2,17 @@
Renders a carousel component.
Initially set `state.active` to `0` (index of the first item).
Use the `React.setState()` hook to create the `active` state variable and give it a value of `0` (index of the first item).
Use an object, `style`, to hold the styles for the individual components.
Define a method, `setActiveItem`, which uses `this.setState` to change the state's `active` property to the index of the next item.
Define another method, `changeItem`, which is called by `setActiveItem` after updating the state each time and also when the component
first renders (on `ComponentDidMount`).
In the `render()` method, destructure `state`, `style` and `props`, compute if visibility style should be set to `visible` or not for each carousel item while mapping over and applying the combined style to the carousel item component accordingly.
Render the carousel items using [React.cloneElement](https://reactjs.org/docs/react-api.html#cloneelement) and pass down rest
`props` along with the computed styles.
Use the `React.setEffect()` hook to update the value of `active` to the index of the next item, using `setTimeout`.
Destructure `props`, compute if visibility style should be set to `visible` or not for each carousel item while mapping over and applying the combined style to the carousel item component accordingly.
Render the carousel items using `React.cloneElement()` and pass down rest `props` along with the computed styles.
```jsx
class Carousel extends React.Component {
constructor(props) {
super(props);
this.state = {
active: 0
};
this.scrollInterval = null;
this.style = {
function Carousel(props) {
const [active, setActive] = React.useState(0);
let scrollInterval = null;
const style = {
carousel: {
position: "relative"
},
@ -31,34 +24,21 @@ class Carousel extends React.Component {
visibility: "visible"
}
};
}
componentDidMount() {
this.changeItem();
}
setActiveItem = () => {
const { carouselItems } = this.props;
this.setState(
prevState => ({
active: (prevState.active + 1) % carouselItems.length
}),
this.changeItem
);
};
changeItem = () => {
this.scrollInterval = setTimeout(this.setActiveItem, 2000);
};
render() {
const { carouselItems, ...rest } = this.props;
const { active } = this.state;
const { visible, carousel, carouselItem } = this.style;
React.useEffect(() => {
scrollInterval = setTimeout(() => {
const { carouselItems } = props;
setActive((active + 1) % carouselItems.length);
}, 2000);
});
const { carouselItems, ...rest } = props;
return (
<div style={carousel}>
<div style={style.carousel}>
{carouselItems.map((item, index) => {
const activeStyle = active === index ? visible : {};
const activeStyle = active === index ? style.visible : {};
return React.cloneElement(item, {
...rest,
style: {
...carouselItem,
...style.carouselItem,
...activeStyle
}
});
@ -66,7 +46,6 @@ class Carousel extends React.Component {
</div>
);
}
}
```
```jsx
@ -82,7 +61,7 @@ ReactDOM.render(
);
```
<!-- tags: visual,children,state,class -->
<!-- tags: visual,children,state,effect -->
<!-- expertise: 2 -->

View File

@ -2,56 +2,47 @@
Renders a component with collapsible content.
Use the value of the `collapsed` prop to determine the initial state of the content (collapsed/expanded).
Set the `state` of the component to the value of the `collapsed` prop (cast to a boolean value) and bind the `toggleCollapse` method to the component's context.
Use the `React.setState()` hook to create the `isCollapsed` state variable with an initial value of `props.collapsed`.
Use an object, `style`, to hold the styles for individual components and their states.
Create a method, `toggleCollapse`, which uses `Component.prototype.setState` to change the component's `state` from collapsed to expanded and vice versa.
In the `render()` method, use a `<div>` to wrap both the `<button>` that alters the component's `state` and the content of the component, passed down via `props.children`.
Determine the appearance of the content, based on `state.collapsed` and apply the appropriate CSS rules from the `style` object.
Finally, update the value of the `aria-expanded` attribute based on `state.collapsed` to make the component accessible.
Use a `<div>` to wrap both the `<button>` that alters the component's `isCollapsed` state and the content of the component, passed down via `props.children`.
Determine the appearance of the content, based on `isCollapsed` and apply the appropriate CSS rules from the `style` object.
Finally, update the value of the `aria-expanded` attribute based on `isCollapsed` to make the component accessible.
```jsx
class Collapse extends React.Component {
constructor(props) {
super(props);
this.state = {
collapsed: !!props.collapsed
};
this.style = {
function Collapse(props) {
const [isCollapsed, setIsCollapsed] = React.useState(props.collapsed);
const style = {
collapsed: {
display: 'none'
display: "none"
},
expanded: {
display: 'block'
display: "block"
},
buttonStyle: {
display: 'block',
width: '100%'
display: "block",
width: "100%"
}
};
this.toggleCollapse = this.toggleCollapse.bind(this);
}
toggleCollapse() {
this.setState(state => ({ collapsed: !state.collapsed }));
}
render() {
return (
<div>
<button style={this.style.buttonStyle} onClick={this.toggleCollapse}>
{this.state.collapsed ? 'Show' : 'Hide'} content
<button
style={style.buttonStyle}
onClick={() => setIsCollapsed(!isCollapsed)}
>
{isCollapsed ? "Show" : "Hide"} content
</button>
<div
style= {this.state.collapsed ? this.style.collapsed : this.style.expanded}
aria-expanded = {this.state.collapsed}
className="collapse-content"
style={isCollapsed ? style.collapsed : style.expanded}
aria-expanded={isCollapsed}
>
{this.props.children}
{props.children}
</div>
</div>
);
}
}
```
```jsx
@ -64,6 +55,6 @@ ReactDOM.render(
);
```
<!-- tags: visual,children,state,class -->
<!-- tags: visual,children,state -->
<!-- expertise: 2 -->

View File

@ -21,6 +21,6 @@ ReactDOM.render(<DataList data={names}/>, document.getElementById('root'));
ReactDOM.render(<DataList data={names} isOrdered/>, document.getElementById('root'));
```
<!-- tags: array,functional -->
<!-- tags: array -->
<!-- expertise: 0 -->

View File

@ -36,6 +36,6 @@ ReactDOM.render(
);
```
<!-- tags: array,functional -->
<!-- tags: array -->
<!-- expertise: 0 -->

View File

@ -3,12 +3,13 @@
Renders a file drag and drop component for a single file.
Create a ref called `dropRef` for this component.
Initialize `state.drag` and `state.filename` to `false` and `''` respectively.
The variables `dragCounter` and `state.drag` are used to determine if a file is being dragged, while `state.filename` is used to store the dropped file's name.
Use the `React.useState()` hook to create the `drag` and `filename` variables, initialized to `false` and `''` respectively.
The variables `dragCounter` and `drag` are used to determine if a file is being dragged, while `filename` is used to store the dropped file's name.
Create the `handleDrag`, `handleDragIn`, `handleDragOut` and `handleDrop` methods to handle drag and drop functionality, bind them to the component's context.
Each of the methods will handle a specific event, the listeners for which are created and removed in `componentDidMount` and `componentWillUnmount` respectively.
`handleDrag` prevents the browser from opening the dragged file, `handleDragIn` and `handleDragOut` handle the dragged file entering and exiting the component, while `handleDrop` handles the file being dropped and passes it to `this.props.handleDrop`.
In the `render()` method, create an appropriately styled `<div>` and use `state.drag` and `state.filename` to determine its contents and style.
Each of the methods will handle a specific event, the listeners for which are created and removed in the `React.useEffect()` hook and its attached `cleanup()` method.
`handleDrag` prevents the browser from opening the dragged file, `handleDragIn` and `handleDragOut` handle the dragged file entering and exiting the component, while `handleDrop` handles the file being dropped and passes it to `props.handleDrop`.
Return an appropriately styled `<div>` and use `drag` and `filename` to determine its contents and style.
Finally, bind the `ref` of the created `<div>` to `dropRef`.
@ -32,79 +33,67 @@ Finally, bind the `ref` of the created `<div>` to `dropRef`.
```
```jsx
class FileDrop extends React.Component {
constructor(props) {
super(props);
this.dropRef = React.createRef();
this.state = {
drag: false,
filename: ''
}
this.handleDrag = this.handleDrag.bind(this);
this.handleDragIn = this.handleDragIn.bind(this);
this.handleDragOut = this.handleDragOut.bind(this);
this.handleDrop = this.handleDrop.bind(this);
}
function FileDrop(props) {
const [drag, setDrag] = React.useState(false);
const [filename, setFilename] = React.useState('');
let dropRef = React.createRef();
let dragCounter = 0;
handleDrag(e) {
const handleDrag = e => {
e.preventDefault();
e.stopPropagation();
}
};
handleDragIn(e) {
const handleDragIn = e => {
e.preventDefault();
e.stopPropagation();
this.dragCounter++;
if (e.dataTransfer.items && e.dataTransfer.items.length > 0)
this.setState({ drag: true });
}
dragCounter++;
if (e.dataTransfer.items && e.dataTransfer.items.length > 0) setDrag(true);
};
handleDragOut(e) {
const handleDragOut = e => {
e.preventDefault();
e.stopPropagation();
this.dragCounter--;
if (this.dragCounter === 0)
this.setState({ drag: false });
}
dragCounter--;
if (dragCounter === 0) setDrag(false);
};
handleDrop(e) {
const handleDrop = e => {
e.preventDefault();
e.stopPropagation();
this.setState({ drag: false });
setDrag(false);
if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
this.props.handleDrop(e.dataTransfer.files[0]);
this.setState({ filename : e.dataTransfer.files[0].name});
props.handleDrop(e.dataTransfer.files[0]);
setFilename(e.dataTransfer.files[0].name);
e.dataTransfer.clearData();
this.dragCounter = 0;
}
dragCounter = 0;
}
};
componentDidMount() {
let div = this.dropRef.current;
div.addEventListener('dragenter', this.handleDragIn);
div.addEventListener('dragleave', this.handleDragOut);
div.addEventListener('dragover', this.handleDrag);
div.addEventListener('drop', this.handleDrop);
}
React.useEffect(() => {
let div = dropRef.current;
div.addEventListener("dragenter", handleDragIn);
div.addEventListener("dragleave", handleDragOut);
div.addEventListener("dragover", handleDrag);
div.addEventListener("drop", handleDrop);
return function cleanup() {
div.removeEventListener("dragenter", handleDragIn);
div.removeEventListener("dragleave", handleDragOut);
div.removeEventListener("dragover", handleDrag);
div.removeEventListener("drop", handleDrop);
};
});
componentWillUnmount() {
let div = this.dropRef.current;
div.removeEventListener('dragenter', this.handleDragIn);
div.removeEventListener('dragleave', this.handleDragOut);
div.removeEventListener('dragover', this.handleDrag);
div.removeEventListener('drop', this.handleDrop);
}
render() {
return (
<div ref={this.dropRef} className={this.state.drag ? 'filedrop drag' : this.state.filename ? 'filedrop ready' : 'filedrop'}>
{this.state.filename && !this.state.drag ?
<div>{this.state.filename}</div>
: <div>Drop files here!</div>
<div
ref={dropRef}
className={
drag ? "filedrop drag" : filename ? "filedrop ready" : "filedrop"
}
>
{filename && !drag ? <div>{filename}</div> : <div>Drop files here!</div>}
</div>
)
}
);
}
```
@ -112,6 +101,6 @@ class FileDrop extends React.Component {
ReactDOM.render(<FileDrop handleDrop={console.log}/>, document.getElementById('root'));
```
<!-- tags: visual,input,state,class -->
<!-- tags: visual,input,state,effect -->
<!-- expertise: 2 -->

View File

@ -26,6 +26,6 @@ ReactDOM.render(
);
```
<!-- tags: input,functional -->
<!-- tags: input -->
<!-- expertise: 0 -->

View File

@ -2,42 +2,37 @@
Renders a textarea component with a character limit.
Use the value of the `value` prop to determine the initial `state.content` and `state.characterCount` and the value of the `limit` props to determine the value of `state.limit`.
Create a method, `handleChange`, which trims the `event.target.value` data if necessary and uses `Component.prototype.setState` to update `state.content` and `state.characterCount`, and bind it to the component's context.
In the`render()` method, use a`<div>` to wrap both the`<textarea>` and the `<p>` element that displays the character count and bind the `onChange` event of the `<textarea>` to the `handleChange` method.
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`<div>` to wrap both the`<textarea>` and the `<p>` element that displays the character count and bind the `onChange` event of the `<textarea>` to call `setFormattedContent` with the value of `event.target.value`.
```jsx
class LimitedTextarea extends React.Component {
constructor(props) {
super(props);
this.state = {
content: props.value,
characterCount: props.value.length,
limit: props.limit
};
this.handleChange = this.handleChange.bind(this);
}
function LimitedTextarea({ rows, cols, value, limit }) {
const [content, setContent] = React.useState(value);
const setFormattedContent = text => {
text.length > limit ? setContent(text.slice(0, limit)) : setContent(text);
};
React.useEffect(() => {
setFormattedContent(content);
}, []);
handleChange(event) {
let newContent = event.target.value;
if(newContent.length >= this.state.limit) newContent = newContent.slice(0, this.state.limit);
this.setState(state => ({ content: newContent, characterCount: newContent.length }));
}
render() {
return (
<div>
<textarea
rows={this.props.rows}
cols={this.props.cols}
onChange={this.handleChange}
value={this.state.content}
>
</textarea>
<p>{this.state.characterCount}/{this.props.limit}</p>
rows={rows}
cols={cols}
onChange={event => setFormattedContent(event.target.value)}
value={content}
/>
<p>
{content.length}/{limit}
</p>
</div>
);
}
}
```
```jsx
@ -46,8 +41,7 @@ ReactDOM.render(
document.getElementById('root')
);
```
<!-- tags: input,state,class -->
<!-- tags: input,state,effect -->
<!-- expertise: 0 -->

View File

@ -0,0 +1,61 @@
### LimitedWordTextarea
Renders a textarea component with a word limit.
Use the `React.useState()` hook to create the `content` and `wordCount` state variables and set their values to `value` and `0` respectively.
Create a method `setFormattedContent`, which uses `String.prototype.split(' ')` to turn the input into an array of words and check if the result of applying `Array.prototype.filter(Boolean)` has a `length` longer than `limit`.
If the afforementioned `length` exceeds the `limit`, trim the input, otherwise return the raw input, updating `content` and `wordCount` accordingly in both cases.
Use the `React.useEffect()` hook to call the `setFormattedContent` method on the value of the `content` state variable.
Use a`<div>` to wrap both the`<textarea>` and the `<p>` element that displays the character count and bind the `onChange` event of the `<textarea>` to call `setFormattedContent` with the value of `event.target.value`.
```jsx
function LimitedWordTextarea({ rows, cols, value, limit }) {
const [content, setContent] = React.useState(value);
const [wordCount, setWordCount] = React.useState(0);
const setFormattedContent = text => {
let words = text.split(" ");
if (words.filter(Boolean).length > limit) {
setContent(
text
.split(" ")
.slice(0, limit)
.join(" ")
);
setWordCount(limit);
} else {
setContent(text);
setWordCount(words.filter(Boolean).length);
}
};
React.useEffect(() => {
setFormattedContent(content);
}, []);
return (
<div>
<textarea
rows={rows}
cols={cols}
onChange={event => setFormattedContent(event.target.value)}
value={content}
/>
<p>
{wordCount}/{limit}
</p>
</div>
);
}
```
```jsx
ReactDOM.render(
<LimitedWordTextArea limit={5} value='Hello there!' />,
document.getElementById('root')
);
```
<!-- tags: input,state,effect -->
<!-- expertise: 0 -->

View File

@ -24,6 +24,6 @@ ReactDOM.render(
);
```
<!-- tags: visual,functional -->
<!-- tags: visual -->
<!-- expertise: 0 -->

View File

@ -46,6 +46,6 @@ ReactDOM.render(
#### Notes:
* This component does not work with nested objects and will break if there are nested objects inside any of the properties specified in `propertyNames`.
<!-- tags: array,object,functional -->
<!-- tags: array,object -->
<!-- expertise: 1 -->

View File

@ -2,44 +2,31 @@
Renders a password input field with a reveal button.
Initially set `state.shown` to `false` to ensure that the password is not shown by default.
Create a method, `toggleShown`, which uses `Component.prototype.setState` to change the input's state from shown to hidden and vice versa, bind it to the component's context.
In the`render()` method, use a`<div>` to wrap both the`<input>` and the `<button>` element that toggles the type of the input field.
Finally, bind the `<button>`'s `onClick` event to the `toggleShown` method.
Use the `React.useState()` hook to create the `shown` state variable and set its value to `false`.
Use a`<div>` to wrap both the`<input>` and the `<button>` element that toggles the type of the input field between `"text"` and `"password"`.
```jsx
class PasswordRevealer extends React.Component {
constructor(props) {
super(props);
this.state = {
shown: false
};
this.toggleShown = this.toggleShown.bind(this);
}
function PasswordRevealer({ value }) {
const [shown, setShown] = React.useState(false);
toggleShown() {
this.setState(state => ({ shown: !state.shown }));
}
render() {
return (
<div>
<input
type={this.state.shown ? 'text' : 'password'}
value={this.props.value}
type={shown ? "text" : "password"}
value={value}
onChange={() => {}}
/>
<button onClick={this.toggleShown}>Show/Hide</button>
<button onClick={() => setShown(!shown)}>Show/Hide</button>
</div>
);
}
}
```
```jsx
ReactDOM.render(<PasswordRevealer />, document.getElementById('root'));
```
<!--tags: input,state,class -->
<!--tags: input,state -->
<!--expertise: 0 -->

View File

@ -33,6 +33,6 @@ ReactDOM.render(
);
```
<!-- tags: input,functional -->
<!-- tags: input -->
<!-- expertise: 0 -->

View File

@ -2,68 +2,50 @@
Renders a star rating component.
Use and IIFE to define a functional component, called `Star` that will render each individual star with the appropriate appearance, based on the parent component's `state` and return the class component `StarRating`.
Use the value of the `rating` prop to determine if a valid rating is supplied and store it in `state.rating` (or `0` if invalid or not supplied).
Initialize `state.selection` to `0`.
Create two methods, `hoverOver` and `setRating`, that take an event as argument and update `state.selected` and `state.rating` according to it, bind them both to the component's context.
In the `render()` method, create a `<div>` to wrap the `<Star>` components, which are created using `Array.prototype.map` on an array of 5 elements, created using `Array.from`, and handle the `onMouseLeave` event to set `state.selection` to `0`, the `onClick` event to set
the `state.rating` and the `onMouseOver` event to set `state.selection` to the `star-id` attribute of the `event.target` respectively.
Define a component, called `Star` that will render each individual star with the appropriate appearance, based on the parent component's state.
In the `StarRating` component, use the `React.setState()` hook to define the `rating` and `selection` state variables with the initial values of `props.rating` (or `0` if invalid or not supplied) and `0`.
Create a method, `hoverOver`, that updates `selected` and `rating` according to the provided `event`.
Create a `<div>` to wrap the `<Star>` components, which are created using `Array.prototype.map` on an array of 5 elements, created using `Array.from`, and handle the `onMouseLeave` event to set `selection` to `0`, the `onClick` event to set the `rating` and the `onMouseOver` event to set `selection` to the `star-id` attribute of the `event.target` respectively.
Finally, pass the appropriate values to each `<Star>` component (`starId` and `marked`).
```jsx
const StarRating = (function() {
function Star({ marked, starId }) {
return (
<span star-id={starId} style={{ color: '#ff9933' }} role='button'>
{marked ? '\u2605' : '\u2606'}
<span star-id={starId} style={{ color: "#ff9933" }} role="button">
{marked ? "\u2605" : "\u2606"}
</span>
);
}
return class StarRating extends React.Component {
constructor(props) {
super(props);
this.state = {
rating: typeof props.rating == 'number' ? props.rating : 0,
selection: 0
};
this.hoverOver = this.hoverOver.bind(this);
this.hoverOut = this.hoverOver.bind(this, null);
this.handleClick = this.handleClick.bind(this);
}
hoverOver(event) {
function StarRating(props) {
const [rating, setRating] = React.useState(
typeof props.rating == "number" ? props.rating : 0
);
const [selection, setSelection] = React.useState(0);
const hoverOver = event => {
let val = 0;
if (event && event.target && event.target.getAttribute('star-id'))
val = event.target.getAttribute('star-id');
this.setState(state => ({ selection: val }));
}
handleClick(event) {
const val = event.target.getAttribute('star-id') || this.state.rating;
this.setState(state => ({ rating: val }));
}
render() {
if (event && event.target && event.target.getAttribute("star-id"))
val = event.target.getAttribute("star-id");
setSelection(val);
};
return (
<div
onMouseOut={this.hoverOut}
onClick={this.handleClick}
onMouseOver={this.hoverOver}
onMouseOut={() => hoverOver(null)}
onClick={() =>
setRating(event.target.getAttribute("star-id") || this.state.rating)
}
onMouseOver={hoverOver}
>
{Array.from({ length: 5 }, (v, i) => (
<Star
starId={i + 1}
key={`star_${i + 1} `}
marked={
this.state.selection
? this.state.selection >= i+1
: this.state.rating >= i+1
}
marked={selection ? selection >= i + 1 : rating >= i + 1}
/>
))}
</div>
);
}
};
})();
```
```jsx
@ -71,6 +53,6 @@ ReactDOM.render(<StarRating/>, document.getElementById('root'));
ReactDOM.render(<StarRating rating={2} />, document.getElementById('root'));
```
<!-- tags: visual,children,input,state,class -->
<!-- tags: visual,children,input,state -->
<!-- expertise: 2 -->

View File

@ -1,10 +1,11 @@
### Tab
Renders a tabbed menu and view component.
Define `TabItem` as a middleware, pass it to the `Tab` and remove unnecessary nodes expect for `TabItem` by identifying the function's name in `props.children`.
Define a `TabItem` component, pass it to the `Tab` and remove unnecessary nodes expect for `TabItem` by identifying the function's name in `props.children`.
Use the `React.useState()` hook to initialize the value of the `bindIndex` state variable to `props.defaultIndex`.
Use `Array.prototype.map` on the collected nodes to render the `tab-menu` and `tab-view`.
Define `changeTab`, which will be executed when clicking a `<button>` from the `tab-menu`.
`changeTab` executes the passed callback, `onTabClick` and updates `state.bindIndex`, which in turn causes a re-render, evaluating the `style` and `className` of the `tab-view` items and `tab-menu` buttons according to their `index`.
`changeTab` executes the passed callback, `onTabClick` and updates `bindIndex`, which in turn causes a re-render, evaluating the `style` and `className` of the `tab-view` items and `tab-menu` buttons according to their `index`.
```css
.tab-menu > button {
@ -23,39 +24,25 @@ Define `changeTab`, which will be executed when clicking a `<button>` from the `
```
```jsx
class Tab extends React.Component {
constructor(props) {
super(props);
this.state = {
bindIndex: props.defaultIndex
function TabItem(props) {
return <div {...props} />;
}
function Tabs(props) {
const [bindIndex, setBindIndex] = React.useState(props.defaultIndex);
const changeTab = newIndex => {
if (typeof props.onTabClick === "function") props.onTabClick(newIndex);
setBindIndex(newIndex);
};
}
changeTab(newIndex) {
if (typeof this.props.onTabClick === "function")
this.props.onTabClick(newIndex);
this.setState({
bindIndex: newIndex
});
}
buttonClass(index) {
return this.state.bindIndex === index ? "focus" : "";
}
itemStyle(index) {
return {
display: this.state.bindIndex === index ? "block" : "none"
};
}
render() {
const items = this.props.children.filter(
item => item.type.name === "TabItem"
);
const items = props.children.filter(item => item.type.name === "TabItem");
return (
<div className="wrapper">
<div className="tab-menu">
{items.map(({ props: { index, label } }) => (
<button
onClick={() => this.changeTab(index)}
className={this.buttonClass(index)}
onClick={() => changeTab(index)}
className={bindIndex === index ? "focus" : ""}
>
{label}
</button>
@ -67,33 +54,29 @@ class Tab extends React.Component {
{...props}
className="tab-view_item"
key={props.index}
style={this.itemStyle(props.index)}
style={{ display: bindIndex === props.index ? "block" : "none" }}
/>
))}
</div>
</div>
);
}
}
function TabItem(props) {
return <div {...props} />;
}
```
```jsx
ReactDOM.render(
<Tab defaultIndex="1" onTabClick={console.log}>
<Tabs defaultIndex="1" onTabClick={console.log}>
<TabItem label="A" index="1">
Lorem ipsum
</TabItem>
<TabItem label="B" index="2">
Dolor sit amet
</TabItem>
</Tab>,
</Tabs>,
document.getElementById("root")
);
```
<!-- tags: visual,children,class -->
<!-- tags: visual,state,children -->
<!-- expertise: 1 -->

View File

@ -27,6 +27,6 @@ ReactDOM.render(
);
```
<!-- tags: input,functional -->
<!-- tags: input -->
<!-- expertise: 0 -->

View File

@ -2,37 +2,30 @@
Renders a ticker component.
- The ticker state is initially set to zero
- When the `Tick!` button is clicked, `timer` is incremented periodically at the given `interval`
- When the `Reset` button is clicked, the value of the timer is set to zero and the `setInterval` is cleared
- The `setInterval` is cleared once the desired `time` is reached
- `time` and `interval` are the required props
Use the `React.useState()` hook to initialize the `ticker` state variable to `0`.
Define two methods, `tick` and `reset`, that will periodically increment `timer` based on `interval` and reset `interval` respectively.
Return a `<div>` with two `<button>` elements, each of which calls `tick` and `reset` respectively.
```jsx
class Ticker extends Component {
constructor(props) {
super(props);
this.state = {ticker: 0}
this.interval = null
function Ticker(props) {
const [ticker, setTicker] = React.useState(0);
let interval = null;
const tick = () => {
reset();
interval = setInterval(() => {
if (ticker < props.times)
setTicker(ticker + 1);
else
clearInterval(interval);
}, props.interval);
}
tick = () => {
this.reset()
this.interval = setInterval(() => {
if (this.state.ticker < this.props.times) {
this.setState(({ ticker }) => ({ticker: ticker + 1}))
}else{
clearInterval(this.interval)
}
}, this.props.interval)
const reset = () => {
setTicker(0);
clearInterval(interval);
}
reset = () => {
this.setState({ticker: 0})
clearInterval(this.interval)
}
render() {
return (
<div>
<span style={{ fontSize: 100 }}>{this.state.ticker}</span>
@ -41,13 +34,12 @@ class Ticker extends Component {
</div>
);
}
}
```
```jsx
ReactDOM.render(<Ticker times={5} interval={1000} />, document.getElementById('root'));
```
<!-- tags: visual,state,class -->
<!-- tags: visual,state -->
<!-- expertise: 1 -->

View File

@ -2,56 +2,37 @@
Renders a toggle component.
Initialize `state.isToggleOn` to `false`, bind the `handleClick` method to the component's context.
Use the `React.useState()` to initialize the `isToggleOn` state variable to `false`.
Use an object, `style`, to hold the styles for individual components and their states.
Create a method, `handleClick`, which uses `Component.prototype.setState` to change the component's `state.toggleOn`.
In the `render()` method, destructure `state` and `style`, create a `<button>` that alters the component's `state` and determine the appearance of the content based on `state.isToggleOn`, applying the appropriate CSS rules from the `style` object.
Return a `<button>` that alters the component's `isToggledOn` when its `onClick` event is fired and determine the appearance of the content based on `isToggleOn`, applying the appropriate CSS rules from the `style` object.
```jsx
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {
isToggleOn: false
};
this.style = {
function Toggle(props) {
const [isToggleOn, setIsToggleOn] = React.useState(false);
style = {
on: {
backgroundColor: 'green'
backgroundColor: "green"
},
off: {
backgroundColor: 'grey'
backgroundColor: "grey"
}
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
const { isToggleOn } = this.state;
const { on, off } = this.style;
return (
<button
onClick={this.handleClick}
style={isToggleOn ? on : off}
onClick={() => setIsToggleOn(!isToggleOn)}
style={isToggleOn ? style.on : style.off}
>
{isToggleOn ? 'ON' : 'OFF'}
{isToggleOn ? "ON" : "OFF"}
</button>
);
}
}
```
```jsx
ReactDOM.render(<Toggle />, document.getElementById('root'));
```
<!-- tags: visual,state,class -->
<!-- tags: visual,state -->
<!-- expertise: 0 -->

View File

@ -2,68 +2,48 @@
Renders a tooltip component.
Set the `state` of the component to `show: false` initially, define an object, `style`, to hold the styles for individual components and their states.
Create a method, `toggleTooltip`, which uses `this.setState` to change the state's `show` property from `true` to `false` and vice versa.
Bind `showTooltip` and `hideTooltip` to the component's context with the respective values of `true` and `false`.
In the `render()` method, compute if the tooltip should be shown or hidden, render the content of the tooltip and bind the `onMouseEnter` and `onMouseLeave` events to `showTooltip` and `hideTooltip` respectively.
Use the `React.useState()` hook to create the `show` variable and initialize it to `false`.
Return a `<div>` element that contains the `<div>` that will be the tooltip and the `children` passed to the component.
Handle the `onMouseEnter` and `onMouseLeave` methods, by altering the value of the `show` variable.
```jsx
class Tooltip extends React.Component {
constructor(props) {
super(props);
this.state = {
show: false
};
this.style = {
tooltip: {
position: 'relative',
backgroundColor: "rgba(0,0,0,0.7)",
color: "white",
visibility: "hidden",
width: "fit-content",
padding: 5,
borderRadius: 5
},
tooltipArrow: {
position: 'absolute',
top: '100%',
left: '50%',
borderWidth: 5,
borderStyle: 'solid',
borderColor: "rgba(0,0,0,0.7) transparent transparent",
},
visible: {
visibility: "visible"
},
};
this.showTooltip = this.toggleTooltip.bind(this, true);
this.hideTooltip = this.toggleTooltip.bind(this, false);
```css
.tooltip {
position: relative;
background: rgba(0, 0, 0, 0.7);
color: white;
visibility: hidden;
padding: 5px;
border-radius: 5px;
}
.tooltip-arrow {
position: absolute;
top: 100%;
left: 50%;
border-width: 5px;
border-style: solid;
border-color: rgba(0, 0, 0, 0.7) transparent transparent;
}
```
```jsx
function Tooltip({ children, text, ...rest }) {
const [show, setShow] = React.useState(false);
toggleTooltip = tooltipState => {
this.setState({
show: tooltipState
});
};
render() {
const { children, text, ...rest } = this.props;
const { show } = this.state;
const { visible, tooltip, tooltipArrow } = this.style;
const showTooltip = show ? visible : {};
return (
<div>
<div style={{ ...tooltip, ...showTooltip }}>
<div className="tooltip" style={show ? { visibility: "visible" } : {}}>
{text}
<span style={tooltipArrow}/>
<span className="tooltip-arrow" />
</div>
<div {...rest} onMouseEnter={this.showTooltip} onMouseLeave={this.hideTooltip}>
<div
{...rest}
onMouseEnter={() => setShow(true)}
onMouseLeave={() => setShow(false)}
>
{children}
</div>
</div>
);
}
}
```
```jsx
@ -75,6 +55,6 @@ class Tooltip extends React.Component {
);
```
<!-- tags: visual,state,children,class -->
<!-- tags: visual,state,children -->
<!-- expertise: 1 -->

128
snippets/TreeView.md Normal file
View File

@ -0,0 +1,128 @@
### TreeView
Renders a tree view of a JSON object or array with collapsible content.
Use object destructuring to set defaults for certain props.
Use the value of the `toggled` prop to determine the initial state of the content (collapsed/expanded).
Use the `React.setState()` hook to create the `isToggled` state variable and give it the value of the `toggled` prop initially.
Return a `<div>` to wrap the contents of the component and the `<span>` element, used to alter the component's `isToggled` state.
Determine the appearance of the component, based on `isParentToggled`, `isToggled`, `name` and `Array.isArray()` on `data`.
For each child in `data`, determine if it is an object or array and recursively render a sub-tree.
Otherwise, render a `<p>` element with the appropriate style.
```css
.tree-element {
margin: 0;
position: relative;
}
div.tree-element:before {
content: '';
position: absolute;
top: 24px;
left: 1px;
height: calc(100% - 48px);
border-left: 1px solid gray;
}
.toggler {
position: absolute;
top: 10px;
left: 0px;
width: 0;
height: 0;
border-top: 4px solid transparent;
border-bottom: 4px solid transparent;
border-left: 5px solid gray;
cursor: pointer;
}
.toggler.closed {
transform: rotate(90deg);
}
.collapsed {
display: none;
}
```
```jsx
function TreeView({
data,
toggled = true,
name = null,
isLast = true,
isChildElement = false,
isParentToggled = true
}) {
const [isToggled, setIsToggled] = React.useState(toggled);
return (
<div
style={{ marginLeft: isChildElement ? 16 : 4 + "px" }}
className={isParentToggled ? "tree-element" : "tree-element collapsed"}
>
<span
className={isToggled ? "toggler" : "toggler closed"}
onClick={() => setIsToggled(!isToggled)}
/>
{name ? <strong>&nbsp;&nbsp;{name}: </strong> : <span>&nbsp;&nbsp;</span>}
{Array.isArray(data) ? "[" : "{"}
{!isToggled && "..."}
{Object.keys(data).map(
(v, i, a) =>
typeof data[v] == "object" ? (
<TreeView
data={data[v]}
isLast={i === a.length - 1}
name={Array.isArray(data) ? null : v}
isChildElement
isParentToggled={isParentToggled && isToggled}
/>
) : (
<p
style={{ marginLeft: 16 + "px" }}
className={isToggled ? "tree-element" : "tree-element collapsed"}
>
{Array.isArray(data) ? "" : <strong>{v}: </strong>}
{data[v]}
{i === a.length - 1 ? "" : ","}
</p>
)
)}
{Array.isArray(data) ? "]" : "}"}
{!isLast ? "," : ""}
</div>
);
}
```
```jsx
let data = {
lorem: {
ipsum: "dolor sit",
amet: {
consectetur: "adipiscing",
elit: [
"duis",
"vitae",
{
semper: "orci"
},
{
est: "sed ornare"
},
"etiam",
["laoreet", "tincidunt"],
["vestibulum", "ante"]
]
},
ipsum: "primis"
}
};
ReactDOM.render(<TreeView data={data} name='data'/>, document.getElementById("root"));
```
<!-- tags: object,visual,state,recursion -->
<!-- expertise: 2 -->

View File

@ -4,6 +4,31 @@
> Curated collection of useful React snippets that you can understand in 30 seconds or less.
* Use <kbd>Ctrl</kbd> + <kbd>F</kbd> or <kbd>command</kbd> + <kbd>F</kbd> to search for a snippet.
* Contributions welcome, please read the [contribution guide](CONTRIBUTING.md).
* Snippets are written in React 16.8+, using hooks.
### Prerequisites
To import a snippet into your project, you must import `React` and copy-paste the component's JavaScript code like this:
```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 `<div>`) and that you have imported `ReactDOM`, like this:
```js
import ReactDOM from 'react-dom';
```
#### Related projects
* [30 Seconds of Code](https://30secondsofcode.org)