Curated collection of useful React snippets that you can understand in 30 seconds or less.
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';
Renders a list of elements from an array of primitives.
isOrdered
prop to conditionally render a <ol>
or <ul>
list.Array.prototype.map
to render every item in data
as a <li>
element, give it a key
produced from the concatenation of the its index and value.isOrdered
prop 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>;
}
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.
<table>
element with two columns (ID
and Value
).Array.prototype.map
to render every item in data
as a <tr>
element, consisting of its index and value, give it a key
produced 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>
);
}
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.
Object.keys()
, Array.prototype.filter()
, Array.prototype.includes()
and Array.prototype.reduce()
to produce a filteredData
array, containing all objects with the keys specified in propertyNames
.<table>
element with a set of columns equal to the amount of values in propertyNames
.Array.prototype.map
to render each value in the propertyNames
array as a <th>
element.Array.prototype.map
to render each object in the filteredData
array as a <tr>
element, containing a <td>
for each key in the object.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>
);
}
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 -->,<!-expertise: 1 -->
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')
);
Renders an <input>
element that uses a callback function to pass its value to the parent component.
<input>
element.<input>
element with the appropriate attributes and use the callback
function in the onChange
event to pass the value of the input to the parent.function Input({ callback, type = 'text', disabled = false, readOnly = false, placeholder = '' }) {
return (
<input
type={type}
disabled={disabled}
readOnly={readOnly}
placeholder={placeholder}
onChange={({ target: { value } }) => callback(value)}
/>
);
}
ReactDOM.render(
<Input type="text" placeholder="Insert some text here..." callback={val => console.log(val)} />,
document.getElementById('root')
);
Renders a textarea component with a character limit.
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
.React.useEffect()
hook to call the setFormattedContent
method on the value of the content
state variable.<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
.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>
);
}
ReactDOM.render(<LimitedTextarea limit={32} value="Hello!" />, document.getElementById('root'));
Renders a textarea component with a word limit.
React.useState()
hook to create the content
and wordCount
state variables and set their values to value
and 0
respectively.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
.length
exceeds the limit
, trim the input, otherwise return the raw input, updating content
and wordCount
accordingly in both cases.React.useEffect()
hook to call the setFormattedContent
method on the value of the content
state variable.<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
.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>
);
}
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.
React.setState()
to create a data
state variable and set its initial value equal to the options
prop.toggle
that is used to toggle the checked
to update the data
state variable and call the onChange
callback passed via the component's props.<ul>
element and use Array.prototype.map()
to map the data
state variable to individual <li>
elements with <input>
elements as their children.<input>
element has the type='checkbox'
attribute and is marked as readOnly
, as its click events are handled by the parent <li>
element's onClick
handler.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.map((_, 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>
);
}
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.
React.useState()
hook to create the shown
state variable and set its value to false
.<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>
);
}
ReactDOM.render(<PasswordRevealer />, document.getElementById('root'));
Renders a <select>
element that uses a callback function to pass its value to the parent component.
<select>
element.<select>
element with the appropriate attributes and use the callback
function in the onChange
event to pass the value of the textarea to the parent.values
array to pass an array of value
and text
elements and the selected
attribute to define the initial value
of 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>
);
}
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.
<input>
element.<input>
element of type "range"
and the appropriate attributes, use the callback
function in the onChange
event 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)}
/>
);
}
ReactDOM.render(<Slider callback={val => console.log(val)} />, document.getElementById('root'));
Renders a <textarea>
element that uses a callback function to pass its value to the parent component.
<textarea>
element.<textarea>
element with the appropriate attributes and use the callback
function in the onChange
event 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)}
/>
);
}
ReactDOM.render(
<TextArea placeholder="Insert some text here..." callback={val => console.log(val)} />,
document.getElementById('root')
);
Renders a tree view of a JSON object or array with collapsible content.
toggled
prop to determine the initial state of the content (collapsed/expanded).React.setState()
hook to create the isToggled
state variable and give it the value of the toggled
prop initially.<div>
to wrap the contents of the component and the <span>
element, used to alter the component's isToggled
state.isParentToggled
, isToggled
, name
and Array.isArray()
on data
.data
, determine if it is an object or array and recursively render a sub-tree.<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>
);
}
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'));
Renders a string as plaintext, with URLs converted to appropriate <a>
elements.
String.prototype.split()
and String.prototype.match()
with a regular expression to find URLs in a string.<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>
);
}
ReactDOM.render(
<AutoLink text="foo bar baz http://example.org bar" />,
document.getElementById('root')
);
Renders an accordion menu with multiple collapsible content components.
AccordionItem
component, pass it to the Accordion
and remove unnecessary nodes expect for AccordionItem
by identifying the function's name in props.children
.AccordionItem
component renders a <button>
that is used to update the Accordion
via the props.handleClick
callback and the content of the component, passed down via props.children
, while its appearance is determined by props.isCollapsed
and based on style
.Accordion
component, use the React.useState()
hook to initialize the value of the bindIndex
state variable to props.defaultIndex
.Array.prototype.map
on the collected nodes to render the individual collapsiple elements.changeItem
, which will be executed when clicking an AccordionItem
's <button>
. changeItem
executes the passed callback, onItemClick
and updates bindIndex
based 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>
);
}
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')
);
Renders a carousel component.
React.setState()
hook to create the active
state variable and give it a value of 0
(index of the first item).style
, to hold the styles for the individual components.React.setEffect()
hook to update the value of active
to the index of the next item, using setTimeout
.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.React.cloneElement()
and pass down rest props
along 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);
});
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>
);
}
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.
React.setState()
hook to create the isCollapsed
state variable with an initial value of props.collapsed
.style
, to hold the styles for individual components and their states.<div>
to wrap both the <button>
that alters the component's isCollapsed
state and the content of the component, passed down via props.children
.isCollapsed
and apply the appropriate CSS rules from the style
object.aria-expanded
attribute based on isCollapsed
to 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>
);
}
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.
hours
, minutes
and seconds
props.React.useState()
hook to create the time
, paused
and over
state variables and set their values to the values of the passed props, false
and false
respectively.tick
, that updates the value of time
based on the current value (i.e. decreasing the time by one second).paused
or over
is true
, tick
will return immediately.reset
, that resets all state variables to their initial states.React.useEffect()
hook to call the tick
method every second via the use of setInterval()
and use clearInterval()
to cleanup when the component is unmounted.<div>
to wrap a <p>
element with the textual representation of the components time
state variable, as well as two <button>
elements that will pause/unpause and restart the timer respectively.over
is true
, the timer will display a message instead of the value of time
.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>
);
}
ReactDOM.render(<CountDown hours="1" minutes="45" />, document.getElementById('root'));
Renders a file drag and drop component for a single file.
dropRef
for this component.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.handleDrag
, handleDragIn
, handleDragOut
and handleDrop
methods to handle drag and drop functionality, bind them to the component's context.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
.<div>
and use drag
and filename
to determine its contents and style.ref
of the created <div>
to dropRef
..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>
);
}
ReactDOM.render(<FileDrop handleDrop={console.log} />, document.getElementById('root'));
Renders a link formatted to send an email.
email
, subject
and body
to create a <a>
element with an appropriate href
attribute.props.children
as its content.function Mailto({ email, subject, body, ...props }) {
return (
<a href={`mailto:${email}?subject=${subject || ''}&body=${body || ''}`}>{props.children}</a>
);
}
ReactDOM.render(
<Mailto email="foo@bar.baz" subject="Hello" 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.
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).React.useEffect()
hook to add or remove the keydown
event listener, which calls keydownHandler
.isVisible
prop to determine if the modal should be shown or not..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>
)
}
//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 star rating component.
Star
that will render each individual star with the appropriate appearance, based on the parent component's state.StarRating
component, use the React.useState()
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
.hoverOver
, that updates selected
and rating
according to the provided event
.<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.<Star>
component (starId
and marked
).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>
);
}
ReactDOM.render(<StarRating />, document.getElementById('root'));
ReactDOM.render(<StarRating rating={2} />, document.getElementById('root'));
Renders a tabbed menu and view component.
TabItem
component, pass it to the Tab
and remove unnecessary nodes expect for TabItem
by identifying the function's name in props.children
.React.useState()
hook to initialize the value of the bindIndex
state variable to props.defaultIndex
.Array.prototype.map
on the collected nodes to render the tab-menu
and tab-view
.changeTab
, which will be executed when clicking a <button>
from the tab-menu
.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
..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>
);
}
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.
React.useState()
hook to initialize the ticker
state variable to 0
.tick
and reset
, that will periodically increment timer
based on interval
and reset interval
respectively.<div>
with two <button>
elements, each of which calls tick
and reset
respectively.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 }}>{this.state.ticker}</span>
<button onClick={this.tick}>Tick!</button>
<button onClick={this.reset}>Reset</button>
</div>
);
}
ReactDOM.render(<Ticker times={5} interval={1000} />, document.getElementById('root'));
Renders a toggle component.
React.useState()
to initialize the isToggleOn
state variable to false
.style
, to hold the styles for individual components and their states.<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.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>
);
}
ReactDOM.render(<Toggle />, document.getElementById('root'));
Renders a tooltip component.
React.useState()
hook to create the show
variable and initialize it to false
.<div>
element that contains the <div>
that will be the tooltip and the children
passed to the component.onMouseEnter
and onMouseLeave
methods, by altering the value of the show
variable..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>
);
}
ReactDOM.render(
<Tooltip text="Simple tooltip">
<button>Hover me!</button>
</Tooltip>,
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.