react-select-plus + Every backspace Removes call load option method - javascript

How to call every backspace remove call load option. I am using select.Async control in every input change method call fetch method and fetch data but when I press backspace then first time called that load option method but in next time that can't call load option method.
getUsers = (input) => {
console.log(input);
if (!input) {
return Promise.resolve({ options: [] });
}
if (input.length > 2) {
const newstate = this.state;
newstate.loadingtext = 'Searching...';
this.setState({ newstate });
const searchLocation = Object.assign({}, cookie.load('cLocation'));
searchLocation.searchText = input;
searchLocation.isBusiness = false;
console.log(input.toString().length);
return fetch(sourceName.searchNavbar, apiUrl.locationAndBusiness,
searchLocation)
.then(json => ({ options: json.data }));
}
return null;
}
<Select.Async
onChange={this.onChange}
valueKey="placeID"
filterOptions={this.filterOptions}
labelKey="cityState"
loadOptions={this.getUsers}
onInputChange={inp => this.onInputChange(inp)}
onBlur={this.onBlur}
onInputKeyDown={inp => this.onInputKeyDown(inp)}
onFocus={this.onFocousSearch}
value={this.state.value}
searchPromptText="Please enter 3 or more characters"
loadingPlaceholder={this.state.loadingtext}
noResultsText="No results found"
options={this.state.options}
placeholder={this.state.placeholdervalue}
autoBlur
/>
In above code in load option that get user method call. My get User method is :
When I click first time back space then get user method call but in second time I pressed back space then not called that get user method. How to call every backspace remove call this method.

solved my self using cache property in react-select-plus plugin. give that false like this cache={false}. so every time call getuser() method.

Related

Don't make http call on subsequent button clicks

I have a button user can click on that makes an http call to fetch data.
When user click the button the first time, I make http call. If the user clicks the button again, just provide the data that was fetched earlier.
How do I do that without storing it in a local variable explicitly? I have tried few things but none seem to work. It always makes the http request. I have tried to use "shareReplay" and "share" operators.
<button (click)=getData()>Click me</button>
getData() {
source$.pipe(
switchMap(sourceVal => {
const source2$ = this.getSource2();
const source3$ = this.getSource3(); -------- I do not want this call to be made on subsequent button clicks because it's a reference data
return combineLatest([source2$, source3$])
}),
map(([source2Val, source3Val]) => {
//do some processing
return 'done'
})
)
}
I am using angular and rxjs.
you can disable the button or prevent sending multiple requests via a variable.
fetching = false;
getData() {
if(!this.fetching) {
this.fetching = true;
this.http.get('url').pipe(shareReplay(1), finalize(() => {
this.fetching = false;
}));
}
}

Subscribe to observable, async map result with input from dialog, use result from map to route

