React useQuery called multiple times when dropdown is opened - javascript

I am using "react-query" to call an API from a component . For the purpose of this question , I am returning a mock response from the API .
Every time , I open the dropdown , the useQuery function is called which in turn calls the mock API .
App.js
import React from 'react';
import './style.css';
import { QueryClient, QueryClientProvider } from 'react-query';
import { DropDown } from './Dropdown.js';
const queryClient = new QueryClient();
export default function App() {
return (
<QueryClientProvider client={queryClient}>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<DropDown />
</div>
</QueryClientProvider>
);
}
Dropdown.js
import React from 'react';
import { useQuery } from 'react-query';
export const DropDown = () => {
console.log('DropDown re-rendered');
const { data, isLoading, isError } = useQuery('API', () => {
return new Promise((resolve, reject) => {
console.log('API called');
resolve(['mockData']);
});
});
return (
<>
<select>
<option> 1 </option>
<option> 2 </option>
</select>
</>
);
};
You can find the demo here : https://react-quxtxd.stackblitz.io
In the console you will see that every time you open the dropdown , useQuery is called.
Stackblitz Editor url : https://stackblitz.com/edit/react-quxtxd?file=src/Dropdown.js
As an alternative to avoid this , I can use the traditional useEffect to make the API calls but I was looking at leveraging the caching advantage that useQuery provides but I am stuck due to this "re-rendering" issue .
Any suggestions / modifications ?

This works for me
{ refetchOnWindowFocus: false }
Usage:
const { data, status } = useQuery("users", fetchUsers, {
refetchOnWindowFocus: false
});

It seems that the original stackblitz has been fixed, so the issue is no longer reproducible. For posterity:
You've probably seen a background refetch due to focusing the window. This is because staleTime defaults to 0 and refetchOnWindowFocus defaults to true. You can either turn off the flag, or set a higher staleTime (recommended).

Related

React Query resends request after unfocusing React TinyMCE

Recently I've tried to create my own component using React Query with TinyMCE for React, but I noticed that every time I loose focus from TinyMCE editor's text area my request from React Query is sent again even though nothing has changed in my components (at least I think there is no need for rerender of the component).
Here you can see that every time I click into editor and after that outside of the editor the request is sent
I created project to simulate the problem. You can see that every time you focus and unfocus from the text area of the TinyMCE editor the request from React Query is sent. I tried to use useEffect to know if the provided callback is called multiple times as well, but the useEffect works as expected.
import React, { useEffect } from "react";
import {
QueryClient,
QueryClientProvider,
useQuery
} from "#tanstack/react-query";
import ReactDOM from "react-dom/client";
import { Editor } from "#tinymce/tinymce-react";
export default function MyEditor() {
return (
<div>
<Editor />
</div>
);
}
const fetchData = async () => {
console.log("Fetching data", new Date());
return await fetch("https://jsonplaceholder.typicode.com/posts/1").then((d) =>
d.json()
);
};
function App() {
useQuery(["api"], fetchData);
useEffect(() => {
console.log("This is going to be logged only once");
}, []);
return (
<form>
<h1>My editor</h1>
<MyEditor />
</form>
);
}
const queryClient = new QueryClient();
ReactDOM.createRoot(document.getElementById("container")).render(
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
);
These are my package versions:
{
"dependencies": {
"#tanstack/react-query": "4.3.9",
"#tinymce/tinymce-react": "4.2.0",
"react": "18.2.0",
"react-dom": "18.2.0"
}
}
I think it's because the editor renders an iframe, you can use the refetchOnWindowFocus option to avoid refetching:
useQuery(["api"], fetchData, { refetchOnWindowFocus: false })
or perhaps using the method to ignore iframe focus described in the same doc page
It works on codesandbox

Next.js renders element twice

