How to fix map is not function even there is already set data in the useState using React Hook - reactjs

Right now I am creating a chart and the chart data is based on the backend api created. Now I have problem where I need to push all the amounts in the array. So by doing this I need of course map the state and push it on the array however there is error happen it says that
TypeError: receiveGroupCount.map is not a function. I don't know why this happen.
Error:
Response:
State:
const [receiveCheckCount, setReceiveCheckCount] = useState();
const [receiveGroupCount, setReceiveGroupCount] = useState({
});
API:
let list_filter_countings_url = process.env.BASEURL+"chart_filter_range/"+moment(selectionRange.startDate).format("YYYY-MM-DD")+"/"+moment(selectionRange.endDate).format("YYYY-MM-DD");
axios.get(list_filter_countings_url, { headers: { 'Authorization': AuthToken, 'Accept': 'application/json', 'Content-Type': 'application/json' } })
.then(res => {
console.log(res);
if(res) {
const count_receive_check = res.data.data.receive;
const count_receive_grouped = res.data.data.data_receive_grouped;
setReceiveCheckCount(count_receive_check);
setReceiveGroupCount(count_receive_grouped);
}
})
.catch((error) => {
});
Chart Parameters:
useEffect(() => {
setChartData({
labels: dataDate(selectionRange),
datasets: [
{
label: 'Receive Check',
backgroundColor: 'rgb(195 218 251 / 35%)',
borderColor: '#70a8f3',
pointBackgroundColor: "#fff",
pointBorderWidth: '2',
fill: true,
lineTension: 0.0,
pointRadius: 7,
borderWidth: 1,
data: randomVal(),
//data:[12, 19, 3, 5, 2, 3]
}
]
})
},[selectionRange,receiveGroupCount,receiveCheckCount])
const randomVal = () => {
var randomArr = []
var from = new Date(selectionRange.startDate);
var to = new Date(selectionRange.endDate);
console.log(receiveGroupCount)
if(receiveGroupCount != null) {
for (var day = from; day <= to; day.setDate(day.getDate() + 1)) {
receiveGroupCount.map((rows) => {
randomArr.push(rows.amount)
})
}
return randomArr
}
}

It looks like you're initializing that state to an empty object:
const [receiveGroupCount, setReceiveGroupCount] = useState({});
Objects don't have a .map property. Try initializing to an empty array instead.

receiveGroupCount is not null initially, it is {}(an empty object) because of this statement:
const [receiveGroupCount, setReceiveGroupCount] = useState({ });
.map() is a method for arrays, and although you might have an array like data later in receiveGroupCount, the first time your randomVal() is run, it will fail.
You are already trying to mitigate the issue by checking for null but if you do not want to change your initial state you can change your if check like this:
if(receiveGroupCount != {}) {
for (var day = from; day <= to; day.setDate(day.getDate() + 1)) {
receiveGroupCount.map((rows) => {
randomArr.push(rows.amount)
})
}
return randomArr
}
Note: Your useEffect will run the first time and after every render where any of the selectionRange,receiveGroupCount,receiveCheckCount changes.

You just simply have to create a state with the default variable
try:
const [receiveGroupCount, setReceiveGroupCount] = useState({
labels: // your code here,
datasets: []
});

Related

ReactJS: TypeError: Cannot read properties of undefined (reading 'reduce')

