React-query mutation dosent refresh - node.js

I have another fetch of "SpecificList in another component, but when posts excutes it dosent refetch it only refetches on rewindowfocus? What could the reason be?
const { mutate,isLoading:isAdding } = useMutation((values)=>handleAddValue(values),
{
onSuccess:()=> queryClient.invalidateQueries("specificList")
},
)
async function handleAddValue(values){
return await ShoppingListAPI.post("/addToShoppingList",{
product:values.newValue,
list:values.listId
})
}
if(isAdding){ return <p>Loading...</p> }
if (isLoading) { return <p>Loading...</p> }
return (
<Box>
{data.products &&
<Autocomplete onChange={(event, newValue) => mutate({newValue,listId})
} options={data.products} getOptionLabel={(option) => option.name} renderInput={(params) => <TextField {...params} label="Products" />}></Autocomplete>
}
</Box>
)

What could the reason be?
hard to tell without seeing the whole code / a codesandbox reproduction. Some things to keep an eye on are:
matching query keys (upper case / lower case)
make sure the queryClient is stable: either created outside of your App component, or inside it on a state or ref instance. Otherwise, a re-render will create a new client, throwing away the cache.
maybe your mutation is not Successful, so onSuccess is not called.

Related

Update prop for dynamically inserted element

New to react... Really banging my head against it with this one... I'm trying to figure out how to get a dynamically inserted component to update when the props are changed. I've assigned it to a parent state object but it doesn't seem to re-render. I've read that this is what's supposed to happen.
I was using ReactDOM.unmountComponentAtNode to re-render the specific elements I needed to, but it kept yelling at me with red text.
I need to hide "chat.message" unless the user has the authority to see it (server just sends empty string), but I still need to render the fact that it exists, and reveal it should the user get authentication. I'm using a css transition to reveal it, but I really need a good way to update the chat.message prop easily.
renderChats(uuid){
let userState = this.state.userStates.find(user => {
return user.uuid === uuid;
});
const children = userState.chats.map((chat) => {
let ChatReactElement = this.getChatMarkup(chat.cuid, chat.message, chat.status);
return ChatReactElement;
});
ReactDOM.render(children, document.getElementById(`chats-${this.state.guid}-${uuid}`));
}
getChatMarkup() just returns JSX and inserts Props... I feel like state should be getting passed along here. Even when I use a for-loop and insert the state explicitly, it doesn't seem to re-render on changes.
getChatMarkup(cuid, message, status){
return(
<BasicChatComponent
key={cuid}
cuid={cuid}
message={message}
status={status}
/>
);
}
I attempted to insert some code line this:
renderChats(uuid){
let userState = this.state.userStates.find(user => {
return user.uuid === uuid;
});
const children = userState.chats.map((chat) => {
let ChatReactElement = this.getChatMarkup(chat.cuid, chat.message, chat.status);
if(chat.status.hidden)
this.setState({ hiddenChatRE: [ ...this.state.hiddenChatRE, ChatReactElement ] }); // <== save elements
return ChatReactElement;
});
ReactDOM.render(children, document.getElementById(`chats-${this.state.guid}-${uuid}`));
}
and later in my code:
this.state.hiddenChatRE.every(ReactElement => {
if(ReactElement.key == basicChats[chatIndex].cuid){
ReactElement.props = {
... //completely invalid code
}
}
});
The only response I see here is my ReactDOM.unmountComponentAtNode(); approach...
Can anyone point me in the right direction here?
Although perhaps I should be kicking myself, I read up on how React deals with keys on their components. So there's actually a fairly trivial answer here if anyone comes looking... Just call your render function again after you update the state.
In my case, something like:
this.setState(state =>({
...state,
userStates : state.userStates.map((userstate) => {
if(userstate.uuid == basicChats[chatIndex].uuid) return {
...userstate,
chats: userstate.chats.map((chat) => {
if(chat.cuid == basicChats[chatIndex].cuid){
//
return {
cuid: basicChats[chatIndex].cuid,
message: basicChats[chatIndex].message,
status: basicChats[chatIndex].status
}
}
else return chat;
})
}
else return userstate;
})
}));
and then, elsewhere in my example:
this.state.userStates.map((userstate) => {
this.renderChats(userstate.uuid);
});
Other than the fact that I'd recommend using indexed arrays for this example to cut complexity, this is the solution, and works. This is because even though it feels like you'd end up with duplicates (that was my intuition), the uid on the BasicChatComponent itself makes all the difference, letting react know to only re-render those specific elements.

