I'm trying to reset my state by calling the APi to get the latest data. The API call returns an array of object.
export const launchesState = atom<Launch[]>({
key: 'launchesStateLatest',
default: selector<Launch[]>({
key: 'launchesStateInit',
get: async () => {
const response = await getLaunches();
return response.data.map(launch => ({ ...launch, isSelected: false }));
},
}),
});
I'm using a selectorFamily to select each object when the list is rendered.
export const launchState = selectorFamily<Launch | undefined, string>({
key: 'launchState',
get:
missionName =>
({ get }) => {
return get(launchesState).find(item => item.mission_name === missionName);
},
set:
missionName =>
({ get, set }) => {
const currentState = get(launchesState);
const index = currentState.findIndex(item => item.mission_name === missionName);
set(
launchesState,
produce(currentState, draft => {
draft[index].isSelected = !currentState[index].isSelected;
}),
);
},
});
The UI contains a checkbox for each item in the array and when click it uses the set function in the selectorFamily ti update the launchesState.
I'd like to refresh the data using:
useRecoilRefresher_UNSTABLE(launchesState)
Which works ok if the data has never been altered, or is reset using useResetRecoilState(launchesState), but if I have clicked any of the checkboxes and altered the state then the state isn't refreshed.
Can someone help me understand why this is happening, is it a bug or is this happening for a reason?
Related
I am having a problem assigning data to useState by fetching data using reference type value from firebase.
const [preOil, setPreOil] = useState([]);
const [oilChange, setOilChange] = useState([]);
useEffect(() => {
getDocs(query(collection(db, "oil"), orderBy("timestamp"))).then(
(snapshot) => {
setPreOil(
snapshot.docs.map((doc) => ({
id: doc.id,
...doc.data(),
}))
);
}
);
}, []);
useEffect(() => {
let current = preOil.length > 0 ? [...preOil] : [];
current.map((_oil, i) => {
getDoc(_oil.oilReference).then((oilRef) => {
current[i].oilReference = oilRef.data();
});
});
setOilChange(current);
}, [preOil]);
In the first useEffect, the data is fetched successfully in this form,
preOil = {
id:"RxbAOAIs8d3kGOHNskJ4",
oilReference: Ta {converter: null, _key: ut, type: 'document', firestore: Na},
customerName: "USAMA",
}
In the second useEffect based on [preOil], I need to reassign the oilReference with the data fetched from firestorm through its reference(oilReference), The data is successfully fetched from the database and assigned to current but The main problem is when I set to state setOilChange(current) it updates my oilChange state when I inspect in inspect tools in chrome but in JSX the changes don't reflect
I am updating the state in useEffect
I am having desired data assigned in a local variable and assign that variable to the state
Then What am I missing?
In your second useEffect(), more specifically, in
current.map((_oil, i) => {
getDoc(_oil.oilReference).then((oilRef) => {
current[i].oilReference = oilRef.data();
});
});
setOilChange(current);
You are mutating the content of the current variable. This mutation, because it is async, will happen after the setOilChange call. Such mutation will thus not trigger a re-render.
What you need is to instead first wait for all the docs to be loaded and only after that set the state. Example:
const _docs = current.map((_oil, i) => {
return getDoc(_oil.oilReference).then((oilRef) => { // changed here
return { // changed here
...current[i], // changed here
oilReference: oilRef.data() // changed here
} // changed here
}); // changed here
});
Promise.all(_docs).then(() => {
setOilChange(_docs);
});
Also notice I didn't mutate current, rather I returned a new object. This is not mandatory but just a best practice overall.
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.
So after the content is loaded in the website I'm trying to call a function to fill [fields] (State) with the events but something Isn't working.
Here is what I mean:
const [events, setEvents] = useState([]);
useEffect(() => {
if (orders.orders !== null && orders.isLoading !== true)
orders.orders.map((order) =>
setEvents([
...events,
{
title: `Meeting with ${order.customer.firstName}`,
date: `${order.appointment}`,
},
])
);
setLoading(false);
}, [orders]);
So when I console log the events, I get [] (empty), but if i console.log the object it works.
I'm not sure what you are trying to achieve with the code but looks like your useEffect has a missing dependency of events. Since you are also updating events with setEvents, you should update with
setEvents(prev=>[
...prev,
{
title: `Meeting with ${order.customer.firstName}`,
date: `${order.appointment}`,
}
])
to avoid an infinite loop.
You are directly updating state so it is shown in console but in react rerender should be done to update state so pass parameter for setEvents and spread parameter but not directly events because you cannot directly update state.
const [events, setEvents] = useState([]);
useEffect(() => {
if (orders.orders !== null && orders.isLoading !== true)
orders.orders.map((order) =>
setEvents(prev => [
...prev,
{
title: `Meeting with ${order.customer.firstName}`,
date: `${order.appointment}`,
},
])
);
setLoading(false);
}, [orders]);
(state) => state?.clientAddress?.getAddress
);
useEffect(() => {
dispatch(GET_CLIENT_ADDRESS({}))
}, [dispatch]);
const onSavedAddressSelect = (addressId) => {
debugger
if (addressId) {
const { data: selectedAddress = [] } = clientAddress ?? {} ;
console.clear()
console.log(selectedAddress)
const addressObj = selectedAddress?.filter(
(addressItem) => addressItem?.value === addressId
)?.[0];
setAddress(addressObj);
setSelectedVenue(addressObj?.value);
}
props.drawer.setDrawer({
open: false,
});
};
My clientAddress inside onSavedAddressSelect is not getting updated with latest redux state even if I dispatch on first render on useEffect.
Please help.
isn't clientAddress is not updated because you call
dispatch(GET_CLIENT_ADDRESS({})) with empty object, so it's stays the same? Try to pass there some object with values
Here useVideos() give us all videos form database. After adding a new video the new entry is not append in the Material UI table , but if I refresh the page then it's showing that new entry. Now I want to show this new entry after add operation. Please help me to do this! Thanks in Advance.
const initialState = [{}];
const reducer = (state, action) => {
switch (action.type) {
case "videos":
const data = [];
let cnt = 1;
action.value.forEach((video, index) => {
data.push({
sl: cnt,
noq: video.noq,
id: index,
youtubeID: video.youtubeID,
title: video.title,
});
cnt = cnt + 1;
});
return data;
default:
return state;
}
};
export default function ManageVideos() {
const { videos, addVideo, updateVideo, deleteVideo } = useVideos("");
const [videoList, dispatch] = useReducer(reducer, initialState);
useEffect(() => {
dispatch({
type: "videos",
value: videos,
});
}, [videos]);
const columns = [
{ title: "Serial", field: "sl" },
{ title: "Title", field: "title" },
{ title: "Number of Qusetion", field: "noq" },
{ title: "Youtube Video Id", field: "youtubeID" },
];
return (
<div>
<MaterialTable
title="Videos Table"
data={videoList}
columns={columns}
editable={{
onRowAdd: (newData) =>
new Promise((resolve, reject) => {
setTimeout(() => {
addVideo(newData);
resolve();
}, 1000);
}),
onRowUpdate: (newData) =>
new Promise((resolve, reject) => {
setTimeout(() => {
updateVideo(newData);
resolve();
}, 1000);
}),
}}
/>
</div>
);
}
Since the information provided is a bit lacking, I'll assume that the useEffect hook is not working when you update your videos (check it with consle.log("I'm not working") and if it does work then you can just ignore this answer).
You can define a simple state in this component, let's call it reRender and set the value to 0, whenever the user clicks on the button to add a video you should call a function which adds 1 to the value of reRender (()=>setReRender(prevState=>prevState+1)). In your useEffect hook , for the second argument pass reRender. This way, when the user clicks to submit the changes , reRender causes useEffect to run and dispatch to get the new data.
If this doesn't work , I have a solution which takes a bit more work. You will need to use a state manager like redux or context api to store your state at a global level. You should store your videos there and use 1 of the various ways to access the states in this component (mapStateToProps or store.subscribe() or ...). Then pass the video global state as the second argument to useEffect and voilĂ , it will definitely work.