ReactJS autocomplete from React Bootstrap not working - javascript

I'm trying to build an autocomplete search field, using this form component from React Bootstrap, which is rendered as a input by the browser.
Here's what I did in my React component:
<FormControl
id="frenchToEnglishInput"
placeholder="type a word..."
aria-label="Recipient's username"
autocomplete="on"
data={frenchToEnglishWords}
onChange={this.fetchFrenchToEnglish}
renderItem = {item => {
return (
<div>
{item}
</div>
);
}}
/>
the frenchToEnglishWords array is declared outside the component as a var, as I intend to update it as I type some value into the input field.
Now here is the function that triggers the onChange event :
fetchFrenchToEnglish = async () => {
if(document.getElementById("frenchToEnglishInput").value!==''){
axios.get(dictionaryURIs.english.French_English+""+document.getElementById("frenchToEnglishInput").value)
.then(response => {
frenchToEnglishWords = response.data
})
}
}
The request is made from MongoDb, after setting up an autocomplete index on a collection, but this part works fine.
My question is, why is the only "autocomplete" I'm getting is the one made of the previous words I've typed ?
Or maybe the array I'm using as input data must be a const (as I've seen in many examples) and not a var ?
When I do type in some word, I do not get any autosuggestion from the frenchToEnglishWords, which is being updated from the DB.

You need to use State!
This is not working because the data field is not a State, you need to bind the fetchFrenchToEnglish function to the data state.
But first of all, there's no reason to use var, because the most differences between them is just scope and immutability, here is more about it.
Also, you can use hooks like useState and useRef to no use getElementById.

Related

Making v-model:value lazy

