Official Solution
import React, { useEffect, useReducer, useRef, useState } from 'react';
function reducer(state, action) {
if (action.type === 'add') return [{ id: action.id, text: action.text }, ...state];
if (action.type === 'remove') return state.filter((t) => t.id !== action.id);
return state;
}
export default function App() {
const [toasts, dispatch] = useReducer(reducer, []);
const [text, setText] = useState('Saved to meetcode');
const timers = useRef(new Map());
useEffect(() => {
return () => {
for (const id of timers.current.values()) clearTimeout(id);
timers.current.clear();
};
}, []);
function addToast() {
const t = text.trim();
if (!t) return;
const id = Date.now().toString(36) + Math.random().toString(36).slice(2);
dispatch({ type: 'add', id, text: t });
const timerId = setTimeout(() => {
dispatch({ type: 'remove', id });
timers.current.delete(id);
}, 2400);
timers.current.set(id, timerId);
}
return (
<div style={{ padding: 16 }}>
<h2 style={{ marginTop: 0 }}>meetcode toasts</h2>
<div style={{ display: 'flex', gap: 10 }}>
<input value={text} onChange={(e) => setText(e.target.value)} style={{ flex: 1, padding: '10px 12px', borderRadius: 12, border: '1px solid #bbb' }} />
<button type='button' onClick={addToast} style={{ padding: '10px 14px', borderRadius: 12, border: 0, background: '#0b5', color: '#fff' }}>Show</button>
</div>
<div style={{ position: 'fixed', right: 16, top: 16, display: 'grid', gap: 10, width: 280 }}>
{toasts.map((t) => (
<div key={t.id} style={{ border: '1px solid #eee', background: '#fff', borderRadius: 14, padding: 12, boxShadow: '0 6px 20px rgba(0,0,0,0.08)' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', gap: 10 }}>
<div style={{ color: '#124', fontWeight: 700 }}>{t.text}</div>
<button type='button' onClick={() => dispatch({ type: 'remove', id: t.id })} style={{ border: 0, background: 'transparent', fontSize: 16, lineHeight: 1 }} aria-label='Dismiss'>×</button>
</div>
</div>
))}
</div>
</div>
);
}
No comments yet. Start the discussion!