What's the difference between usePreloadedQuery and useQueryLoader? - javascript

I'm learning graphQL and react-relay library.
In these 2 sections:
Rendering Queries: introduce usePreloadedQuery.
Fetching Queries for Render: introduce useQueryLoader.
For short, I will say 1st-query instead of usePreloadedQuery, 2nd-query for useQueryLoader.
Question-1
The 1st-query will use a graphQL and it's generated flow type, query the server, then return the data. That's OK to me.
The 2nd-query seems to do the same thing? What's the difference except the library API/syntax?
Question-2
Here's the sample code in the 2nd section:
import type {HomeTabQuery as HomeTabQueryType} from 'HomeTabQuery.graphql';
import type {PreloadedQuery} from 'react-relay';
const HomeTabQuery = require('HomeTabQuery.graphql')
const {useQueryLoader} = require('react-relay');
type Props = {
initialQueryRef: PreloadedQuery<HomeTabQueryType>,
};
function AppTabs(props) {
const [
homeTabQueryRef,
loadHomeTabQuery,
] = useQueryLoader<HomeTabQueryType>(
HomeTabQuery,
props.initialQueryRef, /* e.g. provided by router */
);
const onSelectHomeTab = () => {
// Start loading query for HomeTab immediately in the event handler
// that triggers navigation to that tab, *before* we even start
// rendering the target tab.
// Calling this function will update the value of homeTabQueryRef.
loadHomeTabQuery({id: '4'});
// ...
}
// ...
return (
screen === 'HomeTab' && homeTabQueryRef != null ?
// Pass to component that uses usePreloadedQuery
<HomeTab queryRef={homeTabQueryRef} /> :
// ...
);
}
The line-1 use import type {HomeTabQuery as HomeTabQueryType} from 'HomeTabQuery.graphql'. And the line-4 use const HomeTabQuery = require('HomeTabQuery.graphql').
I don't understand, aren't these 2 lines do the same thing?

Related

Too many re-renderds | React App | State Variables updated in Component Function Not Working

