Prevent loading options in SELECT drop down until minimum characters are entered - reactjs

here I'm trying to load options in select drop down only when minimum of 5 characters are entered. This is because my options list is humungous and it makes my applications performance poor.
I have tried below code, please suggest how can I tweak it to make it work.
this.state= {
options_show: false, // It's false in state
};
inputEntered(e) {
let value = e.target.value;
if (value.length > 5){
this.setState({options_show: true})
}
}
handleChangeName = (e, i) => {
let { nameID, nameIDArray } = this.state;
let filteredID = namesData.filter(name => name.name == e.value)
.map((name) => {
return name.id
})
nameIDArray[i] = filteredID[0];
this.setState({ nameIDArray });
};
<Select placeholder="Enter Profile Name"
name="SID0"
onChange={(e) => this.handleChangeName(e, 0)}
options={this.state.options_show ? options: []}
onInputChange={this.inputEntered}
// openMenuOnFocus={false}
// openMenuOnClick={false}
/>
This does not restrict the input here, on entering each character it starts loading the entire list.

Try with this hope it will work :)
this.state= {
options_show: false, // It's false in state
}
inputEntered(inputText) {
if (inputText.length >= 5) {
this.setState({ options_show: true })
} else {
this.setState({ options_show: false })
}
}
<Select placeholder="Enter Profile Name"
name="SID0"
onChange={(e) => this.handleChangeName(e, 0)}
options={this.state.options_show ? options: []}
onInputChange={(e) => this.inputEntered(e)}
/>

Related

React JS IOS Field focus sent to end of text when editing text in input

Just wondering if anyone is able to help me with a little problem that is small but annoying!
I am building a web app that allows users to create a username and add social media account names in but for that i am doing checks to ensure that there are no spaces and its all lowercase.
The issue is that on Iphone when you click in the middle of the text to alter a typo or something - once you type one character it sends you to the end of the text.
Is there a way for me to stop this from happening? See below for the code:
Component being displayed:
<TextInput
id="website"
name="website"
placeholder="Enter a link / website"
type="url"
label="link/website"
error={linkError}
onChange={(e) => {
setlinkError('');
handleChange(e);
fieldChange(e, true, true, false);
validateLink(e.target);
}}
isPublic={false}
defaultValue={data.website}
/>
Validations being used:
const fieldChange = (e: any, lowercase = false, removeSpaces = false, removeSpecialChars = false) => {
if (e && e.target && e.target.value) {
if (lowercase) {
e.target.value = e.target.value.toLowerCase();
}
if (removeSpaces) {
e.target.value = e.target.value.replace(' ', '');
}
if (removeSpecialChars) {
e.target.value = e.target.value.replace(/[^a-zA-Z0-9]/g, '');
}
}
}
const validateLink = (el: any) => {
const prefixes = ['https://', 'http://'];
let hasPrefix = false;
prefixes.forEach((i: any) => {
if (el.value.startsWith(i)) {
hasPrefix = true;
}
})
if (hasPrefix) {
setlinkError('');
} else {
setlinkError('Please enter a valid website link - You must include https://');
}
}
handleChange method
const handleChange = (e: any) => {
const target = e.target;
setFormData({
...formData,
[target.name]: target.files ? target.files?.[0] : target.type == 'checkbox' ? target.checked : target.value
});
};

React. Trigger a function inside an onChange only if N time has passed without onChange

