I need help implementing Autocomplete from antdesign ui - javascript

i'm still learning and having some trouble understanding the syntax of the [autocomplete][1] from ant design framework ( [1]: https://ant.design/components/auto-complete/)
I need the user to search some value, and then the component will show a suggestions list based on what objects i have
So i wrote the logic to that search,put some fixed value as a const to text and it works
but now i can't understand how to substitute the input value in my logic and how to show the options on the autocomplete component.
here's the call
const [proceds,setProceds] = useState([])
const text = 'aparelho'
useEffect(() => {
const loadProceds = async () => {
const res = await axios.get('myapi_url');
setProceds([...res.data])
}
loadProceds();
},[])
let results = proceds.filter((proced) =>
proced.terminologia.toLowerCase().includes(text.toLowerCase())
);
console.log(results);
in this case it searchs the text and shows which objcs in my api contains it .
my autocomplete blank template is like this
function handleChange(value) {
console.log(`selected ${value}`);
}
const Complete = () => (
<AutoComplete
style={{ width: 120 }}
dropdownMenuStyle={{ maxHeight: 1000, overflowY: 'visible' }}
onChange={handleChange}
>
</AutoComplete>
);
return (
<>
<Complete />
</>

Related

React: How to create a select multiple input that accepting same value

I'm using antd library and reactJS. I want the user to be able to input multiple values into a select multiple input statement, and the user can input the same value at once. Something like: [20, 100, 100]. Currently in normal multiple or tags mode, when the user enters another input that already exists, the input will be removed. Basically, I wanted to keep it there. These are the codes I got from antd docs:
const children = [];
function handleChange(value) {
console.log(`selected ${value}`);
}
ReactDOM.render(
<Select mode="tags" style={{ width: '100%' }} placeholder="Tags Mode" onChange={handleChange}>
{children}
</Select>,
document.getElementById('container'),
);
I have tried:
Setting a unique key for each of the values selected. But I found difficulties in modifying the current options that the user selected. There is no API provided that I can use to update the current options.
Setting a unique key as the user selects the option by appending Date.now() to the key. But again, I'm not sure how to do this on the select props. Something like this:
ReactDOM.render(
<Select
mode="tags"
style={{ width: "100%" }}
placeholder="Tags Mode"
onChange={handleChange}
value={Date.now() + '' + selectedValue}
>
{children}
</Select>
But again, selectedValue is not a valid variable that I can use.
I tried adding labelInValue, but it just added the id and value, with no option to customize the id.
Note: This solution only fix the problem that when ever we have a tag in select and you try to add the same tag, it do not remove the existing selected tag from Antd Select.
import { Select } from "antd";
import { useState } from "react";
function App() {
const [value, setValue] = useState([]);
const [searchValue, setSearchValue] = useState("");
// const [options, setOptions] = useState([]);
const onSelect = (value) => {
setValue((prev) => [...prev, value]);
setSearchValue("");
// If you want to show tags after user added removes it, you can enable this code
// Enable options and setOptions
// setOptions((prev) => {
// const index = prev.find((o) => o.value === value);
// if (!index) {
// return [...prev, { label: value, value }];
// }
// return prev;
// });
};
const onDeselect = (value) => {
if (value !== searchValue) {
setValue((prev) => prev.filter((v) => v !== value));
}
setSearchValue("");
};
return (
<div className='App'>
<Select
// options={options}
mode='tags'
searchValue={searchValue}
onSearch={setSearchValue}
value={value}
style={{ width: "100%" }}
placeholder='Tags Mode'
onSelect={onSelect}
onDeselect={onDeselect}
/>
</div>
);
}
export default App;

react-select dynamic dropdown with async options loaded as user is typing

I'm new to React and I'm trying to merge 2 different features. A dynamic form where you can add and/or remove inputs AND one with async react-select where you can start typing a word and options appear and get filtered based on an API source (based on connected user for example)
I'm almost done (I think) BUT :
When I start typing I correctly see my options...and options get filtered correctly BUT when I click on an item (to select this item) I get an error.
The error I got is Cannot read property 'name' of undefined but I don't understand it and I'm not sure it's the only problem I got. I have no clue how to get my choice to cprrectly get selected and correctly put into my array of objects (inputFields)
Here are the 2 different sources I try to mix (They both work perfectly put independantly)
React-Select Async dropdown : https://stackblitz.com/edit/react-select-async-component?file=index.js
Dynamic form field : https://www.youtube.com/watch?v=zgKH12s_95A
Thank you for helping me understand what's the problem !!!
Here is my code :
function AsyncDynamicForm(props) {
const [inputFields, setInputFields] = useState([
{ firstName: '' },
]);
const [inputValue, setValue] = useState("");
const handleChangeInput = (index, event) => {
const values = [...inputFields];
values[index][event.target.name] = event.target.value;
setInputFields(values);
};
const AddFields = () => {
setInputFields([...inputFields, { firstName: '' }]);
};
const RemoveFields = (index) => {
const values = [...inputFields];
values.splice(index, 1);
setInputFields(values);
};
const loadOptions = (inputValue) => {
return fetch(
`http://127.0.0.1:8000/api/Objects/?q=${inputValue}`
).then((res) => res.json());
};
const handleInputChange = (value) => {
setValue(value)
};
const handleSubmit = (e) => {
e.preventDefault();
console.log("inputFields", inputFields); // Nothing for now
};
return (
<div>
<Container>
<Form onSubmit={handleSubmit}>
{inputFields.map((inputField, index) => (
<div key={index}>
<Form.Field inline>
<AsyncSelect
name="firstName"
value={inputField.firstName}
onChange={(event) => handleChangeInput(index, event)}
cacheOptions
defaultOptions
getOptionLabel={(e) => e.name.toString()}
getOptionValue={(e) => e.id}
loadOptions={loadOptions}
onInputChange={handleInputChange}
/>
</Form.Field>
<Button.Group basic size="small">
<Button icon="add" onClick={() => AddFields()} />
<Button
icon="x"
onClick={() => RemoveFields(index)}
/>
</Button.Group>
</div>
))}
<Button type="submit" onClick={handleSubmit}>
click
</Button>
</Form>
</Container>
</div>
);
}
export default AsyncDynamicForm
The documentation is very helpful here. The onChange prop takes a method with a specific signature.
const onChange = (option, {action}) => {
/* The `option` value will be different, based on the Select type
* and the action, being one of `option`, an array of `option`s
* (in the instance of a multiselect), `null` (typical when clearing
* an option), or `undefined`.
* What you actually get will depend on the `action` the select passes,
* being one of:
* - 'select-option'
* - 'deselect-option'
* - 'remove-value'
* - 'pop-value'
* - 'set-value'
* - 'clear'
* - 'create-option'
*/
// example uses the `useState` hook defined earlier
switch (action) {
case 'select-option',
case 'remove-value',
case 'clear':
setColor(option);
break;
default:
// I'm not worried about other actions right now
break;
}
};
Remember that React-Select treats value as the entire option object, not just the option value you define in getOptionValue. If you're looking at setting a true form 'value', you'll probably wrap Select in some way to handle that.
React-Select is incredibly powerful, and incredibly complex. The documentation is your friend here. I find it helpful to play around in CodeSandbox, when trying out features I don't fully understand yet.

How to remove repeatable API results when using react-infinite-scroll and react hooks

I'm working with react-infinite-scroll-component library and randomuser.me api. I can fetch the data and the infinite scroll works just fine but the problem I have is that since the component makes a request at every scroll, I'm getting repeatable results.
I've found a workaround that removes arrays that are the same but it is not working properly with the infinite scroll package.
This is my code:
function App() {
const [people, setPeople] = useState([]);
const fetchPeopleImages = () => {
axios.get(`https://randomuser.me/api/?results=30&nat=br`).then((res) => {
const result = res.data.results;
setPeople([...people, ...result]);
});
// if (people.length > 30) {
// const removedDuplicatedPeople = people.filter(
// (ele, ind) =>
// ind ===
// people.findIndex(
// (elem) => elem.picture.medium === ele.picture.medium,
// ),
// );
// setPeople([...people, removedDuplicatedPeople]);
// }
};
useEffect(() => {
fetchPeopleImages();
// commented below because ESLINT was asking me to use useCallback
// inside fetchPeopleImage func. idk why
// eslint-disable-next-line
}, []);
return (
<div className="App">
<InfiniteScroll
dataLength={people.length}
next={() => fetchPeopleImages()}
hasMore={true}
loader={<h4>Carregando...</h4>}
endMessage={
<p style={{ textAlign: 'center' }}>
<b>Yay! You have seen it all</b>
</p>
}
>
{people.length > 1 &&
people.map((people, i) => (
<div>
<img src={people.picture.medium} alt="Imagem de uma pessoa" />
</div>
))}
</InfiniteScroll>
</div>
);
}
export default App;
What is commented was the workaround I've found to remove arrays that have the same image link.
Codesandbox: https://codesandbox.io/s/gracious-wildflower-opjx5?file=/src/App.js
Ideally don't do the filtering business specially with frequent fetches (like scroll). It is better if you maintain a state say page and pass it to your api and the api should return the data.
The problem with the inconsistency you are facing is due to the limitations of randomuser.me api. The api has only limited images to serve, so it will mix up the names, ages, images etc and tries its best to serve unique records. Hence you will often see duplicate images. You can check by rendering the name along with image and you will see 2 same images will have different names.
Some suggestions to solve your issue:
- provide a low value to results query param say 10
- use seed option to the api
- on every scroll increment the page and pass it to the api query param
See updated demo here - it works to some extent
Note - you are still not guaranteed to see unique images rendered. However, you can use the updated code and it will work when you use it with real api.
updated code snippet
function App() {
const [people, setPeople] = useState([]);
const [page, setPage] = useState(0);
const fetchPeopleImages = () => {
axios
.get(`https://randomuser.me/api/?results=10&nat=br&page=${page}&seed=abc`)
.then(res => {
const result = res.data.results;
setPeople([...people, ...result]);
setPage(prev => prev + 1);
});
console.log("page", page);
};
useEffect(() => {
fetchPeopleImages();
// commented below because ESLINT was asking me to use useCallback inside
// fetchPeopleImage func. idk why
// eslint-disable-next-line
}, []);
return (
<div className="App">
<InfiniteScroll
dataLength={people.length}
next={() => fetchPeopleImages()}
hasMore={true}
loader={<h4>Loading.....</h4>}
endMessage={
<p style={{ textAlign: "center" }}>
<b>Yay! You have seen it all</b>
</p>
}
>
{people.length > 1 &&
people.map((people, i) => (
<div key={i}>
<img src={people.picture.medium} alt="Person" />
<p>{people.name.first}</p>
</div>
))}
</InfiniteScroll>
</div>
);
}

react virtual list updatable item count

Libraries like react-virtualized, react-window and react-virtuoso have item count property like in code below from materal-ui. However it is located within return. Is there any way to make item counterupdatable?
export default function VirtualizedList() {
const classes = useStyles();
return (
<div className={classes.root}>
<FixedSizeList height={400} width={300} itemSize={46} itemCount={200}>
{renderRow}
</FixedSizeList>
</div>
);
}
Yes you can pass on a dynamic value to the itemCount property in FixedSizeList. It take care of it and also ensure that the scroll remain where it is currently
A sample code would look like
const Example = () => {
const [rowCount, setRowCount] = useState(10);
useEffect(() => {
setTimeout(() => {
console.log("changed");
setRowCount(1000);
}, 10000);
}, []);
console.log(rowCount);
return (
<List
className="List"
height={150}
itemCount={rowCount}
itemSize={35}
width={300}
>
{Row}
</List>
);
};
Working demo

How to remove HTML div on click using Javascript

So I have this Display() function which takes events from the Google Calendar and the function returns a list of elements (each element is associated with a slider) to be rendered on the screen (see return statement of Display() function) and renders them as seen here. So each element comes with a Remove button so that I can remove an unwanted element from the page using the hideMe() function inside the Display() function. The hideMe() function does seem to do its work, however, it removes all the elements on the page as seen here. So I am struggling to figure out what I should fix so that when I click on the Remove button, it only removes the element and the slider associated to that remove button. I am new to React and JavaScript so please help. Any help is appreciated and thank you in advance.
function Display() {
const isMounted = useRef(true);
const [items, saveItems] = useState([]);
const [visible, setVisible] = useState(true);
const [fading, setFading] = useState(false);
useEffect(() => {
return () => {
isMounted.current = false;
};
}, []);
useEffect(() => {
(async () => {
const items = await fetchItems();
//Do not update state if component is unmounted
if (isMounted.current) {
saveItems(items);
}
})();
}, []);
function hideMe() {
setFading(true);
setTimeout(() => setVisible(false), 650);
}
return (
<Tab.Pane attached={false}>
<h5>Rate stress level for each event</h5>
<br/>
{items.map(item => (
<div key={item.id} isvisible={!fading}
style={visible ? null : { display: "none" }}>
<Typography id="discrete-slider-restrict" gutterBottom>
{item}
<button onClick={hideMe}>Remove</button>
</Typography>
<PrettoSlider aria-label="pretto slider" defaultValue={98} step={null}marks={stresslevel}/>
</div>
))}
</Tab.Pane>
)
}
It seems to me that this issue is happening because all elements are available in same state or i would say that they all share same state. So, this executes for all. If it is possible for you to extract it to a new component and use the hideMe function there. This will i am sure work for each individual elements.
It is my suggestion please go through below. May be you have to tweak a little bit.
You can extract the elements in a separate component like:
const Item = props => {
const [visible, setVisible] = useState(true);
const [fading, setFading] = useState(false);
function hideMe() {
setFading(true);
setTimeout(() => setVisible(false), 650);
}
return (
<div isvisible={!fading} style={visible ? null : { display: "none" }}>
<Typography id="discrete-slider-restrict" gutterBottom>
{item}
<button onClick={hideMe}>Remove</button>
</Typography>
<PrettoSlider aria-label="pretto slider" defaultValue={98}
step={null} marks={stresslevel}/>
</div>
);
};
export default Item;
Then you can use it like:
// import Item
{items.map(item => (
<Item key={item.id} itemObj={item} />
// in case if you need item obj then props.itemObj will get you the object.
))}
In this way you can manage the hideMe function with the separate specific Item component.

Categories

Resources