I do the weather app and need some help. In component Chart in options and series comes [object Object]. When you change something in the code, it is displayed. I think that the problem with useEffect? but I don't know how to fix that
import React, { useContext, useState, useEffect } from 'react';
import Chart from 'react-apexcharts';
import { Context } from '../../contex';
const WeatherGrapth = () => {
const {dailyForecast} = useContext(Context);
const [category, setCategory] = useState([])
const [data, setData] = useState([])
useEffect(() => {
const day = [];
const temp =[];
const items = dailyForecast.map((d) => {
const unixTimestamp = d.dt;
const getTemp = Math.round(d.temp.day)
let getDay = new Date(unixTimestamp* 3600 * 24 * 1000).getDate();
day.push(getDay)
temp.push(getTemp)
})
setCategory(day)
setData(temp)
}, []);
return(
<div>
<Chart options={{
chart: {
id: 'weather-graph'
},
xaxis: {
categories: category,
title: {
text: 'Date',
},
},
yaxis: {
title: {
text: 'Temperature °C',
},
},
}}
series={[{
name: 'temp',
data: data
}]} type="line" height={'349px'} />
</div>
)
}
export default WeatherGrapth;
But as soon as I change something in the code, everything will update and a graph will appear.
As React doc says:
By default, effect runs both after the first render and after every update
If you want to run an effect and clean it up only once (on mount and
unmount), you can pass an empty array ([]) as a second argument. This
tells React that your effect doesn’t depend on any values from props
or state, so it never needs to re-run
If you use this optimization, make sure the array includes all values from the component scope (such as props and state) that change over time and that are used by the effect. Otherwise, your code will reference stale values from previous renders.
Probably At first dailyForecast context is empty or has not any valid data and after that it fills with data you should pass it to useEffect as dependency to run the effect at changes:
const {dailyForecast} = useContext(Context);
const [category, setCategory] = useState([])
const [data, setData] = useState([])
useEffect(() => {
const day = [];
const temp =[];
const items = dailyForecast.map((d) => {
const unixTimestamp = d.dt;
const getTemp = Math.round(d.temp.day)
let getDay = new Date(unixTimestamp* 3600 * 24 * 1000).getDate();
day.push(getDay)
temp.push(getTemp)
})
setCategory(day)
setData(temp)
}, [dailyForecast]);
Related
so i have 2 redux state and selectors that is working well. but i want to call the second selector (get the detail list based on category.id) inside map() loop. how can i do that?
const Dashboard = () => {
const [data, setData] = useState([]);
const categories = useSelector(viewFinalCategories);
// categories is loaded well
const createFinalData = () => {
const finalData = categories.map((category) => {
return {
title: category.label,
category: category,
data: useSelector(viewInventoriesByCategory(category.id)), // <- error hook cannot called here..
};
});
setData(finalData);
};
useEffect(() => {
createFinalData();
}, [categories]);
return (
// SectionList of RN here...
)
Since it violates the hook rule, you can't call useSelector inside a function.
the solution is to get the data in the component level and do the filtering inside the function
const {inventories} = useSelector(state => state)
const createFinalData = () => {
const finalData = categories.map((category) => {
return {
title: category.label,
category: category,
data: inventories.filter((item) => item.idCategory === idCategory)
};
});
setData(finalData);
};
useSelector is a hook and it has to follow hook rules, one of them is it can't be used inside a loop. Instaed, you need move all this logic from component to yet another selector. I would use createSelector from reselect in this case since you can combine selecting categories into newly created selectFinalData:
import { createSelector } from 'reselect'
const selectFinalData = () =>
createSelector(
viewFinalCategories,
(state, categories) => categories.map((category) => ({
title: category.label,
category: category,
data: viewInventoriesByCategory(category.id)),
})
)
)
and use it in component :
const finalData = useSelector(selectFinalData())
setData(finalData);
Currently, I'm trying to use useState to change a value to an object.
However, the thing I've done doesn't make any errors.
So, I want to know if the way i use UseState is making issues later.
So, my code is like this :
this is where I make useState :
export const SettingContextProvider = props => {
const [title, setTitle] = useState({});
const [label, setLabel] = useState({});
}
and I pass to this component:
const DataSettingPage = () => {
const { layoutValue, chartValue, MainPage } = useContext(CommonStateContext);
const { SetTitle, SetLabel, SetDatas, SetLabels } = useContext(SettingContext);
const [changeLayout] = layoutValue;
const [chart] = chartValue;
const [title, setTitle] = SetTitle;
const [label, setLabel] = SetLabel;
{chart[index].key === "Bar" && (
<BarChart
title={title[id]}
label={label[id]}
/>
)}
and I'm using that state to here as a props :
import React from "react";
import { Bar } from "react-chartjs-2";
const BarChart = ({ title, label }) => {
let data = {
labels: [],
datasets: [
{
label: label,
data: [],
backgroundColor: ["#a7def8e1"],
},
],
};
return (
<>
<Bar
data={data}
options={{
responsive: true,
maintainAspectRatio: false,
plugins: {
title: {
display: true,
text: title,
color: "#345fbb",
font: { size: 22 },
},
},
}}
/>
</>
);
};
export default BarChart;
Thank you in advance
You don't need to destructure the passed value further in your child components.
these lines aren't needed:
const [title, setTitle] = SetTitle;
const [label, setLabel] = SetLabel;
Also, I didn't find SetDatas and SetLabels anywhere in your code, have you defined them?
Apart from these two issues, it is perfectly fine to define a state in a parent component and pass both the value and the function to the children:
Parent:
export const SettingContextProvider = props => {
const [title, setTitle] = useState({});
const [label, setLabel] = useState({});
return (<SettingContext.Provider value={[title,setTitle,label,setLabel]}>
<DataSettingPage/>
</SettingContext.Provider>
Child:
const DataSettingPage = () => {
const { layoutValue, chartValue, MainPage } = useContext(CommonStateContext);
const [title,setTitle,label,setLabel] =
useContext(SettingContext); // you can use them as needed
...
Are you calling React.createContext for your context objects? If not I believe your code may have issues as it's not the correct way to use useContext() according to React docs.
Your BarChart component looks like it's missing most of the data (only taking title and one label), so you should fill those in properly.
I would suggest to delete SettingContextProvider completely and simplify the DataSettingPage component as so:
const DataSettingPage = ({title, label}) => {
// I'm not sure what these are for so just leaving them here.
const { layoutValue, chartValue, MainPage } = useContext(CommonStateContext);
const [changeLayout] = layoutValue;
const [chart] = chartValue;
// DELETE
// const { SetTitle, SetLabel, SetDatas, SetLabels } = useContext(SettingContext);
const [title, setTitle] = useState(title)
const [label, setLabel] = useState(label)
{chart[index].key === "Bar" && (
<BarChart
title={title[id]} // <-- Where does your "id" come from??
label={label[id]}
/>
)}
At the moment, there is a component that is essentially copied 4 times. I would like to make it more abstract, and simply render it 4 times, and pass in the dynamic data each time. The data that's passed into each component are state hooks.
With that being the goal, could I get some help on the implementation?
Here's what a component call looks like in the parent:
const [allBlueItems, setAllBlueItems] = useState([]);
const [selectedBlueItems, setSelectedBlueItems] = useState([]);
const [allRedItems, setAllRedItems] = useState([]);
const [selectedRedItems, setSelectedRedItems] = useState([]);
<BlueSelection
allBlueItems={allBlueItems}
selectedBlueItems={setSelectedBlueItems}
setSelectedBlueItems={selectedBlueItems}
/>
<RedSelection
allRedItems={allRedItems}
selectedRedItems={setSelectedRedItems}
setSelectedRedItems={selectedRedItems}
/>
Then, the ItemSelection component uses those useState values passed in as props to render data, and updates the state accordingly:
const BlueSelection = ({ allBlueItems, selectedBlueItems, setSelectedBlueItems }) => {
useEffect(() => {
setSelectedBlueItems([]);
}
}, []);
Then I repeat myself and implement the exact same code to handle the RedItem
const RedSelection = ({ allRedItems, selectedRedItems, setSelectedRedItems }) => {
useEffect(() => {
setSelectedRedItems([]);
}
}, []);
Refactor
export default const Selection = () => {
const [allItems, setAllItems] = useState([]);
const [selectedItems, setSelectedItems] = useState([]);
return (
<Selection
allItems={allItems}
selectedItems={setSelectedItems}
setSelectedItems={selectedItems}
/>)}
import this wherever you need it like....
import Selection as RedSelection from ...
import Selection as BlueSelection from...
new to react so I am not quite sure what I am doing wrong here... I am trying to call data from an API, then use this data to populate a charts.js based component. When I cmd + s, the API data is called in the console, but if I refresh I get 'Undefined'.
I know I am missing some key understanding about the useEffect hook here, but i just cant figure it out? All I want is to be able to access the array data in my component, so I can push the required values to an array... ive commented out my attempt at the for loop too..
Any advice would be greatly appreciated! My not so functional code below:
import React, {useState, useEffect} from 'react'
import {Pie} from 'react-chartjs-2'
const Piegraph = () => {
const [chartData, setChartData] = useState();
const [apiValue, setApiValue] = useState();
useEffect(async() => {
const response = await fetch('https://api.spacexdata.com/v4/launches/past');
const data = await response.json();
const item = data.results;
setApiValue(item);
chart();
},[]);
const chart = () => {
console.log(apiValue);
const success = [];
const failure = [];
// for(var i = 0; i < apiValue.length; i++){
// if(apiValue[i].success === true){
// success.push("success");
// } else if (apiValue[i].success === false){
// failure.push("failure");
// }
// }
var chartSuccess = success.length;
var chartFail = failure.length;
setChartData({
labels: ['Success', 'Fail'],
datasets: [
{
label: 'Space X Launch Statistics',
data: [chartSuccess, chartFail],
backgroundColor: ['rgba(75,192,192,0.6)'],
borderWidth: 4
}
]
})
}
return (
<div className="chart_item" >
<Pie data={chartData} />
</div>
);
}
export default Piegraph;
There are a couple issues that need sorting out here. First, you can't pass an async function directly to the useEffect hook. Instead, define the async function inside the hook's callback and call it immediately.
Next, chartData is entirely derived from the apiCall, so you can make that derived rather than being its own state variable.
import React, { useState, useEffect } from "react";
import { Pie } from "react-chartjs-2";
const Piegraph = () => {
const [apiValue, setApiValue] = useState([]);
useEffect(() => {
async function loadData() {
const response = await fetch(
"https://api.spacexdata.com/v4/launches/past"
);
const data = await response.json();
const item = data.results;
setApiValue(item);
}
loadData();
}, []);
const success = apiValue.filter((v) => v.success);
const failure = apiValue.filter((v) => !v.success);
const chartSuccess = success.length;
const chartFail = failure.length;
const chartData = {
labels: ["Success", "Fail"],
datasets: [
{
label: "Space X Launch Statistics",
data: [chartSuccess, chartFail],
backgroundColor: ["rgba(75,192,192,0.6)"],
borderWidth: 4,
},
],
};
return (
<div className="chart_item">
<Pie data={chartData} />
</div>
);
};
export default Piegraph;
pull your chart algorithm outside or send item in. Like this
useEffect(async() => {
...
// everything is good here
chart(item)
})
you might wonder why I need to send it in. Because inside useEffect, your apiValue isn't updated to the new value yet.
And if you put the console.log outside of chart().
console.log(apiData)
const chart = () => {
}
you'll get the value to be latest :) amazing ?
A quick explanation is that, the Piegraph is called whenever a state is updated. But this update happens a bit late in the next cycle. So the value won't be latest within useEffect.
Having a state that changes after a while (after fetching content), if I want to construct a variable and build a new state from that, when the first state changes it is not propagated to the second state
How can I solve this? I wouldn't like to merge the 2 states into one since this would mix components that do different things
If you have time you can take a look at the codesandbox below
https://codesandbox.io/s/purple-sun-mx428?fontsize=14&hidenavigation=1&theme=dark
And I paste here the code
import React, { useEffect, useState } from "react";
import "./styles.css";
const wait = ms => new Promise((r, j) => setTimeout(r, ms));
const test2 = () => {
const [test, setTest] = useState("hi");
useEffect(() => {
const testf = async stillMounted => {
await wait(1000);
setTest("bye");
};
const stillMounted = { value: true };
testf(stillMounted);
return () => {
stillMounted.value = false;
};
}, []);
return test;
};
export default function App() {
const here = test2();
const stru = [{ id: "whatever", content: here }];
const [elements, setElements] = useState(stru);
console.log(stru);
console.log(elements);
return (
<div className="App">
<h1>stru: {stru[0].content}</h1>
<h2>elements: {elements[0].content}</h2>
</div>
);
}
You can write an effect that runs when the here variable changes. Add something like this to your App component.
useEffect(() => {
// This runs when 'here' changes and then updates your other state
setElements([{ id: "whatever", content: here }])
}, [here]) // This makes this effect dependent on the 'here' variable