I have the following onChange function
onChange = e => {
this.setState({
autocompleteValues: [],
showPredictivo: true
})
if (e.target.value.length > 3) this.partialSearch(e.target.value) //this is the one im concerned about
this.setState({ value: e.target.value, hasError: false, errorMsg: this.state.errorMsgSafe });
if (this.props.validateOn && (this.props.validateOn === undefined || this.props.validateOn === "onChange")) {
this.validaciones();
};
};
That function partialSearch will only trigger when the value length is more than 3, but i want to add another condition, and its to trigger it only if the value hasnt changed in the past 300ms.
How would i do that there?
EDIT: I have tried to add a debounce / throttle but it doesnt seem to work at all, the function is never triggered
This is my whole code
async partialSearch(searchTerm?: string) {
const environment = process.env.REACT_APP_ENV ? process.env.REACT_APP_ENV : "pre";
const api = process.env.REACT_APP_API ? process.env.REACT_APP_API : "my_api";
const api_version = "v2";
let baseUrl = getBaseUrl(api, environment) + "/search?q=" + searchTerm
return fetchSPA(baseUrl, undefined, { version: api_version })
.then(res => {
if (res && res.data && res.data.length) {
const firstFive = res.data[0].resultSearch?.cars.slice(0, 5).map(val => {
return { denominacion: val.denominacion, plate: val.id };
})
if (firstFive !== undefined) this.setState({ autocompleteValues: firstFive })
}
}).catch((res) => {
console.log("Error Fetch:: ", res)
});
}
onChange = e => {
this.setState({
autocompleteValues: [],
showPredictivo: true
})
if (e.target.value.length > 3) _.debounce(() => this.partialSearch(e.target.value), 300)
this.setState({ value: e.target.value, hasError: false, errorMsg: this.state.errorMsgSafe });
if (this.props.validateOn && (this.props.validateOn === undefined || this.props.validateOn === "onChange")) {
this.validaciones();
};
};
The component where its called
Without the debounce/throttle it works, but when adding it, its never triggered
<SCInput
type={type || "text"}
id={id ? id : null}
name={name}
disabled={disabled}
readOnly={readOnly}
style={style}
hasError={errorMsg ? true : false}
compact={compact}
onChange={onChange}
onBlur={onBlur}
placeholder={placeholder}
value={this.state.value}
>
You're most likely looking for a debounce / throttle here.
This article explains the scenario: https://css-tricks.com/debouncing-throttling-explained-examples/.
In short, by debouncing, you're wrapping a function over the function which gets called often. The "wrapper"-function is making sure, that the function inside only gets called again after a certain time has passed.
An easy way to implement this is to use a utility-library like lodash or you could google for a fitting debounce-function.

React checkboxes. State is late when toggling the checkboxes