React array[0] is undefined despite the array existing when I console log it

I have the code below
import React from 'react';
import axios from 'axios'
//let termExample = "https://en.wikipedia.org/w/api.php?action=query&origin=*&format=json&generator=search&gsrnamespace=0&gsrlimit=5&gsrsearch='New_England_Patriots'";
function APIThing({ term }) {
let wikioutput = []
axios.get(term).then((response) => {for (let i in response.data.query.pages) if(wikioutput) wikioutput.push(response.data.query.pages[i])})
function safe(x){
console.log("test", x[0], x)
if (x[0]){
console.log("test2", x[0], x[0].title)
return x[0].title
}
}
return (
<>
<p>TEST {safe(wikioutput)} TEST</p>
<p>EXAMPLE {safe([{title: "Test"}])}</p>
</>
)
}
export default APIThing;
When term is a wikipedia api url, I cannot return the value of wikioutput[0]. When I log wikioutput (or x in this case) I can see the entire array of objects, but x[0] returns undefined
I came across this post when I looked for an explanation for my very similar problem. I also have an asynchronous API call that gives me an array with several objects.
`useEffect(() => {
async function fetchMailContent() {
const response = await fetch(insertapiwebsitehere.com;
if (open) {
try {
const res = await response.json();
setMailContent(res);
setLoading(false);
} catch (err) {
console.error(err);
setError(true);
setLoading(false);
}
}
}
fetchMailContent();
}, [open]);`
The array I get from this is then given to another function where I attempted to place a value of one of the array's objects into a textfield component.
`export function MainTab(){
[shortened]
return(
<TextField variant={'outlined'} type="text" value={array[0].field_entry} />
)
}`
This would then result in the error of thejonymyster: array[0] is undefined. But a console.log(array[0]) would work fine and gave me the right object.
The fix is indeed rooted in the asynchronicity. You need to ask first if the object value exists before rendering it. This can be done with a simple ? after the array[0] and fixed the issue for me. So the Textfield component looks like this:
<TextField variant={'outlined'} type="text" value={array[0]?.field_entry} />
You should really be using hooks + useEffect in ReactJS that will most likely fix your problem, you can find examples here
what i ended up doing was json stringifying the array and regexing for the first element. json stringify array didnt give empty, miraculously. if anyone knows why array existed but array[0] didnt, id love to know someday. link to this post

How can I store an API call response as an object or parse an array as a JSON?

My app is trying to make an API call and display the data from it in a modal. It isn't working because I'm storing the response from the API call as an array at the moment, meaning I can't access the sub-elements of the response.
My question, then: is there some way to either:
Store an object using State so that I can conveniently reference parts of that object?
OR
Parse an array as a JSON so I can take the data from the response and process it as a JSON when needed?
I realise this is kind of a weird question, so any answer that would achieve the same result would also be great.
Here's my code:
const DrinkPopup = (props) => {
let [drinkDetails,setDrinkDetails] = useState([])
let selectedDrinkId = props.drink
const getDrinkAsync = async (selectedDrinkId) => {
try {
let response = await fetch('https://www.thecocktaildb.com/api/json/v1/1/lookup.php?i='+selectedDrinkId);
let data = await response.json();
setDrinkDetails(data.drinks)
return true;
} catch (error) {
console.error(error);
}
}
useEffect (() => {
getDrinkAsync(selectedDrinkId)
console.log(selectedDrinkId)
console.log(drinkDetails)
},[]);
return(
<Modal isVisible={props.modalVisible}
onBackdropPress={()=>{props.setModalVisible(false)}} //allows closing modal by tapping outside it or back button
onBackButtonPress={()=>{props.setModalVisible(false)}}
animationIn={"slideInUp"}>
<View style={styles.infocard}>
<View style={styles.titleBox}>
<Text style={styles.header}>{drinkDetails.idDrink}</Text>
</View>
</View>
</Modal>
)
}
A really easy way to convert your array to an object is to use Object.assign like so:
Object.assign({}, data.drinks)
It would return an object with numbered keys and you can store that to state.