I have a simple Vue component, which comes from a 3rd party package.
The component is a text editor, and I have to provide it with a value for it to render correctly.
<SomeComponent v-model:value="text" />
<script setup>
const props = {
records: {
type: Object,
},
}
const text = computed(() => props.records.first())
</script>
Now, I want to update my database everytime the text property is changed.
watch(text, () => {
//Post to database...
})
I don't, however, want to update the database on every keystroke - hence, I want to make the v-model lazy. This I am trying to do like this:
<SomeComponent v-model:value.lazy="text" />
However, this doesn't work. The code inside my watch method is being fired on every keystroke.
As shown in the documentation, v-model is just some sugar syntax.
So that those are the same
<input v-model="text">
<input :value="text" #input="event => text = event.target.value">
If you want to have your component updated on the change event, you could then use something like this
<SomeComponent :value="text" #change="event => text = event.target.value" />
Or this one rather
<SomeComponent v-model.lazy="text" /> <!-- text is the getter and the setter here -->
As shown in the documentation: https://vuejs.org/guide/essentials/forms.html#lazy
If you want something even more advanced, you could look into debouncing your inputs (more work but it's kinda more clever into handling when to update the state).
You do not need to use v-model:value="text", just v-model="text" is enough.
If you wanted the input to be lazy, without any extra functionality (like posting to DB on every change) then v-model.lazy="text" would be enough.
When using watch, it doesn't matter whether your input is updated lazily or instantly. The watcher watches for each and every change, i.e: every keystroke in the case of an input.
So if you wish to make it lazy and make a call to DB on every change, then you need to this:
<SomeComponent :value="text" #change="onChange">\
<script setup>
const onChange = (event) => {
text.value = event.target.value;
// post to DB
}
</script>
If this doesn't work, then the suspect is the 3rd party package. In which case I suggest you look at the package's documentation and see if they provide a prop for the text editor to be lazy.
The 3rd party plugin did not allow me to omit the v-model:value property, so I ended up using lodash' debounce method instead:
<SomeComponent v-model:value.lazy="text" />
watch(text, _.debounce(function () {
//Post to database...
}, 500));

Read value of imported form component upon submit (without storing in state)

I developed a component for ReactJS as to be used as a form item in conjunction with Antd's form. But, the onFinish callback function returns undefined for the value of my custom component. Probably, Antd's form is not being able to retrieve the value from my component. That does not happen when I am only using Antd components as form items.
On the example below, MultipleEntry is the component I have developed. MultipleEntry has a nested TextArea component.
function Sandbox(props) {
return (
<>
<Form onFinish={(values) => console.log(values)}>
<Form.Item name={'myComponent'} >
<MultipleEntry />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">Submit</Button>
</Form.Item>
</Form>
</>
);
}
function MultipleEntry(props) {
const [value, setValue] = useState([]);
const Split = string =>
{
setValue(String(string).split(';'))
}
return (
<TextArea
onChange={(e) => {Split(e.target.value)}}
/>
);
}
I thought about two alternatives here:
Storing the values of MultipleEntry in its internal state.
Storing the values of MultipleEntry on the parent component.
But, I dont want to store the values anywhere on the client's state, since the data inside the MultipleEntry component would be too big and impactful for performance.
How can I use Antd form's API to make it read the value of MultipleEntry as a form field, and populate the input parameter of onFinish?
Antd's FormItem behaves like a high-order component and passes some props to the child component.
So, if you are using any HTML form field elements like input, then FormItem will pass the onChange and value as props to input. Hence FormItem will control the value and onChange event of that element.
But having said above you will not be able to use the styling if some validation error is there in the FormItem.
Similarly Antd's component also usages native HTML form field elements and usages value and onChange. But on top of that, you will get the styles if there are any validation errors.
Here it comes your specific case, you can use onChange and value from props and utilize the same as following
function MultipleEntry(props) {
const Split = e => {
e.target.value = String(e.target.value).split(",");
props.onChange(e);
};
return <input value={props.value} onChange={e => Split(e)} />;
}
If you are using any native HTML form elements then just split the props and pass it across the element like below
function MultipleEntry(props) {
return <input {...props} />;
}
And for splitting you can use getValueFromEvent and then split the value there.
Here is the link for the modified sandbox to especially solve your problem https://codesandbox.io/s/agitated-lake-65prw

Need some suggestions for a dynamic form using React

I'm building an enterprise-level application and I need some tips and suggestions for handling dynamic form.
The fields of the form are totally dynamic and they come differently for each user.
I loop through each field(fields come from an API call) on a file called renderUiType.js and based on a property of the field called uitype, we render different Inputs.
For example if uitype===1{render TextField}, if uitype===2{ render Checkbox } and so on...
So far the displaying part is correct but now I want to save the values of each field rendered and have them all in an object so I can do a POST API Call
So my question is, how can I do that? Should I create an onChange handler function for each form-element at the main file renderUiType.js and then pass it with props to the form-elements components or should I use Redux?
Any suggestion/article or anything is welcomed. Thank you
The folder structure looks like the image below(just in case it helps to understand what I ask)
..
You can use one callback function and use it in each onChange component specific handlers. You could have everything in state of the Form if you would like hidden under the unique keys/id, so you don't need to have Redux. f.e.
if (uitype===1)
{render <TextField value={this.state[fieldId]} onChange={this.onChange}/>}
if (uitype===2)
{ render <Checkbox value={this.state[fieldId]} onChange={this.onChange}/>}
or to simplify:
const getComponentByUIType = (uiType) => {
switch(uiType) {
case 1: return TextField
case 2: return Checkbox
}
}
// ...
onChange = fieldId => value => this.setState(state => ({fieldId: value}))
//...
render() {
getComponentByUIType(uiType).map(Component => <Component value={this.state[fieldId]} onChange = {this.onChange(fieldId)} />
}
Using Redux for this shouldn't be necessary unless you need to access this form's state somewhere outside the form. If you only need the form info to do a POST, I would keep all the data inside one component's state.
Just use the unique ID provided by the IP (the one you were gonna use for the POST) to build that state object. Every field will have an onChange that updates the main form component's state, and then that same value from the state is passed in to each field as a prop.

React - Mutating form object for each change . Is that anyway we can do it in a better way

handleChange(evt, field) {
let form = this.state.form;
form[field] = evt.value;
console.log('change triggered');
this.setState({
form: form
});
}
render(){
return(
<div>
<input type="text" name="name" id="name"
text={this.state.form.name}
onChange={event => { this.handleChange(event, 'name'); }} />
<br />
<input type="text" name="email" id="email"
text={this.state.form.email}
onChange={event => { this.handleChange(event, 'email'); }} />
</div>
);
}
I have added simple form for reference . In the above content Whenever form field changes handleChange method invoked and In that form object mutated updated as per field changes and setting to state object.
I want to know whether we can avoid mutation of object of each field text changes or Is there any other better way to address the same . Also want to know object mutation affects the performance in any way because here I mentioned couple of fields and In my original project I am working contains atleast 15 to 20 fields for each form.
Added the same working module.
https://stackblitz.com/edit/react-ssgcnu?file=Hello.js
Please help me out . Thanks in advance
If you don't want to update each character change you can use onBlur event.
onBlur={event => { this.handleChange(event, 'name'); }}
So that on leaving the field only you can update the state.
'...' Spread operator produces a shallow copy of an object
handleChange(evt, field) {
let form = {...this.state.form}; //object spread notation
form[field] = evt.value;
console.log('change triggered');
this.setState({ form: form });
}
When it comes to forms with React, there are two approaches.
1) Uncontrolled components, which basically making use of refs and getting values from the DOM (please check the official documentation for more details https://reactjs.org/docs/uncontrolled-components.html) or
2) Controlled components making use of states and getting/handling values from React states (please check the official documentation for more details https://reactjs.org/docs/forms.html#controlled-components)
With controlled components => React, state is the single source of truth (as official document suggests), that means, you need to provide methods to handle state changes as user provides the input
With uncontrolled components => Instead of updating state on every single change, you can get values inside of onSubmit method and handle them before submit. Since you won't need to update the state, you won't need additional functions to handle state changes.
For you seeking of better way to handling things and avoid mutations, it actually depends on your use case, but official documentation suggests that
In most cases, we recommend using controlled components to implement
forms. In a controlled component, form data is handled by a React
component
When it comes to mutating object, indeed mutations is not good, but there are ways to avoid mutations during state changes. as #mhkit already suggested, you can use spread operator to create a new object (shallow copy, that means it only copies the values) or you could use Object.assign() method.
Let's say you have the following state
state = {
form: {
email: '',
name: '',
lastName: '',
}
}
When you user provides the email, you basically need to update the email field,
in that case in your handleChangeEmail() method, what you can do is the following
this.handleChangeEmail = (value) => {
this.setState(({form}) => {
return {
form: {
...form,
email: value
}
})
}
So with this method, what I basically do is,
1) I utilize functional setState and extracted the current value of form via ES6 object destructuring, then I say that, okay my new form object inside of the state, will have all the existing field that former form has, BUT, the email field will have a new value based on the new input user provided.
By this way, instead of mutating form object, we created a shallow copy of it with some values are exactly the same, but some values are updated. THUS we prevent the mutation

How to update a field value after action dispatched with redux

I would like to populate a form after an ajax call with redux.
My case is pretty simple:
I have a simple user form (with only one text field for now), it's a react view bound to the state with connect().
I call a rest API to fetch the user.
When the api call is done, an action is dispatched with the user.
A reducer update the store state with the user.
I would like to populate/update the form with the retrieved values.
Solution 1:
If I set the value from the props like that:
const Field = ({ field, onFieldChange, value }) => (
<input
value={value}
onChange={(event) => { onFieldChange(field, event.target.value) }}
type="text"
/>
)
It works but I get this warning:
Field is changing an uncontrolled input of type text to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa).
I understand why I get this error as I should not use a component to display something and also be able to update it.
I also tried to use the defaultValue props but this is only used at the component creation (and we don't have the user yet). After the ajax call return, defaultValue cannot be called.
Solution 2:
Use redux-form with a custom plugin to update the form model each time the state get updated. I don't find this solution really clean but maybe I'm wrong.
I really thin that I'm going in the wrong direction and that it should exist a better way.
Does somebody already faced this kind of issue?
I encountered the same problem when I was trying to pass undefined as the input value.
To fix this, ensure that you are passing at least empty string to the input, not undefined
const Field = ({ field, onFieldChange, value }) => (
<input
value={value || ''} // <- add fallback value here
onChange={(event) => { onFieldChange(field, event.target.value) }}
type="text"
/>
)
Actually you might try to make your component statefull - store and manage value of input inside of it (they say it's ok).
Or if you really need this value in the store use redux-form, I have realy good experience of using it (you'll have to write less boilerplate code).
By the way, you will not have to use any custom plugin, you can use initialValues, see more here
The solution above will work for sure, but it doesn't seem to be nice.

Categories

Resources