I have a group of 3 checkboxes and the main checkbox for checking those 3 checkboxes.
When I select all 3 checkboxes I want for main checkbox to become checked.
When I check those 3 checkboxes nothing happens but when I then uncheck one of those trees the main checkbox becomes checked.
Can someone explain to me what actually is happening behind the scenes and help me somehow to solve this mystery of React state? Thanks!
Here is a code snnipet:
state = {
data: [
{ checked: false, id: 1 },
{ checked: false, id: 2 },
{ checked: false, id: 3 }
],
main: false,
}
onCheckboxChange = id => {
const data = [...this.state.data];
data.forEach(item => {
if (item.id === id) {
item.checked = !item.checked;
}
})
const everyCheckBoxIsTrue = checkbox.every(item => item === true);
this.setState({ data: data, main: everyCheckBoxIsTrue });
}
onMainCheckBoxChange = () => {
let data = [...this.state.data];
data.forEach(item => {
!this.state.main ? item.checked = true : item.checked = false
})
this.setState({
this.state.main: !this.state.main,
this.state.data: data,
});
}
render () {
const checkbox = this.state.data.map(item => (
<input
type="checkbox"
checked={item.checked}
onChange={() => this.onCheckboxChange(item.id)}
/>
))
}
return (
<input type="checkbox" name="main" checked={this.state.main} onChange={this.onMainCheckBoxChange} />
{checkbox}
)
I can't make a working code snippet based on the code you provided, one of the issues was:
const everyCheckBoxIsTrue = checkbox.every(item => item === true);
where checkbox is not defined.
However, I think you confused about using the old state vs the new state, it'd be simpler to differentiate if you name it clearly, e.g.:
eventHandler() {
const { data } = this.state; // old state
const newData = data.map(each => ...); // new object, soon-to-be new state
this.setState({ data }); // update state
}
Here's a working example for your reference:
class App extends React.Component {
state = {
data: [
{ checked: false, id: 1 },
{ checked: false, id: 2 },
{ checked: false, id: 3 }
],
main: false,
}
onCheckboxChange(id) {
const { data } = this.state;
const newData = data.map(each => {
if (each.id === id) {
// Toggle the previous checked value
return Object.assign({}, each, { checked: !each.checked });
}
return each;
});
this.setState({
data: newData,
// Check if every checked box is checked
main: newData.every(item => item.checked === true),
});
}
onMainCheckBoxChange() {
const { main, data } = this.state;
// Toggle the previous main value
const newValue = !main;
this.setState({
data: data.map(each => Object.assign({}, each, { checked: newValue })),
main: newValue,
});
}
render () {
const { data, main } = this.state;
return (
<div>
<label>Main</label>
<input
type="checkbox"
name="main"
// TODO this should be automatically checked instead of assigning to the state
checked={main}
onChange={() => this.onMainCheckBoxChange()}
/>
{
data.map(item => (
<div>
<label>{item.id}</label>
<input
type="checkbox"
checked={item.checked}
onChange={() => this.onCheckboxChange(item.id)}
/>
</div>
))
}
</div>
);
}
}
ReactDOM.render(
<App />
, document.querySelector('#app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Side note: You might want to consider not to use the main state
You shouldn't be storing state.main to determine whether every checkbox is checked.
You are already storing state that determines if all checkboxes are checked, because all checkboxes must be checked if every object in state.data has checked: true.
You can simply render the main checkbox like this:
<input
type="checkbox"
name="main"
checked={this.state.data.every(v => v.checked)}
onChange={this.onMainCheckBoxChange}
/>;
The line this.state.data.every(v => v.checked) will return true if all of the checkboxes are checked.
And when the main checkbox is toggled, the function can look like this:
onMainCheckBoxChange = () => {
this.setState(prev => {
// If all are checked, then we want to uncheck all checkboxes
if (this.state.data.every(v => v.checked)) {
return {
data: prev.data.map(v => ({ ...v, checked: false })),
};
}
// Else some checkboxes must be unchecked, so we check them all
return {
data: prev.data.map(v => ({ ...v, checked: true })),
};
});
};
It is good practice to only store state that you NEED to store. Any state that can be calculated from other state (for example, "are all checkboxes checked?") should be calculated inside the render function. See here where it says:
What Shouldn’t Go in State? ... Computed data: Don't worry about precomputing values based on state — it's easier to ensure that your UI is consistent if you do all computation within render(). For example, if you have an array of list items in state and you want to render the count as a string, simply render this.state.listItems.length + ' list items' in your render() method rather than storing it on state.

react-select how to make a pre selected fixed options

I want to make a pre selected options that cannot be deleted based on w. whether a client has been visited or no, here is what I want to achieve
const { clients } = this.props.clients;
const listOfClients =
clients !== null &&
clients.clients.map(client => ({
value: client._id,
label: client.company
? client.company
: client.lastname + " " + client.lastname,
last_visit: client.last_visit,
wilaya: client.wilaya,
visited: client.visited // true : false
}));
and that's how i render my select options
<Select
name="clients"
isMulti
value={this.state.clients}
onChange={e => this.onChange(e, "clients")}
isClearable={this.state.clients.some(client => !client.visited)}
options={listOfClients || []}
className="basic-multi-select"
classNamePrefix="select"
/>
and my state holds an array of clients as follows :
[{value: "5c0e784f0249ea83d88bddf3", label: "sarl medic", visited: true}]
if visited = true , then this selected option must be greyed out and cannot be deleted. I've looked up this example but i don't understand where i went wrong. Thank you :)
Your listOfClients options are missing the important isFixed, it should be the same value as visited if I understand your code correctly.
Also with multi select you will need to disable manually the remove function like the following code:
const listOfClients =
clients !== null &&
clients.map(client => ({
value: client._id,
label: client.company
? client.company
: client.lastname + " " + client.lastname,
last_visit: client.last_visit,
wilaya: client.wilaya,
visited: client.visited,
isFixed: client.visited // true : false
}));
class App extends Component {
constructor(props) {
super(props);
this.state = {
clients: []
};
}
onChange = (e, option) => {
if (option.removedValue && option.removedValue.isFixed) return;
this.setState({
clients: e
});
};
render() {
return (
<div className="App">
<Select
name="clients"
isMulti
value={this.state.clients}
onChange={this.onChange}
isClearable={!this.state.clients.some(client => client.visited)}
options={listOfClients || []}
className="basic-multi-select"
classNamePrefix="select"
styles={styles}
/>
</div>
);
}
}
Here a live example.

React-Bootstrap form validation - Need one function per field?

I am using the React-Bootstrap forms. I have around 15 fields that need to be filled out in the form. Does this mean I need to have 15 validation functions (e.g validateName, validateDate etc.)?
How is this generally approached?
My data looks something like this:
state = {
person : {
name: '',
startDate: null,
...
...
active: null
}
}
Say for eg you have 2 input fields
state = {
person : {
name: '',
age: 0
},
nameError: null,
ageError: null
}
handleInput = e => {
const { person } = this.state;
person[e.target.name] = e.target.value;
this.setState({
person
});
}
handleSubmit = () => {
const { person } = this.state;
if(person.name === null){
this.setState({
nameError: 'Name is required',
ageError: null
});
}else if(person.age === 0){
this.setState({
ageError: 'Age is required',
nameError: null
});
}else{
//send the values to the backend
//also reset both nameError and ageError here
}
}
render(){
const { person, nameError, ageError } = this.state;
return(
<div>
<input type='text' name='name' value={person.name} onChange={e => this.handleInput(e)} />
{nameError}
<input type='number' name='age' value={person.age} onChange={e => this.handleInput(e)} />
{ageError}
<button value='Submit' onClick={this.handleSubmit} />
</div>
);
}
Please Let me know if you have further queries. Sorry if there are any typos I answered on my mobile

Resources