On the first image you can see next.js rendered this element twice
I used tables and thought that it is because of them but then I tried to remove tables and put jut and it still renders twice so I don't know what it can be.
Next.js does not renders only that element but the first from this object
const Sections = {
1: Locations,
0: Departments, // here it will render this one twice
2: Managers,
3: JobTitles,
};
Maybe it has something to do with useState and my statemanagment in this code below
Component that renders twice.
const Locations = () => {
return <div>hdjsad</div>;
};
// Tab Sections
import Locations from ''
import Departments from ''
import Managers from ''
import JobTitles from ''
import Icons from "../../Icons";
import TabItem from "./TabItem";
const tabs_text = ["Locations", "Departments", "Managers", "Job Titles"];
const Sections = {
0: Locations, // THIS IS THE COMPONENT WHICH RENDERS TWICE
1: Departments,
2: Managers,
3: JobTitles,
};
const SettingsTab = () => {
const [active, setActive] = useState<number>(0);
const select = useCallback((id: number) => {
return () => setActive(id);
}, []);
const ActiveSection = useMemo(() => Sections[active], [active]);
return (
<section className={"mb-[24px]"}>
<header
className={"w-full flex items-center mb-[34px] pl-[24px] pr-[12px]"}
>
<div className={"flex space-x-[8px] !mb-0 overflow-x-scroll"}>
{tabs_text.map((tab_text, i) => {
return (
<div onClick={select(i)} key={i}>
<TabItem active={+active === i}>{tab_text}</TabItem>
</div>
);
})}
</div>
<ImportLocationsAndFilter />
</header>
<ActiveSection />
</section>
);
};
APP.js
import { AppProps } from "next/app";
import "antd/dist/antd.css";
import "../styles/global.css";
function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}
export default MyApp;
I can't comment yet so I'll do it here. I know react says in the official docs to never rely on UseMemo or Use callback for functionality. It says you should create your application so it works without them, and then add them for performance reasons. What would happen if you took the useMemo out and put
ActiveSelection = Selections[active]
I don't think it'll fix your problem but it might give you more insight into what's causing it.
I just imported my tabs dynamically and set SSR: false.
It has to do something with next.js hydration.
https://nextjs.org/docs/advanced-features/dynamic-import
dynamic(
() => import(""),
{
ssr: false,
}
);
It's strange behaviour / bug related to next.js ssr to fix it wrap your Component in a div like this:
function MyApp({ Component, pageProps }: AppProps) {
return <div id=#root><Component {...pageProps} /></div>;
}

How to return a list according to selected item?

I'm still a beginner in ReactJS and I'm creating a project that works with a list of pokemons. The user selects a type of pokemon, and then I must return a list according to the user's selection.
I have a list with all pokemons, but some pokemons can belong to more than one type, as shown in this example below:
Could you tell me how to create a list with only the type of pokemon that the user selected? I think I can do this using reducer(), but I have no idea how to do it.
Here's my code I put into codesandbox
import React from "react";
import { useHistory } from "react-router-dom";
import { Button } from "#material-ui/core";
import { types } from "./data";
import "./styles.css";
const App = () => {
const history = useHistory();
const handleType = (type) => {
history.push({
pathname: "/list",
state: type
});
};
return (
<div className="content">
<h3>Pokémon Types</h3>
{types.results.map((type) => (
<Button
key={type.name}
style={{
margin: "5px"
}}
variant="contained"
onClick={() => handleType(type.name)}
>
{type.name}
</Button>
))}
</div>
);
};
export default App;
import React from "react";
import { useLocation } from "react-router-dom";
import { pokemons } from "../data";
const List = () => {
const { state } = useLocation();
console.log("state: ", state);
console.log(pokemons);
return <div>List</div>;
};
export default List;
Thank you in advance for any help.
You have a lot of ways to do that, but since you are still learning and you got a nice shot of code, I will introduce useMemo for you:
you can add useMemo to memorize and process data, then get the result direct...
look at this example:
const pk = useMemo(() => {
if (!state) return "State Empty!";
let result = [];
pokemons.forEach((v, i) => {
if (v.type.includes(state)) {
result.push(<li key={v.name + i}>{v.name}</li>);
}
});
return result;
}, [pokemons, state]);
return <ul>{pk}</ul>;
By this code, I got your list, check details in a simple loop, and then retrieve the needed list...
Notes:
In key I set name and i, but it's not totally correct, but it seems there is duplication on data, and why its not totally correct?, since we need to make sure to prevent re-render when no real change, but index if order change that's mean re-render...
You can use anyway like reducer, filter, or create a separate component and put it nested of useMemo
You can enhance data style to can check or retrieve data fast by Hash table...
Demo

How do I call an Axios response once and without a button