I'm having a problem here when creating a date range picker with chartjs.
So the case is :
When the user has just logged in, the user opens the "HITAPI" page to view the chart by date.
Then the user filters the date according to his wishes, For example, between the 1st and 13th of February.
When finished selecting the date, on the web a white blank appears instead.
When it is reloaded again and opens the HITAPI page once again and selects a date, data will appear that corresponds to the selected date (and no white blanks appear again).
When I look at the console, it shows (TypeError: Cannot read properties of undefined (reading 'reduce')).
Guess what's the error? Can you guys help me? Thank you
My Code =
const [openModal, setOpenModal] = useState(false);
const defEndDate = new Date().toLocaleDateString("en-US");
const StartDate = useRecoilValue(startDate);
const EndDate = useRecoilValue(endDate);
const setStartDate = useSetRecoilState(startDate);
const setEndDate = useSetRecoilState(endDate);
const [dataKey, setDataKey] = useState([]);
const GetData = async () => {
try {
const result = await getHitApiTotal(StartDate, EndDate);
setDataKey(result.data[1]);
console.log(result.data[1]);
} catch (error) {
console.log(error);
}
};
const hasil = Object.values(
dataKey.reduce((acc, curr) => {
if (acc[curr["#timestamp"].split("T")[0]] == null) {
acc[curr["#timestamp"].split("T")[0]] = {
message: curr.message,
timestamp: curr["#timestamp"].split("T")[0],
count: 0,
};
}
acc[curr["#timestamp"].split("T")[0]].count++;
return acc;
}, {})
);
const labels = hasil.map((item) => item.timestamp);
const data = {
labels,
datasets: [
{
data: hasil.map((item) => item.count),
borderColor: "rgb(255, 99, 132)",
backgroundColor: "rgba(255, 99, 132, 0.5)",
pointRadius: 6,
xAxisID: "x1",
},
],
options: {
plugins: {
legend: {
display: false,
},
},
},
};
useEffect(() => {
if (defEndDate !== EndDate) {
GetData();
}
}, [EndDate, StartDate, defEndDate]);
for more details, you can see the video link that I wrote below:
https://va.media.tumblr.com/tumblr_rq0olbfygO1zb5h2t.mp4

How do i stop a dependency from re-rendering infinite times in a useEffect?

