Why formik is losing focus immediatelly I hit the second field? - reactjs

I have a form with formik that is losing focus at the second field.
It's seems to be a really basic stuff, but I can't find the problem.
Check this sandbox: https://codesandbox.io/s/create-react-app-forked-m145g
Click on the e-mail field, type anything (or nothing), hit tab to jump to the next field and watch the focus going away.
As you can see, the field is beeing validated, so, I don't know (and would find quite unlikely) if my custom handleBlur function had something to do with it:
const customHandleBlur = (e) => {
if (!values.recaptcha) this._reCaptchaRef.current.execute();
handleBlur(e);
};
This function is responsible to execute Google's recaptcha v3.
What am I doing wrong?

Try changing your customHandleBlur to only execute if you have a value for both email and description.
const customHandleBlur = (e) => {
if (!!values.email && !!values.description && !values.recaptcha) this._reCaptchaRef.current.execute();
handleBlur(e);
};
This will keep the description from losing focus when the this._reCaptchaRef.current.execute() function is called.
It looks like there are other issues to... but this will keep your description field from losing the focus, which is what your question was.

Related

RxJS and repeated events

I am new to RxJs in general but am investigating a bug in some React code in which, upon an unrelated action, an old event seems to be emitted and rendered to a display error. Think if you had two buttons that generated two messages somewhere on screen, and clicking one button was showing the message for the other button.
Being new to RxJs I'm not positive where the problem lays. I don't see a single ReplaySubject in the code, only Obserables, Subjects, and BehaviourSubjects. So this is either misuse of an RxJs feature or just some bad logic somewhere.
Anyway I found the code with the related Observable and I'm not quite sure what this person was trying to accomplish here. I have read up on combineLatest, map, and pipe, but this looks like pointless code to me. Could it also be somehow re-emitting old events? I don't see dynamic subscriptions anywhere, especially in this case.
Tldr I don't understand the intent of this code.
export interface IFeedback {
id: number
text: string
}
export interface IFeedbackMessages {
message: IFeedback | undefined
}
feedback$ = new BehaviorSubject<IFeedback | undefined>(undefined)
feedbackNotifs$: Observable<IFeedbackMessages> = combineLatest([
feedback$
]).pipe(
map(([feedback]) => ({
feedback
})
))
I also found this which maybe be an issue. In the React component that displays this message, am I wrong but does it look like each time this thing renders it subscribes and then unsubscribes to the above Subject?
const FeedbackDisplay: React.FC () => {
const [feedbackNotifications, setFeedbackNotifications] = React.useState<IFeedbackMessages>()
React.useEffect(() =>
{
const sub = notification$.subscribe(setFeedbackNotifications)
return () => sub?.unsubscribe()
}, [notifications$])
}
Could it also be somehow re-emitting old events?
Yes, it probably is. BehaviorSubject has the unique property of immediately emitting the last value pushed to it as soon as you subscribe to it.
It's great when you want to model some persistent state value, and it's not good for events whose actual moment of occurrence is key. It sounds like the feedback messages you're working with fall into the second category, in which case Subject is probably a better choice.
does it look like each time this thing renders it subscribes and then unsubscribes to the above Subject?
Not exactly. useEffect accepts a callback, and within that callback you can optionally return a "cleanup" function. React will hang onto that function until the effect is triggered again, then it calls it to clean things up (which in this case consists of closing out the subscription) to make room for the next effect.
So in this case, the unsubscribe will only happen when the component is rendered with a new value for notifications$. Also worth pointing out that notifications$ will only change if it's either passed as a prop or created within the component function. If it's defined outside the function (imported from another file for example), you don't need to (and in fact should not) put it into useEffect's dependency array.

react-table is extremely slow with react-select: how to speed it up?

I have a table of editable data that's displayed with react-table. I'm using react-select for the cells. Whenever I change an entry, it takes a long time (close to a second) for the page to update. How can I speed this up?
Here's a fork of the official "editable table" example: https://codesandbox.io/s/gracious-cdn-fcu3i?file=/src/App.js, modified to use <Select> instead of <input>.
Part of the code from that official example updates the data on blur:
// We'll only update the external data when the input is blurred
const onBlur = () => {
updateMyData(index, id, value)
}
This means that even tabbing through the elements is extremely slow. I was able to address that by adding a check in updateMyData to abort if the data didn't actually change:
const updateMyData = (rowIndex, columnId, value) => {
if (data[rowIndex][columnId] === value) return;
etc. // (otherwise update)
}
So that fixes the tabbing issue, I believe, but what about data entry? Could it be made asynchronous, for example, or would that have dangerous consequences?
The reason they use onBlur with inputs is to avoid re-rendering the whole table after each keystroke, with selects you don't have the same problem, so you don't need to use onBlur at all, just report the update in the onChange handler (no need to keep the value in the cell state either). Saying that, the whole solution doesn't look particularly efficient - after each change the table is re-rendered twice. I've wrapped updateMyState into useCallback and SelectCell in memo, which seems to make it a bit better, but you might want to find another approach, particularly if you have a big table. Anyways, have a look here https://codesandbox.io/s/tender-night-vymfi?file=/src/App.js

