I am trying to build a react app and have run into a bug I can't seem to fix. I have a context file with a days
variable in it and a data
object that depends on number of days. it looks like this:
const DataContext = createContext({ days: 720, data: { timestamp: [], price: [], short_ma: [], long_ma: [], money: [], action: [] }, daysChangeHandler: (newDays) => { } })
export default DataContext
export const DataContextProvider = (props) => {
const daysChangeHandler = (newDays) => {
setDays(newDays, getNewData())
}
const getNewData = () => {
getData(days).then(response => setData(response))
console.log('NEW DATA')
}
const [days, setDays] = useState(720)
const [data, setData] = useState({ timestamp: [], price: [], short_ma: [], long_ma: [], money: [], action: [] })
return <DataContext.Provider
value={{ days: days, data: data, daysChangeHandler: daysChangeHandler }}>
{props.children}
</DataContext.Provider>
}
getData
triggers a call to a database and gets the data back, it works I tested it.
The app is wrapped in the dataContextProvider
.
When a button is clicked, it looks like this:
export default function NumberOfDays(props) {
const context = useContext(DataContext)
const predefinedDaysHandler = (days) => {
context.daysChangeHandler(days)
}
return <button className='submit-button' onClick={() => predefinedDaysHandler(0)}>Max</button>
}
Finally, this should be displayed in a rendered component looking like this:
export default function DataChart(props) {
const context = useContext(DataContext)
console.log(context.data)
return //rendered data here, the updated data should be displayed but is only on the second time I click the submit button
}
The problem is that I have to click twice on the submit button to get the updated data value?
I have tried using useEffect
in the DataContextProvider
:
useEffect(()=>{
getNewData(days)
}, [days])
But it changes nothing.
Also, when I press the submit button the first time, the console.log('NEW DATA')
gets triggered, but the data somehow does not get updated in the component that should display my data before the second click.
To be really clear, the data that is displayed is always the data from just before the last update. How do I change this?
EDIT
Tried to replace daysChangeHandler
with:
const daysChangeHandler = (newDays) => {
setDays(newDays)
getNewData()
}
but it doesn't change anything.
CodePudding user response:
The Problem
Your setState
method is not working properly since you are passing two parameters as to it.
The Solution:
in the DataContextProvider
:
const DataContext = createContext({ days: 720, data: { timestamp: [], price: [], short_ma: [], long_ma: [], money: [], action: [] }, daysChangeHandler: (newDays) => { } })
export default DataContext
export const DataContextProvider = (props) => {
const daysChangeHandler = (newDays) => {
setDays(newDays)
getData(days).then(response => setData(response))
}
const [days, setDays] = useState(720)
const [data, setData] = useState({ timestamp: [], price: [], short_ma: [], long_ma: [], money: [], action: [] })
return <DataContext.Provider
value={{ days: days, data: data, daysChangeHandler: daysChangeHandler }}>
{props.children}
</DataContext.Provider>
}
Optional
If you want to use a callback function in your setDays
to get the new Data with the previous implementation:
const daysChangeHandler = (newDays) => {
setDays(newDays, () => getNewData())
}
const getNewData = () => {
getData(days).then(response => setData(response))
console.log('NEW DATA')
}
Also, you can pass the value object toDataContext.Provider
in a shorter way since the key
and value
are equals:
return (
<DataContext.Provider
value={{ days, data, daysChangeHandler }}>
{props.children}
</DataContext.Provider>
)
CodePudding user response:
I make a mockup that is specific to your use case
My implementation is this
You may go to CodeSandbox to interact my Demo. But the code is just below
context.tsx file
import { createContext, useState } from "react";
const mockUp = {
days: 720,
data: {
timestamp: [],
price: [],
short_ma: [],
long_ma: [],
money: [],
action: []
},
daysChangeHandler: (newDays) => {}
};
const DataContext = createContext(mockUp);
export default DataContext;
export const DataContextProvider = (props) => {
const [days, setDays] = useState(720);
const [data, setData] = useState(mockUp);
const daysChangeHandler = (newDays) => {
getNewData();
setDays(newDays);
};
const getNewData = () => {
// getData(days).then(response => setData(response))
// getData(days) mock up
Promise.resolve(mockUp).then((res) =>
setData(() => ({ ...res, days: 370 }))
);
console.log("NEW DATA");
};
return (
<DataContext.Provider
value={{ days: days, data: data, daysChangeHandler: daysChangeHandler }}
>
{props.children}
</DataContext.Provider>
);
};
DataChart.tsx
import { useContext, useEffect } from "react";
import DataContext from "./context";
export default function DataChart(props) {
const { data } = useContext(DataContext);
useEffect(() => {
console.log("data chart", data);
}, [data]);
return null; //rendered data here, the updated data should be displayed but is only on the second time I click the submit button
}
NumberOfDays.tsx
import { useContext } from "react";
import DataContext from "./context";
export default function NumberOfDays(props) {
const context = useContext(DataContext);
const predefinedDaysHandler = (days) => {
context.daysChangeHandler(days);
};
return (
<button className="submit-button" onClick={() => predefinedDaysHandler(0)}>
Max
</button>
);
}
App.tsx file
import { DataContextProvider } from "./context";
import DataChart from "./DataChart";
import NumberOfDays from "./NumberOfDays";
export default function App() {
return (
<DataContextProvider>
<DataChart />
<NumberOfDays />
</DataContextProvider>
);
}
CodePudding user response:
the solution is to call useEffect
and set days
as a dependency inside of context:
useEffect(() => {
getData(days).then(response => setData(response))
}, [days]);```