Problem Summary
I am working on a React app with Node.js, Express.js, MogoDB backend.
I have a progress bar component. The number (i.e. percentage) that I pass to the progress bar comes from a function. This function calculates the percentage of the progress bar and then updates the state variable "progress".
The problem is that I seem to have an infinite re-rendering error. However, I cannot tell where it originates from and how to approach it.
Problem Description
Progress Component
Consider the following react component. This component is a progress bar. The component takes a float done and then shows a progress bar with a bar that takes up done% of the progress bar space
const Progress = ({done}) => {
return (
<div class="progress">
<div class="progress-done"
style={{
opacity: 1,
width: `${done}%`
}}>
{done}%
</div>
</div>
)
}
Use of Progress Bar in App.js
I attempted to implement the progress bar like below:
const App = () => {
const [progress, setProgress] = useState(0)
const [groupOfTasks, setGroupOfTasks] = useState([])
// assume that there’s code here that sets groupOfTasks using data from a database
const countDone = (groupOfTasks) => {
var numOfTasks = groupOfTasks.length
var numOfTasksSatisfied = 0
groupOfTasks.map((task, index) => {
if(task.completed == True)
{
numOfTasksSatisfied++
}
}
setProgress(numOfTasksSatisfied/numOfTasks)
}
return (
{countDone(groupOfTakss))
<Progress done={progress}/>
)
}
but I got this error:
How would you recommend I approach this problem? Any help is very appreciated
It's because you are using the countDone inside the return (where the visual render should be done)
You should use the useEffect hook, with the groupOfTasks as dependency.
So whenever the groupOfTasks is updated, you can have the progress updated at the same time
Just after you countDone method
useEffect(()=>{
countDone(groupOfTasks)
},[groupOfTasks])
By the way if you want to understand a bit more about the useEffect here a great article !
The error comes due to the line {countDone(groupOfTrasks)} inside the return statement. This is what happens:
You -> Render App -> Calculate count -> The countDone function sets a state, so it will rebuild the App -> Render App -> loop...
You can come over this problem by using the hook useEffect. The hook works like this: it takes a block of code and then an array.
If the array is empty -> the code will be executed only once when component is rendered.
If the array has variables -> the code will be executed every time one of those variables changes.
This is called dependencies array. In your case, set groupOfTasks inside the array, and it means: "Every time groupOfTasks changes, execute this code".
The function will be:
const App = () => {
const [progress, setProgress] = useState(0)
const [groupOfTasks, setGroupOfTasks] = useState([])
// assume that there’s code here that sets groupOfTasks using data from a database
const countDone = (groupOfTasks) => {
var numOfTasks = groupOfTasks.length
var numOfTasksSatisfied = 0
groupOfTasks.map((task, index) => {
if(task.completed == True)
{
numOfTasksSatisfied++
}
}
setProgress(numOfTasksSatisfied/numOfTasks)
}
useEffect(()=>{
countDone(groupOfTasks)
},[groupOfTasks])
return (
<Progress done={progress}/>
)
}

How to fire NGRX Selector foreach children component independently

I have a component which have a role as a widget in a dashboard. So, I'll use an *ngFor to render as many widgets as the dashboard have. The WidgetComponent is only one and receive a part of its data by #Input() from the parent.
parent
<app-widget *ngFor="let widget of widgets"
[widget]="widget">
</app-widget>
In the child, I listen an event using NGRX Selectors:
this.store.pipe(
select(fromStore.selectPage, {widgetId: this.widgetId, page: this.paginator.currentPage}),
distinctUntilChanged() // can be used, but is unnecessary
).subscribe(rows => {
console.log(rows);
});
When I want to change the page, I dispatch a new event in my store:
ngAfterViewInit(): void {
const pages = currentPage < 3
? [1, 2, 3]
: [currentPage - 1, currentPage, currentPage + 1];
const request: PaginatedData = {
widgetId: this.widget.id,
itemsPerPage: paginator.itemsPerPage,
pages
};
this.store.dispatch(updatePagesAction({request}));
}
selector:
export const selectPage = createSelector(selectState, (state, props) => {
const table = state.widgetTables.find(x => x.widgetId === props.widgetId);
if (typeof table === 'undefined') {
return [];
}
const existingPageKey = Object.keys(table.pages).find(key => key === props.page.toString());
return existingPageKey ? table.pages[existingPageKey] : [];
});
Problem: When I dispatch an action for a widget, there will be fired the selector for all widgets which listen in same time at the store.
I need to fire the selector only for in cause widget. The problem can be that I use the same selector for all widgets?
I can not use a filter() in my widget component pipe() because even if I use something like filter(x => x.widgetId === this.widget.Id), the event will be fired and all widgets will receive again the data, even if is equals with the last value.
Ah, I know: this can be due of at every pange changed, my store return a new state (for all widgets) and so the selectors are fired for all.
Also, I have this feature stored in a service which works very well, but because the app use already ngrx in another modules, I'm thought that is better to align all data which must be saved in memory and used later, to be saved inside a ngrx store (and not using custom services).
thanks
How I would approach the problem
I think you can use a function that returns a selector instead, Try to implement like below
export const selectPageWith = ({widgetId, page}: widgetId: number, page: any) =>
createSelector(selectState, state => {
const table = state.widgetTables.find(x => x.widgetId === widgetId);
if (typeof table === 'undefined') {
return [];
}
const existingPageKey = Object.keys(table.pages).find(key => key === page.toString());
return existingPageKey ? table.pages[existingPageKey] : [];
})
Now you can use this in your component like
this.store.pipe(
select(fromStore.selectPageWith({widgetId: this.widgetId, page: this.paginator.currentPage}),
distinctUntilChanged() // can be used, but is unnecessary
).subscribe(rows => {
console.log(rows);
});
Explanation
Simply we are trying to create unique selectors for each of the widget. By creating a function that returns a selector, different parameters produces different selectors for each widget

Invariant Violation Invalid hook call / cross-origin error

So after making this function work I started to create a loop that would give me feedback from the backend after SSR, I wanted to use hooks so I made it a functional component and started to write but the hook (even with nothing in it) is throwing 2 errors. Invalid Hook Call and A cross origin error was thrown.
I tried changing the file name to jsx, moving the file out of the folder I had because there was a second node modules in there (I thought it was using two versions of React), I also read somewhere just to clear local storage and it was just a in development using localhost problem.
*Edit So i've found that its not even calling the fn: reactToPdfUtils.savePDFNOW(sourceElement, true, undefined, cb) its stopping here
//reactToPdf.js
import React, {useEffect} from 'react';
import { savePDF } from '#progress/kendo-react-pdf';
import { drawDOM, exportPDF } from '#progress/kendo-drawing';
var ClassInstancesStore = require('../libs/goggles/reflux/classInstances-store');
var ClassInstancesActions = require('../libs/goggles/reflux/classInstances-actions');
export const savePDFNOW = (sourceElement, willSaveToDB, pageTemplate, cb) => {
//this hook broke the program as soon as i put it in even with nothing inside
useEffect(() => {
//Functionthat gets called after sending the pdf to the backend
// function onClassInstancesStoreChange(opInfo){
// var e = cloneDeep(opInfo);
// if (e.op === 'Call::StorePassportPDFToDisk') {
// if(e.error){
// console.log(e.ret)
// setPdf({ pdfErrors: e.ret })
// } else {
// console.log(e.ret)
// setPdf({ inProgress: true })
// alert('Successfully created: ' + e.ret.fileName)
// // onSubmit()
// }
// }
// };
// let listeners = [];
// listeners.push(ClassInstancesStore.listen(onClassInstancesStoreChange));
// return function cleanup() {
// _.each(listeners, function(listener) {listener();}); //NOTE: destroy listeners
// }
}, [])
try {
//do all the my functions that make my pdf perfect
} catch (error) {
//snap something went wrong all my awesome error handling
}
};
//previewer.jsx
var React = require('react');
var _ = require('underscore');
var reactToPdfUtils = require('../../../../../components/reactToPdf.js');
handleSave = (sourceElement) => {
reactToPdfUtils.savePDFNOW(sourceElement, true, undefined, cb)
function cb(sendDataContent){
if(sendDataContent.err){
console.log(sendDataContent.message)
} else {
console.log('sucess')
}
}
};
My understanding of the code is that the function handleSave will call the external hook savePDFNOW. If this is what happens, then this will break regardless of the useEffect logic.
The reason for that is that hooks that are extracted outside of the component require their name to start with use
So to allow the hook to run you change its name to useSavePDFNOW.
That being said, I believe this is not a valid use case for useEffect, think of useEffect as componentDidMount/Update. This is relevant to component render cycle rather than event listeners. It makes more sense to do away with the useEffect and keep it a regular function.
A few more things, if you are using the latest react version you don't need to import react. Also it's recommended to use const/let instead of var as well.

React Recoil state not being reset properly

I have some recoil state, that i want to reset.
import { useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil';
...
//should be used for flushing the global recoil state, whenever a user submits an invoice
const resetLabelInvoiceState = useResetRecoilState(labelInvoiceState);
const resetMetaDataState = useResetRecoilState(metadataState);
const resetGlobalAnnotationsState = useResetRecoilState(globalAnnotationState)
I have made function, that i suppoes to reset all the states like this. I have both tried with and without the reset function.
const flushRecoilState = () =>{
console.log('flushed state')
return(
resetLabelInvoiceState(),
resetMetaDataState(),
resetGlobalAnnotationsState()
)
}
...
flushRecoilState()
return history.push('/historyinvoices')
...
When i check the state it is not reset. Is it because the `useResetRecoilState´ is not working properly from the library, is not implemented properly, or is there some other problem.
I could just use the regular useRecoilState hook, and just set the state back to the default value.
Does anybody know why this could be?
I had the same problem today, it turns out to be my own fault. Just put it here for future reference.
My problem was that I changed the set method in the selector, if you customized the set method, you need to check if the incoming value is a DefaultValue.
const targetYear = selector({
key: 'targetYear',
get: ({get}) => get(targetYearAtom),
set: ({set, get}, method) => {
const currentTargetYear = get(targetYearAtom);
switch(method) {
case 'prevYear':
set(targetYearAtom, currentTargetYear - 1);
return;
case 'nextYear':
set(targetYearAtom, currentTargetYear + 1);
return;
default:
if (method instanceof DefaultValue) {
set(targetYearAtom, method);
}
return;
}
},
})

Draft js Editor gets slower as the content increases due to having many decorators

So my draft-js editor becomes really slow(hacky) the more content I insert (after about 20 decorator replacements). I am guessing this behavior is due to the decorator which checks the entire editor content using regex and replaces the matches with emoji component every time the state changes. I am also creating entities for each of the matches the regex find, I do this by decorating the component with editor state as a prop. Is there a way to make it faster?
Here is my decorator :
{
strategy: emojiStrategy,
component: decorateComponentWithProps(RenderEmoji, {
getEditorState: this.getEditorState,
setEditorState: this.onChange
})
}
here is my emojiStrategy :
function emojiRegexF(regex, contentBlock, callback, contentState) {
const text = contentBlock.getText();
let matchArr, start;
while ((matchArr = regex.exec(text)) !== null) {
start = matchArr.index;
callback(start, start + matchArr[0].length);
}
}
function emojiStrategy(contentBlock, callback, contentState) {
emojiRegexF(EMOJI_REGEX, contentBlock, callback, contentState);
}
here is my RenderEmoji component:
const RenderEmoji = props => {
const contentBlock = props.children[0].props.block;
const emojiKey = contentBlock.getEntityAt(props.children[0].props.start);
const emojiShortName = props.decoratedText;
if (!emojiKey) {
setEntity(props, emojiShortName);
}
return (
<Emoji emoji={emojiShortName} set="emojione" size={24}>
{props.children}
</Emoji>
);
};
and here is my setEntity function that sets the entity for the match:
function setEntity(props, emojiShortName) {
const editorState = props.getEditorState();
const contentstate = editorState.getCurrentContent();
const contentStateWithEntity = contentstate.createEntity(
"emoji",
"IMMUTABLE",
{
emojiUnicode: emojiShortName
}
);
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
const oldSelectionState = editorState.getSelection();
const selectionState = oldSelectionState.merge({
focusOffset: props.children[0].props.start + props.decoratedText.length,
anchorOffset: props.children[0].props.start
});
const newContentState = Modifier.applyEntity(
contentstate,
selectionState,
entityKey
);
const withBlank = Modifier.replaceText(
newContentState,
selectionState,
emojiShortName + " ",
null,
entityKey
);
const newEditorState = EditorState.push(
editorState,
withBlank,
"apply-entity"
);
props.setEditorState(newEditorState);
}
Any way I can optimize this? Thanks
I'm not sure if this would be the source of any real performance problem but there are two things that seem funny:
Matching the emoji decorator by regex, even though you're creating entities for them.
Changing the editor state (via setEntity) during the rendering of the decorator. Render functions should be pure.
I imagine you do this type of processing because emojis might be inserted via copy-paste, or via some kind of native emoji picker. A better way would be to:
Insert entities for emojis with the setEntity logic as part of onChange – before the content is saved and ultimately rendered.
Use a decorator strategy based on entities only, e.g.:
const emojiStrategy = (contentBlock, callback, contentState) => {
contentBlock.findEntityRanges(character => {
const entityKey = character.getEntity();
return (
entityKey !== null &&
contentState.getEntity(entityKey).getType() === 'emoji'
);
}, callback);
};
Then your decorator component won't need to update the editor state during the rendering. You also might not need to use decorateComponentWithProps anymore.
Now back to performance – the best way for you to know for sure how to improve it is to profile your app. You'll be able to tell exactly what takes time to render during keystrokes, and then track down the issue.

Categories

Resources