Ad
How Pass A Function Into An Own React Context Provider Which Lets You Edit The State?
I have the following code:
import React, { useState } from 'react'
export const NumberContext = React.createContext()
export function NumberProvider({ children }) {
const [numbers, setNumbers] = useState([])
function addNumber(number) {
const copy = [...numbers]
copy.push(number)
setNumbers(copy)
}
return (
<NumberContext.Provider value={[numbers, addNumber]}>
{children}
</NumberContext.Provider>
)
}
export function App() {
return (
<NumberProvider>
<DisplayNumbers />
<AlterNumbers />
</NumberProvider>
)
}
export function DisplayNumbers() {
const [numbers, addNumbers] = useContext(NumberContext)
return (<p>{numbers}</p>)
}
If I now call the addNumber()
function via the Context api within the provider like this
export function AlterNumbers() {
const [numbers, addNumber] = useContext(NumberContext)
function alterNumbers(){
addNumber(1)
setTimeout(() => {
addNumber(2)
}, 3000)
}
return (<button onClick={alterNumbers}>)
}
numbers
is equal to [2]
instead of [1, 2]
The state in my provider function NumberProvider
does not update like the one in the NumberContext.Provider
. How can I prevent this from happening? How would a design pattern that accomplishes this look like?
Ad
Answer
Because setters of useState
are asynchronous, one call overrides the other:
const NumberConsumer = () => {
const [numbers, addNumber] = useContext(NumberContext);
addNumber(1); // Will update numbers to [1] in "future"
setTimeout(() => { // Will update numbers to [2] in "future"
addNumber(2); // The call within the timeout,
// is not aware of the previous state
}, 3000);
return <h1>{JSON.stringify(numbers)}</h1>;
};
You should use the functional useState
:
setState(prevState => {
// Object.assign would also work
return {...prevState, ...updatedValues};
});
As discussed in the comments, you should change the logic to something more robust with useEffect
:
export function ToastProvider({ children }) {
const [toasts, setToasts] = useState([])
const [key, setKey] = useState(0)
function showToast(label, message) {
setKey(key + 1)
setToasts([
...toasts,
{
key,
label,
message,
},
])
}
useEffect(() => {
setTimeout(() => {
const copy = [...toasts]
copy.pop()
setToasts(copy)
}, 5000)
}, toasts);
return (
<ToastContext.Provider value={[toasts, showToast]}>
{children}
</ToastContext.Provider>
)
}
Ad
source: stackoverflow.com
Related Questions
- → How to update data attribute on Ajax complete
- → October CMS - Radio Button Ajax Click Twice in a Row Causes Content to disappear
- → Octobercms Component Unique id (Twig & Javascript)
- → Passing a JS var from AJAX response to Twig
- → Laravel {!! Form::open() !!} doesn't work within AngularJS
- → DropzoneJS & Laravel - Output form validation errors
- → Import statement and Babel
- → Uncaught TypeError: Cannot read property '__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED' of undefined
- → React-router: Passing props to children
- → ListView.DataSource looping data for React Native
- → Can't test submit handler in React component
- → React + Flux - How to avoid global variable
- → Webpack, React & Babel, not rendering DOM
Ad