Curated collection of useful React snippets that you can understand in 30 seconds or less.
- Use Ctrl + F or command + F to search for a snippet.
- Contributions welcome, please read the contribution guide.
- Snippets are written in React 16.8+, using hooks.
To import a snippet into your project, you must import React and copy-paste the component's JavaScript code like this:
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:
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:
import ReactDOM from 'react-dom';View contents
View contents
View contents
View contents
Renders a list of elements from an array of primitives.
- Use the value of the
isOrderedprop to conditionally render a<ol>or<ul>list. - Use
Array.prototype.mapto render every item indataas a<li>element, give it akeyproduced from the concatenation of the its index and value. - Omit the
isOrderedprop to render a<ul>list by default.
function DataList({ isOrdered, data }) {
const list = data.map((val, i) => <li key={`${i}_${val}`}>{val}</li>);
return isOrdered ? <ol>{list}</ol> : <ul>{list}</ul>;
}Examples
const names = ['John', 'Paul', 'Mary'];
ReactDOM.render(<DataList data={names} />, document.getElementById('root'));
ReactDOM.render(<DataList data={names} isOrdered />, document.getElementById('root'));Renders a table with rows dynamically created from an array of primitives.
- Render a
<table>element with two columns (IDandValue). - Use
Array.prototype.mapto render every item indataas a<tr>element, consisting of its index and value, give it akeyproduced from the concatenation of the two.
function DataTable({ data }) {
return (
<table>
<thead>
<tr>
<th>ID</th>
<th>Value</th>
</tr>
</thead>
<tbody>
{data.map((val, i) => (
<tr key={`${i}_${val}`}>
<td>{i}</td>
<td>{val}</td>
</tr>
))}
</tbody>
</table>
);
}Examples
const people = ['John', 'Jesse'];
ReactDOM.render(<DataTable data={people} />, document.getElementById('root'));Renders a table with rows dynamically created from an array of objects and a list of property names.
- Use
Object.keys(),Array.prototype.filter(),Array.prototype.includes()andArray.prototype.reduce()to produce afilteredDataarray, containing all objects with the keys specified inpropertyNames. - Render a
<table>element with a set of columns equal to the amount of values inpropertyNames. - Use
Array.prototype.mapto render each value in thepropertyNamesarray as a<th>element. - Use
Array.prototype.mapto render each object in thefilteredDataarray as a<tr>element, containing a<td>for each key in the object.
This component does not work with nested objects and will break if there are nested objects inside any of the properties specified in propertyNames
function MappedTable({ data, propertyNames }) {
let filteredData = data.map(v =>
Object.keys(v)
.filter(k => propertyNames.includes(k))
.reduce((acc, key) => ((acc[key] = v[key]), acc), {})
);
return (
<table>
<thead>
<tr>
{propertyNames.map(val => (
<th key={`h_${val}`}>{val}</th>
))}
</tr>
</thead>
<tbody>
{filteredData.map((val, i) => (
<tr key={`i_${i}`}>
{propertyNames.map(p => (
<td key={`i_${i}_${p}`}>{val[p]}</td>
))}
</tr>
))}
</tbody>
</table>
);
}Examples
const people = [
{ name: 'John', surname: 'Smith', age: 42 },
{ name: 'Adam', surname: 'Smith', gender: 'male' }
];
const propertyNames = ['name', 'surname', 'age'];
ReactDOM.render(
<MappedTable data={people} propertyNames={propertyNames} />,
document.getElementById('root')
);A hook that handles the event of clicking inside the wrapped component.
- Create a custom hook that takes a
refand acallbackto handle theclickevent. - Use the
React.useEffect()hook to append and clean up theclickevent. - Use the
React.useRef()hook to create areffor your click component and pass it to theuseClickInsidehook.
const useClickInside = (ref, callback) => {
const handleClick = e => {
if (ref.current && ref.current.contains(e.target)) {
callback();
}
};
React.useEffect(() => {
document.addEventListener('click', handleClick);
return () => {
document.removeEventListener('click', handleClick);
};
});
};Examples
const ClickBox = ({ onClickInside }) => {
const clickRef = React.useRef();
useClickInside(clickRef, onClickInside);
return (
<div
className="click-box"
ref={clickRef}
style={{
border: '2px dashed orangered',
height: 200,
width: 400,
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
}}
>
<p>Click inside this element</p>
</div>
);
};
ReactDOM.render(
<ClickBox onClickInside={() => alert('click inside')} />,
document.getElementById('root')
);A hook that handles the event of clicking outside of the wrapped component.
- Create a custom hook that takes a
refand acallbackto handle theclickevent. - Use the
React.useEffect()hook to append and clean up theclickevent. - Use the
React.useRef()hook to create areffor your click component and pass it to theuseClickOutsidehook.
const useClickOutside = (ref, callback) => {
const handleClick = e => {
if (ref.current && !ref.current.contains(e.target)) {
callback();
}
};
React.useEffect(() => {
document.addEventListener('click', handleClick);
return () => {
document.removeEventListener('click', handleClick);
};
});
};Examples
const ClickBox = ({ onClickOutside }) => {
const clickRef = React.useRef();
useClickOutside(clickRef, onClickOutside);
return (
<div
className="click-box"
ref={clickRef}
style={{
border: '2px dashed orangered',
height: 200,
width: 400,
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
}}
>
<p>Click out of this element</p>
</div>
);
};
ReactDOM.render(
<ClickBox onClickOutside={() => alert('click outside')} />,
document.getElementById('root')
);A hook that implements fetch in a declarative manner.
- Create a custom hook that takes a
urlandoptions. - Use the
React.useState()hook to initialize theresponseanderrorstate variables. - Use the
React.useEffect()hook to anychronously callfetch()and update the state varaibles accordingly. - Return an object containting the
responseanderrorstate variables.
const useFetch = (url, options) => {
const [response, setResponse] = React.useState(null);
const [error, setError] = React.useState(null);
React.useEffect(() => {
const fetchData = async () => {
try {
const res = await fetch(url, options);
const json = await res.json();
setResponse(json);
} catch (error) {
setError(error);
}
};
fetchData();
}, []);
return { response, error };
};Examples
const ImageFetch = props => {
const res = useFetch('https://dog.ceo/api/breeds/image/random', {});
if (!res.response) {
return <div>Loading...</div>;
}
const dogName = res.response.status;
const imageUrl = res.response.message;
return (
<div>
<img src={imageUrl} alt="avatar" width={400} height="auto" />
</div>
);
};
ReactDOM.render(<ImageFetch />, document.getElementById('root'));A hook that implements setInterval in a declarative manner.
- Create a custom hook that takes a
callbackand adelay. - Use the
React.useRef()hook to create areffor the callback function. - Use the
React.useEffect()hook to remember the latest callback. - Use the
React.useEffect()hook to set up the interval and clean up.
const useInterval = (callback, delay) => {
const savedCallback = React.useRef();
React.useEffect(() => {
savedCallback.current = callback;
}, [callback]);
React.useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setInterval(tick, delay);
return () => clearInterval(id);
}
}, [delay]);
};Examples
const Timer = props => {
const [seconds, setSeconds] = React.useState(0);
useInterval(() => {
setSeconds(seconds + 1);
}, 1000);
return <p>{seconds}</p>;
};
ReactDOM.render(<Timer />, document.getElementById('root'));A hook that returns if the client is online or offline.
- Create a function,
getOnLineStatus, that uses theNavigatorOnLineweb API to get the online status of the client. - Use the
React.useState()hook to create an appropriate state variable,status, and setter. - Use the
React.useEffect()hook to add listeners for appropriate events, updating state, and cleanup those listeners when unmounting. - Finally return the
statusstate variable.
const getOnLineStatus = () =>
typeof navigator !== "undefined" && typeof navigator.onLine === "boolean"
? navigator.onLine
: true;
const useNavigatorOnLine = () => {
const [status, setStatus] = React.useState(getOnLineStatus());
const setOnline = () => setStatus(true);
const setOffline = () => setStatus(false);
React.useEffect(() => {
window.addEventListener("online", setOnline);
window.addEventListener("offline", setOffline);
return () => {
window.removeEventListener("online", setOnline);
window.removeEventListener("offline", setOffline);
};
}, []);
return status;
};Examples
const StatusIndicator = () => {
const isOnline = useNavigatorOnLine();
return <span>You are {isOnline ? "online" : "offline"}.</span>;
};
ReactDOM.render(<StatusIndicator />, document.getElementById("root"));A hook that checks if the code is running on the browser or the server.
- Create a custom hook that returns an appropriate object.
- Use
typeof window,window.documentandwindow.document.createElementto check if the code is running on the browser. - Use the
React.useState()hook to define theinBrowserstate variable. - Use the
React.useEffect()hook to update theinBrowserstate variable and clean up at the end. - Use the
React.useMemo()to memoize the return values of the custom hook.
const isDOMavailable = !!(
typeof window !== 'undefined' &&
window.document &&
window.document.createElement
);
const useSSR = (callback, delay) => {
const [inBrowser, setInBrowser] = React.useState(isDOMavailable);
React.useEffect(() => {
setInBrowser(isDOMavailable);
return () => {
setInBrowser(false);
}
}, []);
const useSSRObject = React.useMemo(() => ({
isBrowser: inBrowser,
isServer: !inBrowser,
canUseWorkers: typeof Worker !== 'undefined',
canUseEventListeners: inBrowser && !!window.addEventListener,
canUseViewport: inBrowser && !!window.screen
}), [inBrowser]);
return React.useMemo(() => Object.assign(Object.values(useSSRObject), useSSRObject), [inBrowser]);
};
const SSRChecker = props => {
let { isBrowser, isServer } = useSSR();
return <p>{ isBrowser ? 'Running on browser' : 'Running on server' }</p>;
};Examples
const SSRChecker = props => {
let { isBrowser, isServer } = useSSR();
return <p>{ isBrowser ? 'Running on browser' : 'Running on server' }</p>;
};
ReactDOM.render(<SSRChecker />, document.getElementById('root'));A hook that implements setTimeout in a declarative manner.
- Create a custom hook that takes a
callbackand adelay. - Use the
React.useRef()hook to create areffor the callback function. - Use the
React.useEffect()hook to remember the latest callback. - Use the
React.useEffect()hook to set up the timeout and clean up.
const useTimeout = (callback, delay) => {
const savedCallback = React.useRef();
React.useEffect(() => {
savedCallback.current = callback;
}, [callback]);
React.useEffect(() => {
function tick() {
savedCallback.current();
}
if (delay !== null) {
let id = setTimeout(tick, delay);
return () => clearTimeout(id);
}
}, [delay]);
};Examples
const OneSecondTimer = props => {
const [seconds, setSeconds] = React.useState(0);
useTimeout(() => {
setSeconds(seconds + 1);
}, 1000);
return <p>{seconds}</p>;
};
ReactDOM.render(<OneSecondTimer />, document.getElementById('root'));Renders an <input> element with internal state, that uses a callback function to pass its value to the parent component.
- Use object destructuring to set defaults for certain attributes of the
<input>element. - Use the
React.setState()hook to create thevaluestate variable and give it a value of equal to thedefaultValueprop. - Use the
React.useEffect()hook with a second parameter set to thevaluestate variable to call thecallbackfunction every timevalueis updated. - Render an
<input>element with the appropriate attributes and use the theonChangeevent to upda thevaluestate variable.
function ControlledInput({
callback,
type = 'text',
disabled = false,
readOnly = false,
defaultValue,
placeholder = ''
}) {
const [value, setValue] = React.useState(defaultValue);
React.useEffect(() => {
callback(value);
}, [value]);
return (
<input
defaultValue={defaultValue}
type={type}
disabled={disabled}
readOnly={readOnly}
placeholder={placeholder}
onChange={({ target: { value } }) => setValue(value)}
/>
);
}Examples
ReactDOM.render(
<ControlledInput
type="text"
placeholder="Insert some text here..."
callback={val => console.log(val)}
/>,
document.getElementById('root')
);Renders a textarea component with a character limit.
- Use the
React.useState()hook to create thecontentstate variable and set its value tovalue. Create a methodsetFormattedContent, which trims the content of the input if it's longer thanlimit. - Use the
React.useEffect()hook to call thesetFormattedContentmethod on the value of thecontentstate variable. - Use a
<div>to wrap both the<textarea>and the<p>element that displays the character count and bind theonChangeevent of the<textarea>to callsetFormattedContentwith the value ofevent.target.value.
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);
}, []);
return (
<div>
<textarea
rows={rows}
cols={cols}
onChange={event => setFormattedContent(event.target.value)}
value={content}
/>
<p>
{content.length}/{limit}
</p>
</div>
);
}Examples
ReactDOM.render(<LimitedTextarea limit={32} value="Hello!" />, document.getElementById('root'));Renders a textarea component with a word limit.
- Use the
React.useState()hook to create thecontentandwordCountstate variables and set their values tovalueand0respectively. - Create a method
setFormattedContent, which usesString.prototype.split(' ')to turn the input into an array of words and check if the result of applyingArray.prototype.filter(Boolean)has alengthlonger thanlimit. - If the afforementioned
lengthexceeds thelimit, trim the input, otherwise return the raw input, updatingcontentandwordCountaccordingly in both cases. - Use the
React.useEffect()hook to call thesetFormattedContentmethod on the value of thecontentstate variable. - Use a
<div>to wrap both the<textarea>and the<p>element that displays the character count and bind theonChangeevent of the<textarea>to callsetFormattedContentwith the value ofevent.target.value.
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>
);
}Examples
ReactDOM.render(
<LimitedWordTextarea limit={5} value="Hello there!" />,
document.getElementById('root')
);Renders a checkbox list that uses a callback function to pass its selected value/values to the parent component.
- Use
React.setState()to create adatastate variable and set its initial value equal to theoptionsprop. - Create a function
togglethat is used to toggle thecheckedto update thedatastate variable and call theonChangecallback passed via the component's props. - Render a
<ul>element and useArray.prototype.map()to map thedatastate variable to individual<li>elements with<input>elements as their children. - Each
<input>element has thetype='checkbox'attribute and is marked asreadOnly, as its click events are handled by the parent<li>element'sonClickhandler.
const style = {
listContainer: {
listStyle: 'none',
paddingLeft: 0
},
itemStyle: {
cursor: 'pointer',
padding: 5
}
};
function MultiselectCheckbox({ options, onChange }) {
const [data, setData] = React.useState(options);
const toggle = item => {
data.forEach((_, key) => {
if (data[key].label === item.label) data[key].checked = !item.checked;
});
setData([...data]);
onChange(data);
};
return (
<ul style={style.listContainer}>
{data.map(item => {
return (
<li key={item.label} style={style.itemStyle} onClick={() => toggle(item)}>
<input readOnly type="checkbox" checked={item.checked || false} />
{item.label}
</li>
);
})}
</ul>
);
}Examples
const options = [{ label: 'Item One' }, { label: 'Item Two' }];
ReactDOM.render(
<MultiselectCheckbox
options={options}
onChange={data => {
console.log(data);
}}
/>,
document.getElementById('root')
);Renders a password input field with a reveal button.
- Use the
React.useState()hook to create theshownstate variable and set its value tofalse. - Use a
<div>to wrap both the<input>and the<button>element that toggles the type of the input field between"text"and"password".
function PasswordRevealer({ value }) {
const [shown, setShown] = React.useState(false);
return (
<div>
<input type={shown ? 'text' : 'password'} value={value} onChange={() => {}} />
<button onClick={() => setShown(!shown)}>Show/Hide</button>
</div>
);
}Examples
ReactDOM.render(<PasswordRevealer />, document.getElementById('root'));Renders a <select> element that uses a callback function to pass its value to the parent component.
- Use object destructuring to set defaults for certain attributes of the
<select>element. - Render a
<select>element with the appropriate attributes and use thecallbackfunction in theonChangeevent to pass the value of the textarea to the parent. - Use destructuring on the
valuesarray to pass an array ofvalueandtextelements and theselectedattribute to define the initialvalueof the<select>element.
function Select({ values, callback, disabled = false, readonly = false, selected }) {
return (
<select
disabled={disabled}
readOnly={readonly}
onChange={({ target: { value } }) => callback(value)}
>
{values.map(([value, text]) => (
<option selected={selected === value} value={value}>
{text}
</option>
))}
</select>
);
}Examples
let choices = [
['grapefruit', 'Grapefruit'],
['lime', 'Lime'],
['coconut', 'Coconut'],
['mango', 'Mango']
];
ReactDOM.render(
<Select values={choices} selected="lime" callback={val => console.log(val)} />,
document.getElementById('root')
);Renders a slider element that uses a callback function to pass its value to the parent component.
- Use object destructuring to set defaults for certain attributes of the
<input>element. - Render an
<input>element of type"range"and the appropriate attributes, use thecallbackfunction in theonChangeevent to pass the value of the input to the parent.
function Slider({ callback, disabled = false, readOnly = false }) {
return (
<input
type="range"
disabled={disabled}
readOnly={readOnly}
onChange={({ target: { value } }) => callback(value)}
/>
);
}Examples
ReactDOM.render(<Slider callback={val => console.log(val)} />, document.getElementById('root'));Renders a tag input field.
- Define a
TagInputcomponent and useReact.useState()hook to initialize an array with tags passed asprops. - Use
Array.prototype.map()on collected nodes to render the list of tags. - Define the
addTagsmethod, which will be executed on pressing theEnterkey. - The
addTagsmethod uses thesetTagsmethod to add the new tag using the spread (...) operator to prepend the existing tags and adds the new tag at the end of thetagsarray. - Define the
removeTagsmethod, which will be executed on clicking the delete icon in the tag. - Use
Array.prototype.filter()inremoveTagsmethod to remove the tag using theindexof the tag to filter it out fromtagsarray.
.tag-input {
display: flex;
flex-wrap: wrap;
min-height: 48px;
padding: 0 8px;
border: 1px solid #d6d8da;
border-radius: 6px;
}
.tag-input input {
flex: 1;
border: none;
height: 46px;
font-size: 14px;
padding: 4px 0 0;
&:focus {
outline: transparent;
}
}
#tags {
display: flex;
flex-wrap: wrap;
padding: 0;
margin: 8px 0 0;
}
.tag {
width: auto;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
padding: 0 8px;
font-size: 14px;
list-style: none;
border-radius: 6px;
margin: 0 8px 8px 0;
background: #0052cc;
}
.tag-title {
margin-top: 3px;
}
.tag-close-icon {
display: block;
width: 16px;
height: 16px;
line-height: 16px;
text-align: center;
font-size: 14px;
margin-left: 8px;
color: #0052cc;
border-radius: 50%;
background: #fff;
cursor: pointer;
}function TagInput(props) {
const [tags, setTags] = React.useState(props.tags);
const removeTags = indexToRemove => {
setTags([...tags.filter((_, index) => index !== indexToRemove)]);
};
const addTags = event => {
if (event.target.value !== "") {
setTags([...tags, event.target.value]);
event.target.value = "";
}
};
return (
<div className="tag-input">
<ul id="tags">
{tags.map((tag, index) => (
<li key={index} className="tag">
<span className="tag-title">{tag}</span>
<span className="tag-close-icon" onClick={() => removeTags(index)}>
x
</span>
</li>
))}
</ul>
<input
type="text"
onKeyUp={event => (event.key === "Enter" ? addTags(event) : null)}
placeholder="Press enter to add tags"
/>
</div>
);
}Examples
ReactDOM.render(<TagInput tags={['Nodejs', 'MongoDB']}/>, document.getElementById('root'));Renders a <textarea> element that uses a callback function to pass its value to the parent component.
- Use object destructuring to set defaults for certain attributes of the
<textarea>element. - Render a
<textarea>element with the appropriate attributes and use thecallbackfunction in theonChangeevent to pass the value of the textarea to the parent.
function TextArea({
callback,
cols = 20,
rows = 2,
disabled = false,
readOnly = false,
placeholder = ''
}) {
return (
<textarea
cols={cols}
rows={rows}
disabled={disabled}
readOnly={readOnly}
placeholder={placeholder}
onChange={({ target: { value } }) => callback(value)}
/>
);
}Examples
ReactDOM.render(
<TextArea placeholder="Insert some text here..." callback={val => console.log(val)} />,
document.getElementById('root')
);Renders an <input> element that uses a callback function to pass its value to the parent component.
- Use object destructuring to set defaults for certain attributes of the
<input>element. - Render an
<input>element with the appropriate attributes and use thecallbackfunction in theonChangeevent to pass the value of the input to the parent.
function UncontrolledInput({
callback,
type = 'text',
disabled = false,
readOnly = false,
placeholder = ''
}) {
return (
<input
type={type}
disabled={disabled}
readOnly={readOnly}
placeholder={placeholder}
onChange={({ target: { value } }) => callback(value)}
/>
);
}Examples
ReactDOM.render(
<UncontrolledInput
type="text"
placeholder="Insert some text here..."
callback={val => console.log(val)}
/>,
document.getElementById('root')
);Renders an accordion menu with multiple collapsible content components.
- Define an
AccordionItemcomponent, pass it to theAccordionand remove unnecessary nodes expect forAccordionItemby identifying the function's name inprops.children. - Each
AccordionItemcomponent renders a<button>that is used to update theAccordionvia theprops.handleClickcallback and the content of the component, passed down viaprops.children, while its appearance is determined byprops.isCollapsedand based onstyle. - In the
Accordioncomponent, use theReact.useState()hook to initialize the value of thebindIndexstate variable toprops.defaultIndex. - Use
Array.prototype.mapon the collected nodes to render the individual collapsiple elements. - Define
changeItem, which will be executed when clicking anAccordionItem's<button>.changeItemexecutes the passed callback,onItemClickand updatesbindIndexbased on the clicked element.
function AccordionItem(props) {
const style = {
collapsed: {
display: 'none'
},
expanded: {
display: 'block'
},
buttonStyle: {
display: 'block',
width: '100%'
}
};
return (
<div>
<button style={style.buttonStyle} onClick={() => props.handleClick()}>
{props.label}
</button>
<div
className="collapse-content"
style={props.isCollapsed ? style.collapsed : style.expanded}
aria-expanded={props.isCollapsed}
>
{props.children}
</div>
</div>
);
}
function Accordion(props) {
const [bindIndex, setBindIndex] = React.useState(props.defaultIndex);
const changeItem = itemIndex => {
if (typeof props.onItemClick === 'function') props.onItemClick(itemIndex);
if (itemIndex !== bindIndex) setBindIndex(itemIndex);
};
const items = props.children.filter(item => item.type.name === 'AccordionItem');
return (
<div className="wrapper">
{items.map(({ props }) => (
<AccordionItem
isCollapsed={bindIndex !== props.index}
label={props.label}
handleClick={() => changeItem(props.index)}
children={props.children}
/>
))}
</div>
);
}Examples
ReactDOM.render(
<Accordion defaultIndex="1" onItemClick={console.log}>
<AccordionItem label="A" index="1">
Lorem ipsum
</AccordionItem>
<AccordionItem label="B" index="2">
Dolor sit amet
</AccordionItem>
</Accordion>,
document.getElementById('root')
);Creates an alert component with type prop.
- Define appropriate CSS styles and animations for the component's elements.
- Use the
React.setState()hook to create theisShownandisLeavingstate variables and set their values tofalse. - Define
timeoutIdto keep the timer instance for clearing on component unmount. - Use the
React.setEffect()hook to update the value ofisShowntotrueand clear interval by usingtimeoutIdwhen component is unmounted. - Define
closeNotificationfunction to set the component is removed from DOM by displaying fading out animation and setisShowntofalseviasetTimeout(). - Define the component, which renders the alert component with user defined
messageand a close button to remove the component from DOM.
@keyframes leave {
0% { opacity: 1 }
100% { opacity: 0 }
}
.alert {
padding: 0.75rem 0.5rem;
margin-bottom: 0.5rem;
text-align: left;
padding-right: 40px;
border-radius: 4px;
font-size: 16px;
position: relative;
}
.alert.warning{
color: #856404;
background-color: #fff3cd;
border-color: #ffeeba;
}
.alert.error{
color: #721c24;
background-color: #f8d7da;
border-color: #f5c6cb;
}
.alert.leaving{
animation: leave 0.5s forwards;
}
.alert .close {
position: absolute;
top: 0;
right: 0;
padding: 0 0.75rem;
color: #333;
border: 0;
height: 100%;
cursor: pointer;
background: none;
font-weight: 600;
font-size: 16px;
}
.alert .close:after{
content: 'x';
}function Notification(props) {
const [isShown, setIsShown] = React.useState(false);
const [isLeaving, setIsLeaving] = React.useState(false);
let timeoutId = null;
React.useEffect(() => {
setIsShown(true);
return () => {
clearTimeout(timeoutId);
}
}, [props.isShown, props.timeout, timeoutId]);
const closeNotification = () => {
setIsLeaving(true);
timeoutId = setTimeout(() => {
setIsLeaving(false);
setIsShown(false);
}, 250)
}
return isShown && (
<div className={`alert ${props.type}${isLeaving ? ' leaving' : ''}`} role="alert">
<button className="close" onClick={closeNotification} />
{props.message}
</div>
)
}Examples
ReactDOM.render(<Notification type="info" message="This is info" />, document.getElementById('root'));Renders a string as plaintext, with URLs converted to appropriate <a> elements.
- Use
String.prototype.split()andString.prototype.match()with a regular expression to find URLs in a string. - Return a
<React.Fragment>with matched URLs rendered as<a>elements, dealing with missing protocol prefixes if necessary, and the rest of the string rendered as plaintext.
function AutoLink({ text }) {
const delimiter = /((?:https?:\/\/)?(?:(?:[a-z0-9]?(?:[a-z0-9\-]{1,61}[a-z0-9])?\.[^\.|\s])+[a-z\.]*[a-z]+|(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3})(?::\d{1,5})*[a-z0-9.,_\/~#&=;%+?\-\\(\\)]*)/gi;
return (
<React.Fragment>
{text.split(delimiter).map(word => {
let match = word.match(delimiter);
if (match) {
let url = match[0];
return <a href={url.startsWith('http') ? url : `http://${url}`}>{url}</a>;
}
return word;
})}
</React.Fragment>
);
}Examples
ReactDOM.render(
<AutoLink text="foo bar baz http://example.org bar" />,
document.getElementById('root')
);Renders a carousel component.
- Use the
React.setState()hook to create theactivestate variable and give it a value of0(index of the first item). - Use an object,
style, to hold the styles for the individual components. - Use the
React.useEffect()hook to update the value ofactiveto the index of the next item, usingsetTimeout. - Destructure
props, compute if visibility style should be set tovisibleor 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 restpropsalong with the computed styles.
function Carousel(props) {
const [active, setActive] = React.useState(0);
let scrollInterval = null;
const style = {
carousel: {
position: 'relative'
},
carouselItem: {
position: 'absolute',
visibility: 'hidden'
},
visible: {
visibility: 'visible'
}
};
React.useEffect(() => {
scrollInterval = setTimeout(() => {
const { carouselItems } = props;
setActive((active + 1) % carouselItems.length);
}, 2000);
return () => clearTimeout(scrollInterval);
});
const { carouselItems, ...rest } = props;
return (
<div style={style.carousel}>
{carouselItems.map((item, index) => {
const activeStyle = active === index ? style.visible : {};
return React.cloneElement(item, {
...rest,
style: {
...style.carouselItem,
...activeStyle
}
});
})}
</div>
);
}Examples
ReactDOM.render(
<Carousel
carouselItems={[
<div>carousel item 1</div>,
<div>carousel item 2</div>,
<div>carousel item 3</div>
]}
/>,
document.getElementById('root')
);Renders a component with collapsible content.
- Use the
React.setState()hook to create theisCollapsedstate variable with an initial value ofprops.collapsed. - Use an object,
style, to hold the styles for individual components and their states. - Use a
<div>to wrap both the<button>that alters the component'sisCollapsedstate and the content of the component, passed down viaprops.children. - Determine the appearance of the content, based on
isCollapsedand apply the appropriate CSS rules from thestyleobject. - Finally, update the value of the
aria-expandedattribute based onisCollapsedto make the component accessible.
function Collapse(props) {
const [isCollapsed, setIsCollapsed] = React.useState(props.collapsed);
const style = {
collapsed: {
display: 'none'
},
expanded: {
display: 'block'
},
buttonStyle: {
display: 'block',
width: '100%'
}
};
return (
<div>
<button style={style.buttonStyle} onClick={() => setIsCollapsed(!isCollapsed)}>
{isCollapsed ? 'Show' : 'Hide'} content
</button>
<div
className="collapse-content"
style={isCollapsed ? style.collapsed : style.expanded}
aria-expanded={isCollapsed}
>
{props.children}
</div>
</div>
);
}Examples
ReactDOM.render(
<Collapse>
<h1>This is a collapse</h1>
<p>Hello world!</p>
</Collapse>,
document.getElementById('root')
);Renders a countdown timer that prints a message when it reaches zero.
- Use object destructuring to set defaults for the
hours,minutesandsecondsprops. - Use the
React.useState()hook to create thetime,pausedandoverstate variables and set their values to the values of the passed props,falseandfalserespectively. - Create a method
tick, that updates the value oftimebased on the current value (i.e. decreasing the time by one second). - If
pausedoroveristrue,tickwill return immediately. - Create a method
reset, that resets all state variables to their initial states. - Use the the
React.useEffect()hook to call thetickmethod every second via the use ofsetInterval()and useclearInterval()to cleanup when the component is unmounted. - Use a
<div>to wrap a<p>element with the textual representation of the componentstimestate variable, as well as two<button>elements that will pause/unpause and restart the timer respectively. - If
overistrue, the timer will display a message instead of the value oftime.
function CountDown({ hours = 0, minutes = 0, seconds = 0 }) {
const [paused, setPaused] = React.useState(false);
const [over, setOver] = React.useState(false);
const [time, setTime] = React.useState({
hours: parseInt(hours),
minutes: parseInt(minutes),
seconds: parseInt(seconds)
});
const tick = () => {
if (paused || over) return;
if (time.hours == 0 && time.minutes == 0 && time.seconds == 0) setOver(true);
else if (time.minutes == 0 && time.seconds == 0)
setTime({
hours: time.hours - 1,
minutes: 59,
seconds: 59
});
else if (time.seconds == 0)
setTime({
hours: time.hours,
minutes: time.minutes - 1,
seconds: 59
});
else
setTime({
hours: time.hours,
minutes: time.minutes,
seconds: time.seconds - 1
});
};
const reset = () => {
setTime({
hours: parseInt(hours),
minutes: parseInt(minutes),
seconds: parseInt(seconds)
});
setPaused(false);
setOver(false);
};
React.useEffect(() => {
let timerID = setInterval(() => tick(), 1000);
return () => clearInterval(timerID);
});
return (
<div>
<p>{`${time.hours.toString().padStart(2, '0')}:${time.minutes
.toString()
.padStart(2, '0')}:${time.seconds.toString().padStart(2, '0')}`}</p>
<div>{over ? "Time's up!" : ''}</div>
<button onClick={() => setPaused(!paused)}>{paused ? 'Resume' : 'Pause'}</button>
<button onClick={() => reset()}>Restart</button>
</div>
);
}Examples
ReactDOM.render(<CountDown hours="1" minutes="45" />, document.getElementById('root'));Renders a file drag and drop component for a single file.
- Create a ref called
dropReffor this component. - Use the
React.useState()hook to create thedragandfilenamevariables, initialized tofalseand''respectively. The variablesdragCounteranddragare used to determine if a file is being dragged, whilefilenameis used to store the dropped file's name. - Create the
handleDrag,handleDragIn,handleDragOutandhandleDropmethods 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 the
React.useEffect()hook and its attachedcleanup()method. handleDragprevents the browser from opening the dragged file,handleDragInandhandleDragOuthandle the dragged file entering and exiting the component, whilehandleDrophandles the file being dropped and passes it toprops.handleDrop.- Return an appropriately styled
<div>and usedragandfilenameto determine its contents and style. - Finally, bind the
refof the created<div>todropRef.
.filedrop {
min-height: 120px;
border: 3px solid #d3d3d3;
text-align: center;
font-size: 24px;
padding: 32px;
border-radius: 4px;
}
.filedrop.drag {
border: 3px dashed #1e90ff;
}
.filedrop.ready {
border: 3px solid #32cd32;
}function FileDrop(props) {
const [drag, setDrag] = React.useState(false);
const [filename, setFilename] = React.useState('');
let dropRef = React.createRef();
let dragCounter = 0;
const handleDrag = e => {
e.preventDefault();
e.stopPropagation();
};
const handleDragIn = e => {
e.preventDefault();
e.stopPropagation();
dragCounter++;
if (e.dataTransfer.items && e.dataTransfer.items.length > 0) setDrag(true);
};
const handleDragOut = e => {
e.preventDefault();
e.stopPropagation();
dragCounter--;
if (dragCounter === 0) setDrag(false);
};
const handleDrop = e => {
e.preventDefault();
e.stopPropagation();
setDrag(false);
if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
props.handleDrop(e.dataTransfer.files[0]);
setFilename(e.dataTransfer.files[0].name);
e.dataTransfer.clearData();
dragCounter = 0;
}
};
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);
};
});
return (
<div
ref={dropRef}
className={drag ? 'filedrop drag' : filename ? 'filedrop ready' : 'filedrop'}
>
{filename && !drag ? <div>{filename}</div> : <div>Drop files here!</div>}
</div>
);
}Examples
ReactDOM.render(<FileDrop handleDrop={console.log} />, document.getElementById('root'));Creates a spinning loader component.
- Define appropriate CSS styles and animations for the component's elements.
- Define the component, which returns a simple SVG, whose size is determined by the
sizeprop.
.loader {
animation: rotate 2s linear infinite;
}
@keyframes rotate {
100% {
transform: rotate(360deg);
}
}
.loader circle {
animation: dash 1.5s ease-in-out infinite;
}
@keyframes dash {
0% {
stroke-dasharray: 1, 150;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -35;
}
100% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -124;
}
}function Loader({ size }) {
return (
<svg
className="loader"
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="12" cy="12" r="10" />
</svg>
);
}Examples
ReactDOM.render(<Loader size={24} />, document.getElementById('root'));Renders a link formatted to send an email.
- Destructure the component's props, use
email,subjectandbodyto create a<a>element with an appropriatehrefattribute. - Render the link with
props.childrenas its content.
function Mailto({ email, subject, body, ...props }) {
return (
<a href={`mailto:${email}?subject=${encodeURIComponent(subject) || ''}&body=${encodeURIComponent(body) || ''}`}>{props.children}</a>
);
}Examples
ReactDOM.render(
<Mailto email="foo@bar.baz" subject="Hello & Welcome" body="Hello world!">
Mail me!
</Mailto>,
document.getElementById('root')
);Renders a Modal component, controllable through events.
To use the component, import Modal only once and then display it by passing a boolean value to the isVisible attribute.
- Use object destructuring to set defaults for certain attributes of the modal component.
- Define
keydownHandler, a method which handles all keyboard events, which can be used according to your needs to dispatch actions (e.g. close the modal when Esc is pressed). - Use
React.useEffect()hook to add or remove thekeydownevent listener, which callskeydownHandler. - Use the
isVisibleprop to determine if the modal should be shown or not. - Use CSS to style and position the modal component.
.modal {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 100%;
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.25);
animation-name: appear;
animation-duration: 300ms;
}
.modal-dialog {
width: 100%;
max-width: 550px;
background: white;
position: relative;
margin: 0 20px;
max-height: calc(100vh - 40px);
text-align: left;
display: flex;
flex-direction: column;
overflow: hidden;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
-webkit-animation-name: animatetop;
-webkit-animation-duration: 0.4s;
animation-name: slide-in;
animation-duration: 0.5s;
}
.modal-header,
.modal-footer {
display: flex;
align-items: center;
padding: 1rem;
}
.modal-header {
border-bottom: 1px solid #dbdbdb;
justify-content: space-between;
}
.modal-footer {
border-top: 1px solid #dbdbdb;
justify-content: flex-end;
}
.modal-close {
cursor: pointer;
padding: 1rem;
margin: -1rem -1rem -1rem auto;
}
.modal-body {
overflow: auto;
}
.modal-content {
padding: 1rem;
}
@keyframes appear {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes slide-in {
from {
transform: translateY(-150px);
}
to {
transform: translateY(0);
}
}function Modal({ isVisible = false, title, content, footer, onClose }) {
React.useEffect(() => {
document.addEventListener('keydown', keydownHandler);
return () => document.removeEventListener('keydown', keydownHandler);
});
function keydownHandler({ key }) {
switch (key) {
case 'Escape':
onClose();
break;
default:
}
}
return !isVisible ? null : (
<div className="modal" onClick={onClose}>
<div className="modal-dialog" onClick={e => e.stopPropagation()}>
<div className="modal-header">
<h3 className="modal-title">{title}</h3>
<span className="modal-close" onClick={onClose}>
×
</span>
</div>
<div className="modal-body">
<div className="modal-content">{content}</div>
</div>
{footer && <div className="modal-footer">{footer}</div>}
</div>
</div>
);
}Examples
//Add the component to the render function
function App() {
const [isModal, setModal] = React.useState(false);
return (
<React.Fragment>
<button onClick={() => setModal(true)}>Click Here</button>
<Modal
isVisible={isModal}
title="Modal Title"
content={<p>Add your content here</p>}
footer={<button>Cancel</button>}
onClose={() => setModal(false)}
/>
</React.Fragment>
);
}
ReactDOM.render(<App />, document.getElementById('root'));Renders a button that animates a ripple effect when clicked.
- Define some appropriate CSS styles and an animation for the ripple effect.
- Use the
React.useState()hook to create appropriate variables and setters for the pointer's coordinates and for the animation state of the button. - Use the
React.useEffect()hook to change the state every time thecoordsstate variable changes, starting the animation. - Use
setTimeout()in the previous hook to clear the animation after it's done playing. - Use the
React.useEffect()hook a second time to resetcoordswhenever theisRipplingstate variable isfalse. - Handle the
onClickevent by updating thecoordsstate variable and calling the passed callback. - Finally, render a
<button>with one or two<span>elements, setting the position of the.rippleelement based on thecoordsstate variable.
.ripple-button {
border-radius: 4px;
border: none;
margin: 8px;
padding: 14px 24px;
background: #1976d2;
color: #fff;
overflow: hidden;
position: relative;
cursor: pointer;
}
.ripple-button > .ripple {
width: 20px;
height: 20px;
position: absolute;
background: #63a4ff;
display: block;
content: "";
border-radius: 9999px;
opacity: 1;
animation: 1.2s ease 1 forwards ripple-effect;
}
@keyframes ripple-effect {
0% {
transform: scale(1);
opacity: 1;
}
50% {
transform: scale(10);
opacity: 0.375;
}
100% {
transform: scale(35);
opacity: 0;
}
}
.ripple-button > .content {
position: relative;
z-index: 2;
}function RippleButton({ children, onClick }) {
const [coords, setCoords] = React.useState({ x: -1, y: -1 });
const [isRippling, setIsRippling] = React.useState(false);
React.useEffect(
() => {
if (coords.x !== -1 && coords.y !== -1) {
setIsRippling(true);
setTimeout(() => setIsRippling(false), 1200);
} else setIsRippling(false);
},
[coords]
);
React.useEffect(
() => {
if (!isRippling) setCoords({ x: -1, y: -1 });
},
[isRippling]
);
return (
<button
className="ripple-button"
onClick={e => {
var rect = e.target.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
setCoords({ x, y });
onClick && onClick(e);
}}
>
{isRippling ? (
<span
className="ripple"
style={{
left: coords.x + 10,
top: coords.y
}}
/>
) : (
""
)}
<span className="content">{children}</span>
</button>
);
}Examples
ReactDOM.render(
<RippleButton onClick={e => console.log(e)}>Click me</RippleButton>,
document.getElementById('root')
);Renders a star rating component.
- Define a component, called
Starthat will render each individual star with the appropriate appearance, based on the parent component's state. - In the
StarRatingcomponent, use theReact.useState()hook to define theratingandselectionstate variables with the initial values ofprops.rating(or0if invalid or not supplied) and0. - Create a method,
hoverOver, that updatesselectedandratingaccording to the providedevent. - Create a
<div>to wrap the<Star>components, which are created usingArray.prototype.mapon an array of 5 elements, created usingArray.from, and handle theonMouseLeaveevent to setselectionto0, theonClickevent to set theratingand theonMouseOverevent to setselectionto thestar-idattribute of theevent.targetrespectively. - Finally, pass the appropriate values to each
<Star>component (starIdandmarked).
function Star({ marked, starId }) {
return (
<span star-id={starId} style={{ color: '#ff9933' }} role="button">
{marked ? '\u2605' : '\u2606'}
</span>
);
}
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');
setSelection(val);
};
return (
<div
onMouseOut={() => hoverOver(null)}
onClick={event => setRating(event.target.getAttribute('star-id') || rating)}
onMouseOver={hoverOver}
>
{Array.from({ length: 5 }, (v, i) => (
<Star
starId={i + 1}
key={`star_${i + 1} `}
marked={selection ? selection >= i + 1 : rating >= i + 1}
/>
))}
</div>
);
}Examples
ReactDOM.render(<StarRating />, document.getElementById('root'));
ReactDOM.render(<StarRating rating={2} />, document.getElementById('root'));Renders a tabbed menu and view component.
- Define a
TabItemcomponent, pass it to theTaband remove unnecessary nodes expect forTabItemby identifying the function's name inprops.children. - Use the
React.useState()hook to initialize the value of thebindIndexstate variable toprops.defaultIndex. - Use
Array.prototype.mapon the collected nodes to render thetab-menuandtab-view. - Define
changeTab, which will be executed when clicking a<button>from thetab-menu. changeTabexecutes the passed callback,onTabClickand updatesbindIndex, which in turn causes a re-render, evaluating thestyleandclassNameof thetab-viewitems andtab-menubuttons according to theirindex.
.tab-menu > button {
cursor: pointer;
padding: 8px 16px;
border: 0;
border-bottom: 2px solid transparent;
background: none;
}
.tab-menu > button.focus {
border-bottom: 2px solid #007bef;
}
.tab-menu > button:hover {
border-bottom: 2px solid #007bef;
}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);
};
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={() => changeTab(index)} className={bindIndex === index ? 'focus' : ''}>
{label}
</button>
))}
</div>
<div className="tab-view">
{items.map(({ props }) => (
<div
{...props}
className="tab-view_item"
key={props.index}
style={{ display: bindIndex === props.index ? 'block' : 'none' }}
/>
))}
</div>
</div>
);
}Examples
ReactDOM.render(
<Tabs defaultIndex="1" onTabClick={console.log}>
<TabItem label="A" index="1">
Lorem ipsum
</TabItem>
<TabItem label="B" index="2">
Dolor sit amet
</TabItem>
</Tabs>,
document.getElementById('root')
);Renders a ticker component.
- Use the
React.useState()hook to initialize thetickerstate variable to0. - Define two methods,
tickandreset, that will periodically incrementtimerbased onintervaland resetintervalrespectively. - Return a
<div>with two<button>elements, each of which callstickandresetrespectively.
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);
};
const reset = () => {
setTicker(0);
clearInterval(interval);
};
return (
<div>
<span style={{ fontSize: 100 }}>{ticker}</span>
<button onClick={tick}>Tick!</button>
<button onClick={reset}>Reset</button>
</div>
);
}Examples
ReactDOM.render(<Ticker times={5} interval={1000} />, document.getElementById('root'));Renders a toggle component.
- Use the
React.useState()to initialize theisToggleOnstate variable tofalse. - Use an object,
style, to hold the styles for individual components and their states. - Return a
<button>that alters the component'sisToggledOnwhen itsonClickevent is fired and determine the appearance of the content based onisToggleOn, applying the appropriate CSS rules from thestyleobject.
function Toggle(props) {
const [isToggleOn, setIsToggleOn] = React.useState(false);
style = {
on: {
backgroundColor: 'green'
},
off: {
backgroundColor: 'grey'
}
};
return (
<button onClick={() => setIsToggleOn(!isToggleOn)} style={isToggleOn ? style.on : style.off}>
{isToggleOn ? 'ON' : 'OFF'}
</button>
);
}Examples
ReactDOM.render(<Toggle />, document.getElementById('root'));Renders a tooltip component.
- Use the
React.useState()hook to create theshowvariable and initialize it tofalse. - Return a
<div>element that contains the<div>that will be the tooltip and thechildrenpassed to the component. - Handle the
onMouseEnterandonMouseLeavemethods, by altering the value of theshowvariable.
.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;
}function Tooltip({ children, text, ...rest }) {
const [show, setShow] = React.useState(false);
return (
<div>
<div className="tooltip" style={show ? { visibility: 'visible' } : {}}>
{text}
<span className="tooltip-arrow" />
</div>
<div {...rest} onMouseEnter={() => setShow(true)} onMouseLeave={() => setShow(false)}>
{children}
</div>
</div>
);
}Examples
ReactDOM.render(
<Tooltip text="Simple tooltip">
<button>Hover me!</button>
</Tooltip>,
document.getElementById('root')
);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
toggledprop to determine the initial state of the content (collapsed/expanded). - Use the
React.setState()hook to create theisToggledstate variable and give it the value of thetoggledprop initially. - Return a
<div>to wrap the contents of the component and the<span>element, used to alter the component'sisToggledstate. - Determine the appearance of the component, based on
isParentToggled,isToggled,nameandArray.isArray()ondata. - 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.
.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;
}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> {name}: </strong> : <span> </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>
);
}Examples
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'));This repository is a work in progress. If you want to contribute, please check the open issues to see where and how you can help out!
This README is built using markdown-builder.
