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
Related
Im new in react.
I'm Created two file App.js and UseEffect.js
I'm Learn about lifecycle in react with function.
So When I See in console, that's render multiple time.
You can see my picture below.
My Console In Browser
This Is My Code
UseEffect.js
import React, {useState, useEffect} from "react";
function MyFunction(){
console.log('-> Function Init')
const [count, setCount] = useState(0)
const handleCount = () => {
setCount(prevState => {
return prevState+1
})
}
//LifeCycle
useEffect(() => {
console.log('my first effect')
})
console.log(`-> Start Render (${count})`)
return(
<div>
<h1>Function Component</h1>
<p>
<button onClick={handleCount}>Count</button>
{count}
</p>
</div>
)}
export default MyFunction
App.Js
import './App.css';
import UseEffect from './components/UseEffect'
function App() {
return (
<div className="App">
<UseEffect />
</div>
);
}
export default App;
How do it's work?, I Want it. it's just render one times.
Your useEffect call is missing a dependency array. When you want it to run only at the initial render, you need to pass it an empty array as its dependencies.
useEffect(() => {
console.log('my first effect')
}, [])
For further details, see this question.
Why it renders twice:
It's an intentional feature of the StrictMode. This only happens in development, and helps find accidental side effects put into the render phase. We only do this for components with Hooks because those are more likely to accidentally have side effects in the wrong place.
-gaearon
TLDR: It's a feature not a bug.
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).
I have a rails (7.0.2) application and just installed React. I'm very new to react and can't seem to understand why it looks like my component is loading multiple times, the first time with an empty value for props and the second time with the correct values for props.
App.js:
import "./App.css";
import axios from "axios";
import Customers from "./components/customers";
import { useEffect, useState } from "react";
const API_URL = "http://localhost:3000/internal_api/v1/customers";
function getAPIData() {
return axios.get(API_URL).then((response) => response.data);
}
function App() {
const [customers, setCustomers] = useState([]);
useEffect(() => {
let mounted = true;
getAPIData().then((items) => {
if (mounted) {
setCustomers(items);
}
});
return () => (mounted = false);
}, []);
console.log('LOADED App.js');
return (
<div className="App">
<h1>Hello</h1>
<Customers customers={customers} />
</div>
);
}
export default App;
and customers.js:
import React from "react";
function Customers(props) {
console.log('LOADED customers.js');
return (
<div>
<h1>These customers are from the API</h1>
{props.customers.data.map((customer) => {
return (
<div key={customer.id}>
<h2>{customer.id}</h2>
</div>
);
})}
</div>
);
}
export default Customers;
When I remove this part of the code and reload the page, my props come through correctly when looking in console. Then, when I put the code back and save (without reloading), it displays correctly.
{props.customers.data.map((customer) => {
return (
<div key={customer.id}>
<h2>{customer.id}</h2>
</div>
);
However, as soon as I reload again, I get the same following error:
Uncaught TypeError: Cannot read properties of undefined (reading 'map')
It seems as though the first time everything renders, props is empty. Then the second time, it is full with the data. I checked my rails app and it only hits the API once. What am I doing wrong?
More log outputs:
React component rendering multiple times?
React will render fast before completing the request in use Effect
so in first render customers array will be empty
when request is fulfilled, you are changing state, So react will re-render the component
Only component that uses state reloads when the state is changed this is required else UI will not update
failing when reloading the page? | Failed on Initial Load
Since in Initial render customers will have no data customers.data will be undefined so it will not have map
to bypass this error use props.customers?.data && props.customers.data?.map() addding question mark means expression will be evaluated if not undefined
Source - Optional_chaining
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();
}, []);
I'm learning Next.js and I'm trying to integrate the #react-aria/overlays package in my project. I have a layout component, where I'm simply invoking the usePreventScroll method like this:
usePreventScroll({
isDisabled: true
});
This layout component is used in my _app.js.
import { useEffect } from 'react'
import { useRouter } from 'next/router'
import * as gtag from '../lib/gtag'
import 'styles/vendor.scss';
import 'styles/globals.scss';
import Layout from 'components/layout';
import { SSRProvider } from '#react-aria/ssr';
const App = ({ Component, pageProps }) => {
return (
<SSRProvider>
<Layout>
<Component {...pageProps} />
</Layout>
</SSRProvider>
)
}
export default App;
When going to my browser and loading a page, it gives me the following error:
Warning: useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server renderer's output format. This will lead to a mismatch between the initial, non-hydrated UI and the intended UI. To avoid this, useLayoutEffect should only be used in components that render exclusively on the client. See https://reactjs.org/link/uselayouteffect-ssr for common fixes.
at Layout (/home/bas/projects/test-website/build/server/pages/_app.js:718:3)
at div
at $c5f9596976ab8bd94c5879001549a3e$var$OverlayContainerDOM (/home/bas/projects/test-website/node_modules/#react-aria/overlays/dist/main.js:864:7)
at ModalProvider (/home/bas/projects/test-website/node_modules/#react-aria/overlays/dist/main.js:810:5)
at OverlayProvider
at SSRProvider (/home/bas/projects/test-website/node_modules/#react-aria/ssr/dist/main.js:33:13)
at UIContextProvider (/home/bas/projects/test-website/build/server/pages/_app.js:1144:74)
at ManagedUIContext (/home/bas/projects/test-website/build/server/pages/_app.js:1105:3)
at App (/home/bas/projects/test-website/build/server/pages/_app.js:5171:3)
at AppContainer (/home/bas/projects/test-website/node_modules/next/dist/next-server/server/render.js:23:748)
What's the problem here and how would I be able to solve it?
I tried wrapping the the Layout component in the packages <SSRProvider>.
You can dynamically load the component and disable SSR:
import dynamic from 'next/dynamic'
const DynamicComponentWithNoSSR = dynamic(
() => import('../components/hello3'),
{ ssr: false }
)
function Home() {
return (
<div>
<Header />
<DynamicComponentWithNoSSR />
<p>HOME PAGE is here!</p>
</div>
)
}
export default Home
The code example has been taken from the NextJS docs. If that's not your thing, you can call the hook or render the component as long as processs.browser is true.
Next js is computes your 1st page on server. so it does not understand browser scroll or localstorage or other browser api.
you can add a check in your code block if window object is present or execution is running in server and then execute usePreventDefault.
import {useIsSSR} from '#react-aria/ssr';
function Layout() {
let isSSR = useIsSSR();
useEffect(() => {
!isSSR && usePreventScroll({ ... })
}, [isSSR])
}