React-native and Redux healthy way to call actions on props change

I've been using react-native with redux for a while, and the way i learn to call actions when something change on prop is using the componentWillReceiveProps, but when I use it I need to pass between if's and some times it goes to the wrong if, then I need to add more stuff to prevent it.
Here's an example I have done. I know this is not the best way to do it, but it is what I could think of.
componentWillReceiveProps(newProps) {
if(Object.keys(newProps.selected_product).length > 0) {
if(Object.keys(this.props.current_location).length > 0 || Object.keys(newProps.current_location).length > 0) {
this._handleNextPage(2);
this.props.verifyProductById(newProps.selected_product, newProps.current_location, this.props.token);
} else {
this.props.statusScanner(false);
this._handleNextPage(1);
}
} else if(Object.keys(newProps.historic_product_confirm).length > 0) {
if(newProps.historic_product_confirm.location._id == newProps.current_location._id)
this.props.handleModalConfirmPrice(!this.props.modal_confirmPrice_status)
} else if(newProps.scanResult != "") {
this.props.statusScanner(false);
if(Object.keys(newProps.current_location).length > 0) {
this._handleNextPage(2);
} else {
this._handleNextPage(1);
}
} else {
this._handleNextPage(0);
}
}
What I need is a healthy way to call my actions when the props change.
Edit:
Here i have the full OfferScene and an action file example:
OfferScene:
https://gist.github.com/macanhajc/0ac98bbd2974d2f6fac96d9e30fd0642
UtilityActions:
https://gist.github.com/macanhajc/f10960a8254b7659457f8a09c848c8cf
As mentioned in another answer, componentWillReceiveProps is being phased out, so I would aim for trying to eliminate it where possible. You'll be future-proofing your code and keeping your component logic more declarative and easy to reason about. As someone who has been responsible for (and been frustrated by) lifecycle method abuse like this, here are some things that have helped me.
Remember that when using redux-thunk, along with passing dispatch as the first argument, you can also pass getState as the second. This allows you to access state values in your action logic instead of bringing them into your component's props and adding clutter. Something like:
export const ExampleAction = update =>
(dispatch, getState) => {
const { exampleBool } = getState().ExampleReducer
if (exampleBool) {
dispatch({
type: 'UPDATE_EXAMPLE_STATE',
update
})
}
}
Using async/await in action logic can be a lifesaver when your action depends upon fetched results from an API call:
export const ExampleAction = () =>
async (dispatch, getState) => {
const { valueToCheck } = getState().ExampleReducer
, result = await someAPICall(valueToCheck)
.catch(e => console.log(e))
if (result.length > 0) {
dispatch({
type: 'UPDATE_EXAMPLE_STATE',
update: result
})
}
}
For cases where your component's rendering behavior depends upon certain state values after your state has been updated, I highly recommend reselect. A very basic example would be something like:
component.js
import React, { Component, Fragment } from 'react'
import { connect } from 'react-redux'
import { shouldDisplayItems } from '../selectors'
import MyListviewComponent from './myListview'
class ItemList extends Component {
render() {
const { shouldDisplayItems, items } = this.props
return (
<>
{shouldDisplayItems && <MyListviewComponent items={items} />}
</>
)
}
}
const mapStateToProps = ({ ListItems }) => shouldDisplayItems(ListItems)
export default connect(mapStateToProps)(ItemList)
selectors.js:
(Assuming your ListItems reducer has the params items and visibilityFilter)
import { createSelector } from 'reselect'
export const shouldDisplayItems = createSelector(
[state => state],
({ items, visibilityFilter }) => {
return {
shouldDisplayItems: visibilityFilter && items.length > 0,
items
}
}
)
I should mention that another option would be using higher-order components, but it can be tricky to use this approach before having a good grasp on how to keep too much imperative logic out of your components (I learned this the hard way).
I agree with #AnuragChutani and #Goldy in terms of clarity of the code; break it down some more into more components or functions.
Now after some review of your componentWillReceiveProps function, it is definitely not specific enough to narrow down exactly which prop changes. If any connected redux variable changes, the componentWillReceiveProps function will be invoked each time.
So e.g. if 'token' or 'selected_product' updates, componentWillReceiveProps will be triggered, even though you did not want it to trigger for token updates.
You can use a comparison for a specific variable update in the props.
E.g Using lodash
if(!_.isEqual( nextProps.selected_product, this.props.selected_product ))
// if props are different/updated, do something
Secondly, you can call actions/callbacks in your actions to narrow down navigation.
E.g.
takePicture = (camera, options){
...
//on success
dispatch(handleModalConfirmPrice())
...
}}