I have a react-select multiselect component which is required to have preselected values based on the user role. The react-select component is used to filter data in a react-table.
I have 2 user roles - Dev and tester.
If it the dev, I need to have open and Reopened issues to be filtered on load
If it is a tester, I need to have resolved issues on load
This is a part of the code that i am using to achieve preselected
async function loadInfo() {
const body = {
project_id: projDetails.id,
};
const response = await axios({
method: "post",
url: apilist.dropdownData,
data: body,
})
.catch((err) => console.log(err));
if (response) {
const getData = response.data.data;
// console.log("IsGeneralInfo:", getData)
setGeneralInfo(getData);
let filteredenv = getData.status.filter((item) => item.id == 8 || item.id == 6)
let envfiltered = filteredenv.map((k) => {
return ({ label: k.values, value: k.values });
})
// console.log("envfilter", envfiltered);
// handleMultiStatusFilter(envfiltered);
}
}
// const {current:myArray}=useRef([{ label: 'Closed', value: 'Closed' }])
useEffect(() => {
if(envfilter){
let myArray=[{ label: 'Closed', value: 'Closed' },{ label: 'Reopen', value: 'Reopen' }];
handleMultiStatusFilter(myArray);
}
}, [selectedOptions])
const handleStatusFilter = (e) => {
setFilterValue(e);
if (e.length > 0) {
dispatch(filterByValue({ type: e, viewIssue: viewIssue, }))
}
else {
dispatch(showAllStatus({ type: 'All', viewIssue: viewIssue, }))
}
}
const handleMultiStatusFilter = (e) => {
setFiltered([])
let arr = []
e.map((data) => {
setFiltered(prevState => [...prevState, data.value]);
arr.push(data.value);
})
setSelectedOptions(e)
handleStatusFilter(arr)
}
This is a part of the redux code used for filtering
extraReducers: (builder) => {
// Add reducers for additional action types here, and handle loading state as needed
builder.addCase(fetchIssueList.fulfilled, (state, action) => {
// Add user to the state array
state.issuesList = {
status: 'idle',
data: action.payload.data.data !== undefined ? action.payload.data.data : [],
dataContainer: action.payload.data.data !== undefined ? action.payload.data.data : [],
no_of_records: action.payload.data.data !== undefined ? action.payload.data.data.length : 0,
error: {}
}
})
The code works fine with the filtering once i login, but the rerendering keeps going to infinite loop
Is there any way i could stop the infinite rerendering of code and have the filtering happen on load of the screen?
while working with dependencies in useEffect, try to use the most primitive part you can find. no complex objects, as they change way too fast.
for example: use the length of an array not the array itself.
even though for arrays it's mostly safe to use itself.
sorry. correction: for arrays it's not safe either. complex objects are compared by reference not by value. for that you need primitive types like number, string or boolean.

Using UseState to Reset Value Before Adding Some

I couldn't make the setMajor and setMinor work inside the function.
What happen is, it just overwrite some of the data inside major and keeps on adding data inside minor
I use this code to clear all the value inside the major but it's kinda messy and it looks like I'm not using the useState function properly.
major.forEach((tk) => {
tk['material'] = ''
tk['required'] = 0
tk['actual'] = 0
tk['difference'] = 0
tk['crit'] = 0
tk['percent'] = 0
})
The handleSelectInput is a select tag that whenever I change the value it would send a request to the server to fetch the correct value for the major and minor variables
const majorStates = [
{
tank: 1,
material: '',
required: 0,
actual: 0,
difference: 0,
crit: 0,
percent: 0,
},
{
tank: 2,
material: '',
required: 0,
actual: 0,
difference: 0,
crit: 0,
percent: 0,
},...10],
[major, setMajor] = useState(tankStates),
[minor, setMinor] = useState([]),
const handleSelectInput= async (e) => {
const { name, value } = e.target
setMajor(majorStates) // not working
setMinor([]) // not working
await axios
.put(`${PATH}/main-start`, { product: value })
.then((res) => {
for (let i = 0; i < res.data.length; i++) {
//See if data has a type major or minor
//ParseFloat for decimal that's been returned as str
if (res.data[i].type === 'MAJOR') {
major[i].material = res.data[i].material
major[i].required = parseFloat(res.data[i].required_wt)
setMajor([...major])
} else {
res.data[i].required_wt = parseFloat(res.data[i].required_wt)
setMinor([...minor, res.data[i]])
}
}
})
.catch((err) => {
console.log(err)
})
}
I have a majorStates because it is required that the 10 value is shown even if the fetch data is less than 10.
Sorry if it's a noob question
Issue
Based upon enqueueing a bunch of state updates in a loop I'm going to guess that what isn't working is that only the last enqueued state update is what you are seeing. This is caused by starting from the same stale state each loop iteration.
You are also mutating state, major[i].material = res.data[i].material is mutating the major element at index i for example.
Solution
Use functional state updates to update from the previous state, not the state from the previous render cycle. Shallow copy the array and then also shallow copy any nested state/elements that are being updated.
const handleSelectInput= async (e) => {
const { name, value } = e.target
setMajor(majorStates) // reset
setMinor([]) // reset
await axios
.put(`${PATH}/main-start`, { product: value })
.then((res) => {
for (let i = 0; i < res.data.length; i++) {
//See if data has a type major or minor
//ParseFloat for decimal that's been returned as str
if (res.data[i].type === 'MAJOR') {
setMajor(major => major.map((el, index) => index === i ? {
...el,
material: res.data[i].material,
required: parseFloat(res.data[i].required_wt)
} : el);
} else {
res.data[i].required_wt = parseFloat(res.data[i].required_wt)
setMinor(minor => [...minor, res.data[i]])
}
}
})
.catch((err) => {
console.log(err)
})
}

State outside of my useCallback is different to the one that is inside

console.log('outside', currentPageNumber); // 0 then 3.
const fetchMoreItems = useCallback(
page => {
const { from, to } = dateModifier(selectedMonth);
const params = {
from,
to,
limit: ITEMS_PER_PAGE,
page: 3,
};
console.log('inside', currentPageNumber); // 0
if (selectedTab[ISSUES]) dispatchUserSuggestions({ ...params, type: 'issue' });
if (selectedTab[SUGGESTIONS]) dispatchUserSuggestions({ ...params, type: 'suggestion' });
},
[dispatchUserSuggestions, selectedTab, selectedMonth],
);
I need the currentPageNumber to be the new version of the state rather than the old one. I've tried adding it as a dependency to the useCallback but this puts me into an infinite loop.
Don't suppose anyone can tell what is going on?
Ideally you should add the currentPageNumber as a dependency, and solve the infinite loop. The code that causes the loop doesn't appear in your example.
If you can't, you can use a ref as an escape hutch:
const currentRef = useRef();
console.log('outside', currentPageNumber);
useEffect(() => {
currentRef.current = currentPageNumber;
}, [currentPageNumber]);
const fetchMoreItems = useCallback(
page => {
const { from, to } = dateModifier(selectedMonth);
const params = {
from,
to,
limit: ITEMS_PER_PAGE,
page: 3,
};
console.log('inside', currentRef.current);
if (selectedTab[ISSUES]) dispatchUserSuggestions({ ...params, type: 'issue' });
if (selectedTab[SUGGESTIONS]) dispatchUserSuggestions({ ...params, type: 'suggestion' });
},
[dispatchUserSuggestions, selectedTab, selectedMonth],
);

How can I see state within a function? using hooks

I'm trying to update the uploadFiles state inside my updateFile function, when reloading the file, I'm rewriting this component in hooks, but inside the function the state is given as empty.
const [uploadedFiles, setUploadedFiles] = useState({
slides: [],
material: [],
});
const updateFile = useCallback(
(id, data) => {
const value = uploadedFiles.slides.map(uploadedFile => {
return id === uploadedFile.id
? { ...uploadedFile, ...data }
: uploadedFile;
});
console.log('value', value);
console.log('uploadedFilesOnFunction', uploadedFiles);
},
[uploadedFiles]
);
function processUpload(upFile, type) {
const data = new FormData();
data.append('file', upFile.file, upFile.name);
api
.post('dropbox', data, {
onUploadProgress: e => {
const progress = parseInt(Math.round((e.loaded * 100) / e.total), 10);
updateFile(upFile.id, {
progress,
});
},
})
.then(response => {
updateFile(upFile.id, {
uploaded: true,
id: response.data.id,
url: response.data.url,
type,
});
})
.catch(response => {
updateFile(upFile.id, {
error: true,
});
});
}
function handleUpload(files, type) {
const uploaded = files.map(file => ({
file,
id: uniqueId(),
name: file.name,
readableSize: filesize(file.size),
preview: URL.createObjectURL(file),
progress: 0,
uploaded: false,
error: false,
url: null,
type,
}));
setUploadedFiles({
slides: uploadedFiles.slides.concat(uploaded),
});
uploaded.forEach(e => processUpload(e, type));
}
console.log('slides', uploadedFiles);
I expected the state values to be viewed by the function. For me to manipulate and set the state.
There might be other issues, but one thing I've noticed is:
const [uploadedFiles, setUploadedFiles] = useState({
slides: [],
material: [],
});
// A setState CALL FROM THE useState HOOK REPLACES THE STATE WITH THE NEW VALUE
setUploadedFiles({
slides: uploadedFiles.slides.concat(uploaded),
});
From: https://reactjs.org/docs/hooks-state.html
State variables can hold objects and arrays just fine, so you can still group related data together. However, unlike this.setState in a class, updating a state variable always replaces it instead of merging it.
The setState from the useState hook doesn't merge the state. Because it can hold any type of value, not only objects, like we used to do with classes.
From your code you can see that you're erasing some property from state when you're updating like that.
Instead, you should use the functional form of the setState and access the current state prevState, like:
setUploadedFiles((prevState) => {
return({
...prevState,
slides: uploadedFiles.slides.concat(uploaded)
});
});
The updated updateFiles function:
const updateFile = (id, data) => {
setUploadedFiles(prevState => {
const newSlide = prevState.slides.map(slide => {
return id === slide.id ? { ...slide, ...data } : slide;
});
return {
...prevState,
slides: newSlide,
};
});
};

Resources