I'm trying to make a hook for showing errors. Below is the simplified solution.
The useError function takes a factory for elements given errors and returns a proper React element factory for rendering.
My problem is that useError creates a new factory each time it's called, which it needs to because it closes over the current errors. But this results in ErrorsDisplay being mounted on each error added and thus it's state reset (can be seen in the sample, when you click the app to add an error).
const useErrors = factory => {
let [error, setError] = useState([]);
const addError = e => setError(s => [...s, e]);
return [props => factory(error), addError];
};
const ErrorDisplay = props => {
let [someState, setSomeState] = useState(0);
useEffect(() => {
let timeout = setTimeout(() => setSomeState(s => s + 1), 1000);
return () => clearTimeout(timeout);
}, [someState]);
return (
<div>
{someState} + {props.errors.length}
</div>
);
};
export default function App() {
let [Error, add] = useErrors(errors => <ErrorDisplay errors={errors} />);
return (
<div className="App" onClick={() => add("my error")}>
<Error />
</div>
);
}
For a playground see https://codesandbox.io/s/magical-star-k529l.
My only "solution" so far is to keep the instance of the errors array in useError and push new errors onto it (and then force a rerender by setting some other state too) ala:
const useErrors = factory => {
let [error] = useState([]);
let [token, render] = useState(true);
const addError = e => {
error.push(e);
render(!token)
}
return [useCallback((props) => factory(error), []), addError];
};
I don't find the very idiomatic and I don't know if this gives me any other issues down the line.
My question is: Is there a way to make this work without mutable state, preferably with the same api for useError or something along those lines?