Using React Virtualized CellMeasurer with changing data order for List and dynamic heights

I am trying to create a chat application using React Virtualized. So far everything is working but I think think I am doing something wrong when using keyMapper and rowRenderer from the list.
The cache is using the id to store the height in the _rowHeightCache but it seems that the heights are looked up using the index and not the id. Im not sure if I should be passing the id as the rowIndex to CellMeasurer to get the right heights or something of that nature.
The issue is that the heights are wrong due to the changing order of the messages list so the index's don't have the proper heights and also that messages can be removed messing up the index orders. This I think should be fixed by using keyMapper to look up the heights but i must be doing it wrong.
The following is an edited down version of how I am using it.
constructor(props) {
this._cache = new CellMeasurerCache({
defaultHeight: 45,
fixedWidth: true,
keyMapper: (index) => _.get(this.props.messageList[index], ['msgId'])
});
}
render() {
const props = this.filterProps(this.props),
list = this.props.messageList,
rowCount = list.length;
return (
<InfiniteLoader
threshold={0}
isRowLoaded={this._isRowLoaded}
loadMoreRows={this._loadMoreRows}
rowCount={rowCount}>
{({onRowsRendered, registerChild}) => (
<AutoSizer>
{({width, height}) => (
<List
ref={(ref) => {
this.streamLane = ref;
registerChild(ref);
}}
height={height}
width={width}
onRowsRendered={onRowsRendered}
rowCount={rowCount}
rowRenderer={this._rowRenderer}
deferredMeasurementCache={this._cache}
rowHeight={this._cache.rowHeight}
/>
)}
</AutoSizer>
)}
</InfiniteLoader>
);
}
_rowRenderer({ index, key, parent, style }) {
return (
<CellMeasurer
cache={this._cache}
columnIndex={0}
key={key}
parent={parent}
rowIndex={index}>
<div style={{...style}}>
{this.props.messageList[index]}
</div>
</CellMeasurer>
);
}
_loadMoreRows({startIndex, stopIndex}) {
return new Promise((resolve) => {
if (!!stopIndex || !!startIndex || this.props.hasLastMessage || this.state.isPinned) {
return resolve();
}
this.props.loadOlder(() => resolve);
});
}
_isRowLoaded({index}) {
if (index === 0) return false;
return !!this.props.messageList[index];
}
}
Any help, suggestions or criticisms would be amazing.
The issue is that the heights are wrong due to the changing order of the messages list so the index's don't have the proper heights and also that messages can be removed messing up the index orders.
Your example code doesn't show how the order changes or what you do after it changes, so it's hard to say what you're doing wrong.
But when the order changes (eg you sort the data, or you add new items to the beginning of the list causing older items to shift) then you'll need to let List know that its cached positions may be stale. Do do this in your case, you'd need to call this.streamLane.recomputeRowHeights().
(You can pass the index of the first changed item to recomputeRowHeights but I assume in your case that maybe all items have shifted? I don't know, since I don't have all of your code.)
After you call recomputeRowHeights, List will re-calculate the layout using the heights reported to it by CellMeasurerCache. That cache uses your keyMapper function:
rowHeight = ({index}: IndexParam) => {
const key = this._keyMapper(index, 0);
return this._rowHeightCache.hasOwnProperty(key)
? this._rowHeightCache[key]
: this._defaultHeight;
};
So it won't need to remeasure items, even though their indices have changed.

Resources