I am new to using react and Axios and I have created a get request, I can call it once with a button, however I don't want this button and instead want information to be displayed when the page loads/with the page so the user can see it straight away. But when calling my function once it gets called continuously and crashes the web browser and I don't understand why this is happening I have googled and I couldn't find anything. Here is the code that gets ran.
kitchen.js
import React from 'react';
import { Container } from 'react-bootstrap';
// import Axios from 'axios';
import { Link } from 'react-router-dom';
import GetFood from './getFood';
export default function Kitchen() {
return(
<Container>
<div>
<h1>This is the kitchen portal</h1>
<Link to='/gettingfood'><button>Get Food</button></Link>
<Link to="/addingfood"><button>Add food</button></Link>
<Link to="/deletefood"><button>Delete Food</button></Link>
</div>
<GetFood/>
</Container>
);
}
GetFood.js
import React, { useState } from 'react';
import Axios from 'axios';
export default function GetFood() {
const [responseData, setResponseData] = useState([])
// fetches data
async function fetchData(){
await Axios.get("http://localhost:3001/getfood").then((response)=>{
setResponseData(response.data);
console.log(response.data);
alert("Information received!")
})
.catch((error) => {
console.log(error)
})
}
fetchData();
return (
<div>
<button onClick={fetchData}>Get</button>
{responseData.map((val,key)=>{
return (
<div>
<div id="data">
<p>Item:{val.item}</p>
<p>Price:{val.price}</p>
</div>
</div>
)
})}
</div>
)
}
In React, functional components get called everytime they get rendered.
To create side-effects, like requesting data from an external source, you should use the useEffect hook.
This hook takes a function to execute and a dependency array, which defines when the supplied function gets called.
If you specify an empty array, the function only gets called on the first render cycle.
If you specify any variables, the function gets called on the first render cycle and when any of the specified variables change.
This should go instead of your call to fetchData():
useEffect(() => {
fetchData();
}, []);

React-Native/Redux dispatch firing multiple times in action

