What I'm trying to do is fetch an array of objects, then fetch data related to the first object in the array.
const progress = useSelector(state => state.progress);
const appInfo = useSelector(state => state.appInfo);
const {
data: steps,
isLoading: isStepsLoading,
isSuccess: isStepsSuccess
} = useGetAllStepsQuery()
const {
data: questions
} = useGetQuestionsQuery({app_id: appInfo.id, step_parameter: steps[progress.currentStepIndex].step_parameter}, {skip: isStepsLoading})
The problem I'm having is that attempting to extract the step_parameter from the steps array is throwing a Cannot read properties of undefined (reading '0') error: in other words, it's as if the useGetQuestionsQuery is being called before the useGetAllStepsQuery is finished. I've added a skip parameter to the request, per this answer, and tried various values with it (e.g. !isStepsSuccess), none of them resolve this problem. I've also tried assigning the step_parameter conditionally, like this:
step_parameter: isStepsSuccess ? steps[progress.currentStepIndex].step_parameter : ''
Which does prevent the error, but it also appears to prevent the questions call from ever being made. Any help would be appreciated!
Related
I have set 2 arrays with useState in 2 different files:
const [filterQuery, setFilterQuery] = React.useState({
instructor: [],
material: [],
})
const [query, setQuery] = React.useState({
instructor: [],
material: [],
})
And i have this function that will update query with values from filterQuery:
const applyFilter = (filterQuery) => {
setQuery((prev) => ({
...prev,
instructor: filterQuery.instructor,
material: filterQuery.material,
}));
}
The strange behavior is when i call setFilterQuery from a different function, an example is updating filterQuery when a button is clicked
onClick = (value) => {
setFilterQuery({
...filterQuery,
instructor: value
})
}
What I expected is it will just update filterQuery, but query will magically updated with filterQuery too whereas it has no connection to the function whatsoever.
What could trigger this ? I have tried a few things, declaring a new variable first before using the setQuery :
const filter = filterQuery;
setQuery((prev) => ({
...prev,
instructor: filter.instructor,
material: filter.material,
}));
not working.
I have also tried changing the set format without the prev, using just ...query or without them and still not working.
Thank you for reading, helps appreciated.. Will provide more code info / what else I have tried if asked.
In JavaScript primitive types are passed around as values: meaning that each time a value is assigned, a copy of that value is created.
On the other side objects (including plain objects, array, functions, class instances) are references. If you modify the object, then all variables that reference that object are going to see the change.
Here what basically happens is that you are passing the reference of query to filterQuery, not the value to query that's why when you change either query or filterQuery they both get updated.
You can see more details and examples at this question's answer and difference between values and reference
I'm trying to map "historicData" and put them in the labels of chart but it gives me error,"u cannot map the undefined".
So,Error is historicData is undefined .When I console.log(historicData),there is an array data.
This is my Code.
https://github.com/Saithiha24/React-Crypto-Beast/blob/master/src/Components/CoinInfo.js
I try to solve this for 2 days still can't find the answer. Please help me.
I think the problem is that historicData is initially undefined, and while waiting for the async call to complete and give historicData a value, you get the error. I suggest you change this
const [historicData, setHistoricData] = useState();
to this
const [historicData, setHistoricData] = useState([]);
so historicData is originally an empty array instead of undefined. You can also change the definition of data likes this
const data = {
labels: historicData ? historicData.map((coin) => {
//your code
}) : []}
so that if historicData is undefined, you don't try to map, just assign an empty array to labels.
I have no idea why I'm getting this error or from where is coming from because I think I'm not using that ?
I'm doing a firebase update after an user update a row from DataGrid MUI and I'm doing the update as I would normally do nothing different at all and it just jumps into that error.
I'm not sure if is an React error, JS error, Firebase error, MUI error. but I THINK is a firebase error because the path says so
This is what I was trying to do:
const [editRowsModel, setEditRowsModel] = React.useState({});
const [editRowData, setEditRowData] = React.useState({});
const handleEditRowsModelChange = React.useCallback(
(model) => {
const editedIds = Object.keys(model);
if (editedIds.length === 0) {
console.log(editRowData)
console.log(editRowData.uid)
console.log(editRowData.nombre)
console.log(editRowData.colegio)
console.log(editRowData.grado)
const temporalQuery = db.collection("usuarios").doc(user.uid).collection("estudiantes").doc(editRowData.uid);
temporalQuery.update({
nombre: editRowData.nombre,
colegio: editRowData.colegio,
grado: editRowData.grado
})
} else {
setEditRowData(model[editedIds[0]]);
}
setEditRowsModel(model);
},
[editRowData]
);
This is what the console.log shows up. I honestly don't see any error in the way I code it that's how I always do it, never had an issue before. First time I update from a nested collection though
This is how it looks in the firebase
And yes the user.uid also comes correctly
Found the issue is because the data comes with a whole string of information instead of just the value as I though it was coming.
All I had to do was to call the field + the value when I was using it.
Ej:
if I wanted the uid I had to call it editRowData.uid.value
the .doc(name) only accepts strings, so check if your user.uid is a string
I cant figure out why my setStock function is not updating the state and not causing a re-render, while I have several other functions working just fine.
const addToStockOperation = async (addOperation) => {
const payload = {
...
};
const jwtToken = {
...
};
const addToStockOperationResult = await axios.put(`${apiEndpoint}/stock/addtoitem`, payload, jwtToken);
setStock((prevStock) => {
const indexOfModifiedStock = prevStock.findIndex((stock) => stock._id === addOperation.id);
console.log(prevStock[indexOfModifiedStock].operations.added.length);
prevStock[indexOfModifiedStock].operations.added = addToStockOperationResult.data.operations.added;
console.log(prevStock[indexOfModifiedStock].operations.added.length);
return prevStock;
});
};
Both console logs confirm that the modification of prevStock did happen, as the second console.log shows a length of +1 compared to the previous length, so that indicates that the desired part of prevStock was indeed updated, however, a re-render is not caused.
I have also tried making a copy of prevStock const stockCopy = {...prevStock}; and modifying the copy and returning the copy, but no change.
I have also tried simply to return 1; just to see if a re-render will get triggered, still nothing.
I have a few other similar functions that are working just fine and are causing a re-render as expected:
This one is working just fine for setting products:
const setProductsWrapper = async (product) => {
const addProductResult = await axios.post(
`${apiEndpoint}/product/one`,
payload,
token
);
addProductResult.data.name === product.name &&
setProducts((prevProducts) => [addProductResult.data, ...prevProducts]);
};
EDIT: I found the issue, silly me, stock is an array return [...stockCopy]; after modifying the copy, worked.
Returning prevStock is never going to work because it is the current state array (i.e. has reference equality with it) - you need to return a new array for a new render to be triggered. However, it seems likely that an issue is also arising with mutated state.
You're on the way there when you create the copy const stockCopy = [...prevStock], but the problem is that this only copies the state array to one level of depth. Any objects nested inside it, like .operations, will retain their reference equality to the objects in the original state array.
Mutating them directly means that when you return your copy, any effects which rely on a difference in reference equality between these sub-objects will not run because they are already equal. There is no diff-ing to be done.
To fix this you will have to deeply copy the relevant parts of the tree:
setStock((prevStock) => {
const stockCopy = [...prevStock];
const stockIndex = stockCopy.findIndex((stock) => stock._id === addOperation.id);
stockCopy[stockIndex] = {
...stockCopy[stockIndex],
operations: {
...stockCopy[stockIndex].operations,
added: addToStockOperationResult.data.operations.added
}
};
return stockCopy;
});
State mutation sandbox
This can get quite annoying (and potentially expensive) when the data structure is large enough. It's always better to avoid structures like this in immutable state if you can help it. Of course that's often not the case and there are tools to help deal with immutability that can cut down on bloated code if it starts to become an issue.
Given a reducer example like the following
_({
expandAbility: (state, a: { which: string }) => ({
...state,
report: state.report && {
waitingForIt: false,
content: state.report.content && {
...state.report.content,
interaction: {
expandAbilities: !state.report.content.interaction.expandAbilities.contains(a.which)
? state.report.content.interaction.expandAbilities.add(a.which)
: state.report.content.interaction.expandAbilities.remove(a.which)
}
}
}
}),
})
(state type given below just for question context purposes)
const initialState = {
report: undefined as
| {
waitingForIt?: boolean
content?: {
supportedFightIds: number[]
deathCut: number
reportInfo: any
deathsFull: any
deathsByPlayer: any
deathsByAbility: any
interaction: {
expandAbilities: Set<string>
}
}
}
| undefined,
error: undefined as Error | undefined
}
Is there any kind of trick or "flavor-of-the-moment" library which would allow me to write a reducer update operation like expandAbility in a shorter way? (besides maybe creating some vars to reference inner paths)
There are lots of immutable update utilities out there, check out some options at https://github.com/markerikson/redux-ecosystem-links/blob/master/immutable-data.md#immutable-update-utilities and see what would be the best fit for you.
For starters check out Immutability-helper or immer.
So there are two things you could do to help simplify this. The first thing I like to do is move the logic out of the reducer and instead just pass in a value and say set expandAbilities to action. expandAbilities.
The second is actually something we do at work. We use immutableJS and wrote a single reducer that handles all of our state calls because you can give it a path of the parts of state that need to be updated and the value to update it with so we extracted that out and now it is easy to say dispatch(actions.update({path: ['report', 'content', 'interaction', 'expandAbilities'], value: '123' }))
You can even expand this so you can pass in a list of values that need to be updated and even preform validations around the data.