Blur Any Focused Field onSubmit | React Final Form

This might be a bug, but I wanted to post here before I open a GitHub issue to see if the community at large has advice, or if someone can just call me out for doing it wrong.
I want to blur any focused field onSubmit. Simple.
According to the docs, the onSubmit function is passed (values, form) as arguments, and the form api includes ways to get all registered fields, and a method for supposedly bluring any field. Here's the code:
const onSubmit = async (values, form) => {
const fieldsNames = form.getRegisteredFields();
fieldsNames.forEach(name => form.blur(name));
await sleep(300);
window.alert(JSON.stringify(values, 0, 2));
};
I forked the basic example directly from FinalForms docs, and added those two lines to see if I could get their example to work, but it did not. To test, just type into the firstname field, and press your enter key. The field stays focused..
CodeSandbox Demo Here
Final Form: FormApi Docs
Thanks for reading!
form.blur() is how you tell Final Form that the field has been blurred (it marks the field's state as being blurred). It's more of a listener. To actually imperatively blur the field, you'll need a reference to the DOM element.
Something like getElementById('myField').blur().

react-autosuggest with debounce and distinctUntilChanged

Intent:
I'm trying to achieve an ideal search field that will not make any API calls untill:
The debounce time of 350ms has been reached
AND until there's a change in the value of the input field.
What I've tried so far:
I've used a Subject to track for changes in the input field. Every time there's a change in the input field and handleSuggestionsFetchRequested is called, I'm pushing a new value down the Subject using searchString$.next(userInput);
And in the useEffect hook, I'm pipeing the searchString$ with debounceTime(350) and distinctUntilChanged(). Something like this:
useEffect(() => {
searchString$
.pipe(
debounceTime(350),
distinctUntilChanged(),
switchMap(searchString =>
ajax(`https://api.github.com/search/users?q=${searchString}`)
),
map((networkResponse: any) => networkResponse.response.items)
)
.subscribe((suggestions: Array<User>) => setSuggestions(suggestions));
}, [searchString$]);
But the API calls are still going everytime there's a change in the userInput.
The Issue:
I think the issue is that every time the value of the input field changes, I'm setting the state as well:
const handleChange = (
event: React.ChangeEvent<{}>,
{ newValue }: Autosuggest.ChangeEvent
) => {
setUserInput(newValue);
};
This is causing the Component to re-render and calling the useEffect, which is eventually making the API call again and again.
I could be wrong.
How to replicate:
I've created a Sample Code Sandbox that replicates the issue.
Thanks a lot in advance.
Thanks to the comments from yurzui on my tweet, I was able to figure out the reason for the issue.
I was creating a new Subject on every reconciliation as the line:
const searchString$: Subject<string> = new Subject<string>();
was right inside my component function.
I moved it out and it was working like a charm.
NOTE: As suggested by yurzui, don't forget to catch errors in the ajax call otherwise the Subject will die.
I've updated the Code Sandbox Sample, just in case somebody needs to refer to it.

Testing if something is on screen with react

I have an input field with a submit button and when someone enters something that text appears on the screen. how can I test that in react?
I know how to simulate the button click but what would the expectation be? or how would I actually be able to tell if the text is there?
You would need to use refs to check the values of input fields using React. The refs provide the hooking interface between virtual DOM element of input fields to access it values.
You scry for that input element by id or type or class. And you can access the text of that element by looking at .value
I'm not sure I understand the question... the first part of your question makes me think you want to detect when a user is typing some text in an input field, but the second part makes me think you want to automate a test, placing some text in the input and simulating click on a button, kinda confusing ...
In my last project I used that package instead of a classic input: react-contenteditable
You can hook your code to events that this component is firing:
render() {
const { selectedDbItem } = this.props
return (
<div>
<ContentEditable
onBlur={(e)=>this.onBlurField(e, 'supplier')}
className="field-value"
html={selectedDbItem ? selectedDbItem.supplier : ''}
disabled={false}
onChange={(e)=>this.onChangeField(e, 'supplier')}
onKeyDown={(e) => this.onKeyDown(e)}
/>
</div>
You can then implement the callbacks onBlur, onChange, onKeyDown (for input filtering for example ...) in the containing component.
Hope that helps

Resources