I'm making a React/Redux app. In one of my actions, dispatch is firing 6-8 times when called for no apparent reason. See addMarkersRequestAddress below in the action file for my component:
export function addMarkersSuccess(response) {
return {
type: 'addMarkersSuccess',
status: 'success',
response: response,
receivedAt: Date.now(),
};
}
export function addMarkersFailure(error) {
return {
type: 'addMarkersFailure',
status: 'error',
error: error,
receivedAt: Date.now(),
};
}
export function addMarkersRequestCoordinates(submitFormData) {
// Why is this always returning addMarkersFailure? Is it possibly related to why it always fires multiple times?
// Same code as in virtualFenceWalk actions
return (dispatch) => {
console.log('running addMarkersRequestCoordinates');
console.log('submitFormData: ',submitFormData);
let JSONbody = JSON.stringify(submitFormData);
console.log('JSONbody: ',JSONbody);
fetch('http://localhost:8080/virtualFence', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSONbody
}).then(function(response){
dispatch(addMarkersSuccess(response));
}).catch(function(error) {
dispatch(addMarkersFailure(error));
});
}
}
export function addMarkersRequestAddress(submitFormData) {
return (dispatch) => {
console.log('running addMarkersRequestAddress');
console.log('submitFormData: ',submitFormData);
let JSONbody = JSON.stringify(submitFormData);
console.log('JSONbody: ',JSONbody);
// Make a request to a backend route that gets the coordinates from the Google Maps API
fetch('http://localhost:8080/virtualFenceAddress', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSONbody
}).then(function(response){
console.log('addMarkersRequestAddress success');
console.log('response: ',response);
dispatch(addMarkersSuccess(response));
}).catch(function(error) {
console.log('addMarkersRequestAddress failure');
console.log('error: ',error);
dispatch(addMarkersFailure(error));
});
}
}
When this code runs, addMarkersSuccess will fire 6-8 times. It is somehow related to dispatch specifically, because if I remove the dispatch calls and leave only the console logs, addMarkersSuccess fires once as expected and that's it. It also seems unrelated to fetch or asynchronicity since an identical outcome occurs if fetch is removed and the same thing is tried in the main body of the function.
Here is the container wrapping around the component (since I've narrowed it down to an issue with how dispatch is called, as without dispatch other parts of the action only fire once, maybe there is an issue with how dispatch is set up here?):
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, View, Text, TouchableOpacity, TouchableHighlight } from 'react-native';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import VirtualFence from '../components/VirtualFence';
import * as VirtualFenceActions from '../actions/virtualFence';
const styles = StyleSheet.create({
container: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
bottom: 0,
justifyContent: 'flex-end',
alignItems: 'center',
},
back: {
margin: 10,
fontSize: 20,
},
});
// Map the Redux state to props
#connect(
state => ({
bigState: state,
markers: state.markers,
}),
dispatch => bindActionCreators(VirtualFenceActions, dispatch),
)
export default class VirtualFenceContainer extends Component {
render() {
return (
<View style={styles.container}>
<VirtualFence {...this.props} />
</View>
);
}
}
Here is where the action is called in the component itself:
render() {
const {
addMarkersRequestAddress, addMarkersSuccess, addMarkersFailure
} = this.props;
return (
<View>
<TouchableOpacity onPress={this.toggleModal}>
<Text style={styles.bottomText}>Add markers by street address</Text>
</TouchableOpacity>
<Modal isVisible={this.state.isVisible}>
<View style={{ flex: 1 }}>
<TouchableOpacity onPress={this.toggleModal}>
<Text style={styles.bottomText}>Hide me!</Text>
</TouchableOpacity>
<Form
ref="form"
type={Points}
options={pointsOptions}
/>
<Button title="Add form field" onPress={this.addFormField}></Button>
<Button title="Delete form field" onPress={this.deleteFormField}></Button>
<Button
title="Submit markers"
onPress={(argument)=>addMarkersRequestAddress(this.refs.form.getValue())}
/>
</View>
</Modal>
</View>
);
}
While not answering my question, some other answers here and elsewhere seemed to hint that the resolution may have something to do with my configureStore.js file, so here it is:
/* eslint global-require: 0 */
import { Platform } from 'react-native';
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducers';
// Presumably I need to add the other action files here somehow? Nothing seems to change as long as one file is listed...
import * as actionCreators from './actions/activityTracker';
let composeEnhancers = compose;
if (__DEV__) {
// Use it if Remote debugging with RNDebugger, otherwise use remote-redux-devtools
/* eslint-disable no-underscore-dangle */
composeEnhancers = (window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ||
require('remote-redux-devtools').composeWithDevTools)({
name: Platform.OS,
...require('../package.json').remotedev,
actionCreators,
});
/* eslint-enable no-underscore-dangle */
}
const enhancer = composeEnhancers(applyMiddleware(thunk));
// I think the problem with multiple dispatches may be in here
// See https://stackoverflow.com/questions/49734848/redux-dispatch-fires-multiple-times
export default function configureStore(initialState) {
const store = createStore(reducer, initialState, enhancer);
if (module.hot) {
module.hot.accept(() => {
store.replaceReducer(require('./reducers').default);
});
}
return store;
}
Please note that I don't really know what this file is doing. I began the app using react-native-boilerplate so this file is taken from there. If changes need to be made there, it would be super appreciated if you can detail what exactly those changes do.
EDIT 1: When this post was originally written, all dispatches after the first threw errors. After some further work in other parts of the application, the additional firings all log successful now. However, the essential question (the cause of the multiple firings) remains.
EDIT 2: Added the container wrapping around the component.
The cause of my problem turned out to be in the file where I call the combineReducers helper function. I did not suspect this file had anything to do with the problem, so I had not posted it. For components with multiple keys in the initial state object, I incorrectly thought I had to do an import for each key, when in fact I needed a single import for each reducer file. I imported six variables from the virtualFence reducer, and each one caused dispatch to fire.
This is the incorrect version:
import { combineReducers } from 'redux';
import nav from './nav';
import virtualFence from './virtualFence';
import latitude from './virtualFence';
import longitude from './virtualFence';
import latitudeDelta from './virtualFence';
import longitudeDelta from './virtualFence';
import markers from './virtualFence';
export default combineReducers({
nav,
latitude,
longitude,
latitudeDelta,
longitudeDelta,
markers,
virtualFence,
});
And this is the correct version:
import { combineReducers } from 'redux';
import nav from './nav';
import virtualFence from './virtualFence';
export default combineReducers({
nav,
virtualFence,
});
are you using preventDefault() when calling event this might be the case:
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
use preventdefault to disallow to call method when page is loading
<Button title="Add form field" onPress={this.addFormField}></Button>
<Button title="Delete form field" onPress={this.deleteFormField}></Button>
<Button
title="Submit markers"
onPress={(argument)=>addMarkersRequestAddress(this.refs.form.getValue())}
/>
So you state:
addMarkersSuccess will fire once, followed by several firings of addMarkersFailure
addMarkersFailure only gets called when there is an error. This error, of course, contains all the information you need to solve the problem. In particular, it has a stack that indicates not only the exact place the error was fired, but a complete call stack that indicates the entire call chain leading up to the error.
When a Promise has several stages, each stage has an opportunity to fail. A catch following any of the stages will be passed the error.
So:
Promise.resolve('This is just a string, and no error')
.then(theString => {
throw new Error('This is not the original promise result, but a new one: A rejection.');
})
.catch(err => {
console.log('Something went wrong. Maybe it happened in the original promise.');
console.log('Maybe it happened later. To find out, look closely at this:');
console.log(err.stack);
});
In your case, it's probably dispatch that throws. Now, there's nothing wrong with dispatch itself, but when it goes to call your reducer, the reducer is probably doing something wrong and throwing an error. This in turn leads your .catch callback (aka the rejection handler) to be called.
Since you did not include your reducer code I can't point out the error in it. However, you should be able to find it by examining the error message and stack.
In your addMarkersRequestAddress action, try to return the dispatch in .then() like:
.then((response) => {
dispatch(addMarkersSuccess(response));
}).catch((error) => {
dispatch(addMarkersFailure(error));
});
Maybe this will work.

Categories

Resources