// Toast/Snack import { NoSsr, Snackbar } from "@material-ui/core"; import { Alert } from "@material-ui/lab"; import React, { useEffect, useState } from "react"; export default function Snack(props) { const { message, severity = "success" } = props const [open, setOpen] = React.useState(null); useEffect(() => { setOpen(!!message) console.log('eff') console.log(open, message, severity) }, [props]) // const handleClick = () => { // setOpen(true); // }; const handleClose = (event, reason) => { if (reason === 'clickaway') { return; } setOpen(false); }; return ( {message} ) } export const ToastContext = React.createContext({}) export const ToastProvider = ({ children }) => { const [props, setSnackProps] = useState({ message: null, severity: 'success' }) const value = { snackProps: props, setSnackProps, } useEffect(() => { let timer if (props.message) { window.clearTimeout(timer) timer = window.setTimeout(() => { console.log('self-clearing snack') setSnackProps({}) }, 6100) } return (() => { window.clearTimeout(timer) }) }, [!!props.message]) return {children} } // essential app //HabitContextProvider START const [habits, setHabits] = useState([]) //the shape of HabitContextProvider value const AppCtxValue = { habits, //in-memory store fetchHabits: () => { const habits = axios.get('/habits') setHabits(habits) }, updateHabit: async (id, date, enabled) => { const response = await axios.post(`/habits/${id}`, {date, enabled}) await AppCtxValue.fetchHabits() return response }, } //HabitContextProvider END //the shape of ToastProvider value const ToastProviderCtxValue = { setToast(message, errLevel /* warning|errror|info */) } // usePromiseState.js //or, better yet, use https://www.npmjs.com/package/react-promise-hooks instead const usePromiseState = (promiseFn, lazy=false) => { const [data, setData] = useState(undefined) const [error, setError] = useState(undefined) const doIt = async (...args) => { //reset error setError(undefined) setData(undefined) let response, error try { response = await promiseFn(...args) setData(response) } catch (e) { error = e setError(error) //maybe normalize the error? } return {data:response, error} } React.useEffect(()=>{ if(!lazy){ doIt() } },[]) return { doIt, data, error, } } const HabitsPage = () => { const AppCtxValue = useContext('appCtx') const ToastProviderCtxValue = useContext('toastCtx') const { data, error } = usePromiseState( AppCtxValue.fetchHabits) if (error) { ToastProviderCtxValue.setToast(error.message, 'ERROR') return
failed to load
} if (!data) { return
loading...
} return
hello {JSON.stringify(data)}
} const HabitsCard = ({ habit }) => { const ToastProviderCtxValue = useContext('toastCtx') const {doIt } = usePromiseState(AppCtxValue.updateHabit, true) const toggleHabit = async () => { const { data, error, } = await doIt(habit.id, habit.date, !habit.enabled) if (!data) { ToastProviderCtxValue.setToast('Loading...', 'INFO') } if (error) { ToastProviderCtxValue.setToast(error.message, 'ERROR') } } return
hello {JSON.stringify(data)}
} //index.js ReactDOM.render( , document.getElementById('root'));