I am calling an API-service which returns an Observable - containing an array of elements.
apiMethod(input: Input): Observable<ResultElement[]>
From this I have been choosing the first element of the array, subscribing to that. Then used that element to route to another page like this:
this.apiService
.apiMethod(input)
.pipe(map((results) => results[0])
.subscribe(
(result) => {
return this.router.navigate('elements/', result.id)
}
)
This works just fine.
Problem is, I do not want to just use the first element, I want a MatDialog, or other similar to pop up, and give the user option of which element to choose, and THEN route to the correct one.
If the list only contain one element though, the dialog should not show, and the user should be routed immediately.
I have tried to open a dialog in the .pipe(map()) function, but the subscribe() happens before I get answer from the user, causing it to fail. And I am not sure if that even is the correct approach. How would any of you solve this problem?
Edit
Ended up doing partly what #BizzyBob suggested:
Changing map to switchmap in the API-call, making it this way:
this.apiService
.apiMethod(input)
.pipe(switchMap((results) => this.mapToSingle(results)
.subscribe(
(result) => {
return this.router.navigate('elements/', result.id)
}
)
With the mapToSingle(ResultElement[]) being like this:
private mapToSingle(results: ResultElement[]): Observable<ResultElement> {
if (result.length === 1){
return of(results[0]);
}
const dialogConfig = new MatDialogConfig<ResultElement[]>();
dialogConfig.data = results;
const dialogRef = this.dialog.open(ResultDialogComponent, dialogConfig);
return dialogRef.afterClosed();
}
I would create a DialogComponent that takes in the list of choices as an input, and emits the chosen item when it's closed.
Then, create a helper method (maybe call it promptUser) that simply returns an observable that emits the selected value:
this.apiService.apiMethod(input)
.pipe(
switchMap(results => results.length > 1
? this.promptUser(results)
: of(results[0])
)
)
.subscribe(
result => this.router.navigate('elements/', result.id)
);
Here we simply use switchMap to return an observable that emits the proper item. If the length is greater than 1, we return the helper method that displays the dialog and emits the chosen item, else just emit the first (only) item. Notice that we wrapped plain value with of since within switchMap, we need to return observable.
In either case, the desired item is emitted and received by your subscribe callback.
Two possible options:
Having a subject for the selected result that is "nexted" either by user input or a side effect of getting an api result with one element.
Keeping track of an overall state of the component and responding appropriately whenever a selectedResult is set in the state.
The example below is a sketch of using an Observable to keep track of the component's state.
There are two input streams into the state, the results from the api and the user input for the selected result.
Each stream is converted into a reducer function that will modify the overall state.
The UI should subscribe to this state via an async pipe, showing the modal when appropriate, and updating updating state from events via the Subjects.
The redirection should come as an effect to the change of the state when selectedResult has a value.
readonly getResultsSubject = new Subject<MyInput>();
readonly resultSelectedSubject = new Subject<ResultType>();
private readonly apiResults$ = this.getResultsSubjects.pipe(
switchMap((input) => this.apiMethod(input))
);
readonly state = combineLatest([
this.apiResults$.pipe(map(results => (s) => results.length === 1
? { ...s, results, selectedResult: x[0], showModal: false }
: { ...s, results, showModal: results.length > 1 })),
this.resultSelectedSubject.pipe(map(selectedResult => (s) => ({ ...s, selectedResult })))
]).pipe(
scan((s, reducer) => reducer(s), { }),
shareReplay(1)
);
ngOnInit() {
this.state.pipe(
filter(x => !!x.selectedResult)
).subscribe(x => this.router.navigate('elements/', x.selectedResult.id));
}
I've been using this pattern a lot lately. It makes it pretty easy the number of actions and properties of the state grow.
I would solve it using the following method:
Get the data with your subscribe (without the pipe). And save this data in the component variable
options: any;
this.apiService
.apiMethod(input)
.subscribe(
(result) => {
if (result.length === 1) {
this.router.navigate([result[0]]);
return;
}
options = result;
}
)
with an ngIf on the modal (conditional of the length of the array of options > 0 display the component with the different choices when the data is received
<modal-component *ngIf="options.length > 0"></modal-component>
when the user (click) on an option inside your modal, use the router to redirect.
html
<div (click)="redirect(value)">option 1</div>
ts
redirect(value) {
this.router.navigate([value]);
}
That would be the most straight forward

Async issue with State in React Native

I'm trying to build a simple app that lets the user type a name of a movie in a search bar, and get a list of all the movies related to that name (from an external public API).
I have a problem with the actual state updating.
If a user will type "Star", the list will show just movies with "Sta". So if the user would like to see the actual list of "Star" movies, he'd need to type "Star " (with an extra char to update the previous state).
In other words, the search query is one char behind the State.
How should it be written in React Native?
state = {
query: "",
data: []
};
searchUpdate = e => {
let query = this.state.query;
this.setState({ query: e }, () => {
if (query.length > 2) {
this.searchQuery(query.toLowerCase());
}
});
};
searchQuery = async query => {
try {
const get = await fetch(`${API.URL}/?s=${query}&${API.KEY}`);
const get2 = await get.json();
const data = get2.Search; // .Search is to get the actual array from the json
this.setState({ data });
} catch (err) {
console.log(err);
}
};
You don't have to rely on state for the query, just get the value from the event in the change handler
searchUpdate = e => {
if(e.target.value.length > 2) {
this.searchQuery(e.target.value)
}
};
You could keep state updated as well if you need to in order to maintain the value of the input correctly, but you don't need it for the search.
However, to answer what you're problem is, you are getting the value of state.query from the previous state. The first line of your searchUpdate function is getting the value of your query from the current state, which doesn't yet contain the updated value that triggered the searchUpdate function.
I don't prefer to send api call every change of letters. You should send API just when user stop typing and this can achieved by debounce function from lodash
debounce-lodash
this is the best practise and best for user and server instead of sending 10 requests in long phases
the next thing You get the value from previous state you should do API call after changing state as
const changeStateQuery = query => {
this.setState({query}, () => {
//call api call after already changing state
})
}

NGRX execution order of dispatch and select

I have a post button which will trigger the addPost() method to dispatch action (Add post into server) and then select the added post Id. I have a Add_Success reducer to assign the added post id into selectedPostId which will be used by the getCurrentPostHeaderId selector.
The correct execution order that I'm expecting is:
1. Dispatch AddPost Action
2. Select the added post Id
But the order always went wrong after the first execution:
1. Select the previous post Id
2. Dispatch AddPost Action
3. Select the added post Id
On the first run, the execution is correct, the action was dispatched and correct added id was displayed in the log.
But if i immediately add another post after the first run, it seems the old selector will be executed first and the previous id will be displayed. After this, only the new post will be added successfully and new post id is selected.
My Component:
addPost() {
const postStatus = Object.assign({}, this.newPostForm.value);
let postHeader: any = {};
postHeader.postStatus = postStatus;
this.store.dispatch(new postHeaderActions.AddToPost(postHeader)); //Add post
this.store.select(
fromPost.getCurrentPostHeaderId
)
.subscribe((post) => {
if (post) {
console.log(post); //Return the new added post id
}
});
}
My Reducer & Selector:
case ActionTypes.Add_Success: {
console.log('hey');
return postHeaderAdapter.addOne(action.payload, {
...state,
selectedPostId: action.payload.id,
loaded: true
});
}
export const getCurrentPostHeaderId = createSelector(
getPostHeaderFeatureState,
(state: PostHeaderState) => state.selectedPostId
);
The same goes on for multiple run, you can see that from second run onward, it will return the previous Id before showing the new Id.
Can anyone help me on this? Thanks
One way to listen only the next id is to listen only when the id has changed:
this.store.select(
fromPost.getCurrentPostHeaderId
)
.pipe(distinctUntilChanged())
.subscribe(...)

Clear timeout with every function call Angular2 RxJS

I have a http request which is being fired if user enters at least 4 characters inside the input and fires everytime he changes it's content (adding/removing letters). I want to add a timeout, that if user starts typing characters, the function will wait 1 second until it fires the request, to avoid a lot of requests when user is typing quickly. My attempt:
if (this.pointName.length >= 3) {
let timer = function() {
this.http.get(`./points.json`)
.subscribe(res => {
this.pointsArray = res.json();
});
};
clearTimeout(timer);
setTimeout(timer,1000);
My idea was to clear the timeout on every keyup event and set it once again.
But unfortunately it gives me an error, that `Argument of type '() => void' is not assignable to parameter of type 'number'.
Is there any way to do it more efficientely? Maybe using RxJS? Anyways, I'm looking for a working solution. Thank you in advance.
HTML
<input type="text" id="searchInput" placeholder="Point name"(keyup)="getPoints()">
Why dont use debounceTime(500) instead of setTimeout.
https://www.learnrxjs.io/operators/filtering/debouncetime.html
First of all, you'd better use Debounce operator in RxJS.
And the problem in your code is that you should pass the timer_id into clearTimeout instead of the function.
if (this.pointName.length >= 3) {
let timer = function() {
this.http.get(`./points.json`)
.subscribe(res => {
this.pointsArray = res.json();
});
};
let timer_id = undefined;
clearTimeout(timer_id);
timer_id = setTimeout(timer,1000);
Try this:
Create an RxJS Subject as a new member variable of your component
searchTerm$ = new Subject<string>();
In your component's ngOnInit method, set up your observable,
ngOnInit() {
this.searchTerm$
.filter( value => value.length >= 3)
.debounceTime(1000)
.switchMap( val => {
return this.http.get('./points.json')
.map(result => result.json());
})
.subscribe(result => .... // do what you want with the response );
}
In your HTML, change your keyup event binding to submit your input field's value
<input type="text" id="searchInput" placeholder="Point name"(keyup)="getPoints(this.value)">
Then in your component's getPoints method,send a value into your subject$
getPoints(value) {
this.subject$.next(value);
}
Basically, the observable you're creating does several things:
searchTerm$
.filter( value => value.length >= 3) // 1 filter out search terms that are shorter than 3 characters
.debounceTime(1000) // 2. only send events after no event comes for 1 sec
.switchMap( val => { // 3. convert your value to the result of your http request
return this.http.get('./points.json')
.map(result => result.json());
})
.subscribe(result => .... // do what you want with the response );

Categories