Optional field inside Options React Select - reactjs

Hey guys im trying to create a autosuggestion in cooperation with redux-form. Im using the Creatable approach. I loading my options via an external API. The problem is, i need a extra field in every Option Object. {value: "test#gmx.de", label: "test#gmx.de", dn:"CN...." }. Is there a possibility to do so?

I typically add my own properties inside the callback for the API request, just before setting the options in the state. For example...
axios.get('/some/api/request')
.then(response => {
const options = response.data.map(item => {
// Add whatever custom properties you want here
return ({value: "test#gmx.de", label: "test#gmx.de", dn:"CN...." })
})
// set your options in the state to the new options constant from above
dispatch(change('formName', 'options', options))
Hope this helps!

//Handle change with either selectedOption
handleChange(selectedOption){
this.setState({ selectedOption })
if(this.props.onOptionSelect){
this.props.onOptionSelect(selectedOption.data)
}
}
loadOptions(input, callback) {
this.props.loadOptions(input).then(options => {
callback(null, {options: options})
})
}
render() {
const {selectedOption} = this.state
const selectClass = this.props.meta.touched && this.props.meta.error ? "has-error form-group" : "form-group"
return (
<div className={selectClass}>
<AsyncCreatable
value={selectedOption}
onChange={this.handleChange}
loadOptions={this.loadOptions}
isLoading={false}
placeholder={this.props.label}
promptTextCreator={(label) => this.props.promtLabel(label)}
onBlur={() => this.props.input.onBlur(selectedOption.value || "")}
/>
</div>
)
}
//Function to convert incomming users in usable options (React Select)
export const convertADUsersToOptions = users => {
return users.map(user => {
return {
value: normalizeDN(user.dn),
label: user.mail
}
})
}

Related

React Question about promise in a for loop

I am in the process of learning React and making HTTP requests.
Recently Im trying to implement a dropdown for the webpage that Im working on. In my code I had to loop through an array of id, and make a post request for each of the id to extract the metadata. So Im encountering a problem with the dropdown options. The dropdown options are suppose to be the names for the corresponding id.
The array of id is an array of objects that looks like this
[{key: "someidnumber1", count: 5}, {key: "someidnumber2", count: 5}, {key: "someidnumber3", count: 10},....]
So what I did first is to loop through the id array, and make a post request on each of the id as parameter. This is inside my render method.
render() {
return(
<SomeOtherComponent>
{ //Do something to fetch the ids
let promises = [];
let names = [];
let options = [];
ids.map(id => {
promises.push(
axios
.post(TARGET_META_URL, {
filters: [
{
field: "id",
values: [id.key]
}
]
})
.then(response => {
// adding the name from the data into the names array
names.push(response.data[0].name);
})
});
Promise.all(promises).then(() => {
// Wait for the promises to collection all the names
// and pass into a new array
options = [...names];
}
return (
<Dropdown
options={options}
/>
);
}
</SomeOtherComponent>
);
}
My dropdown options after opening it is empty. So I did a couple console log and figured out that the options is declared outside the Promise.all so when the render() method is called, the dropdown takes in an empty array. I need help on how to setup the options for the dropdown so it waits for all the code before it finish running. I tried putting the second return inside the Promise.all() but I get an error method saying that render() doesn't have a return.
Make another component which fetches the data and renders them once the responses have come back. Use Promise.all to wait for all of the Promises to resolve together.
const getName = id => axios
.post(TARGET_META_URL, {
filters: [
{
field: "id",
values: [id.key]
}
]
})
.then(response => response.data[0].name);
const AsyncDropdown = ({ ids }) => {
const [options, setOptions] = useState();
useEffect(() => {
Promise.all(ids.map(getName))
.then(setOptions)
.catch((err) => {
// handle errors
});
}, [ids]);
return options ? <Dropdown options={options} /> : null;
}
And replace your original render method with:
render() {
return(
<SomeOtherComponent>
<AsyncDropdown ids={ids} />
</SomeOtherComponent>
);
}
Maybe this will help -
componentDidMount() {
let promises = [];
let options = [];
ids.map(id => {
promises.push(
axios
.post(TARGET_META_URL, {
filters: [
{
field: "id",
values: [id.key]
}
]
})
});
Promise.all(promises).then((response) => {
// Wait for the promises to collection all the names
// and pass into a new array
options = response.map(res => res.data[0].name);
this.setState({ options })
}
}
render() {
return(
<SomeOtherComponent>
{ this.state.options?.length ? <Dropdown options={this.state.options} /> : null }
</SomeOtherComponent>
);
}

How can i request remote data after press enter? (mbrn/material-table)

I'm using remote data example from material table, The current behavior
In componentDidMount the data request by default.
any search or sorting make by default another request to get data based on the new query
I can delay the request by providing debounceInterval
What I want to do?
I want when Itype in the global search----> I don't want to get data by default unless I press enter
And here is my render method that will make the table resolves the remote data once it's received the data
<Entity
storeId={storeId}
entityRef={ref => { this.entity = ref; }}
onEntityReceived={data => this.onEntityReceived(data)}
onEntityReceivedError={data => this.onEntityReceivedError(data)}
render={store => (
<React.Fragment>
<If condition={this.exceptionError}>
<Message variant={'warning'} text={this.exceptionError} />
</If>
<MaterialTable
tableRef={ref => this.tableRef = ref}
title={this.title}
data={query => {
this.get(query);
return new Promise(resolve => event.on('data-fetched', resolve));
}}
isLoading={(store.loading && this.exceptionErrorsLoader) || isLoading}
options={this.options}
actions={this.actions}
localization={this.localization}
columns={this.columns}
components={this.components}
icons={this.icons}
detailPanel={this.rowDetailsPanel}
onRowClick={onRowClick}
/>
Here is the code that will handle received data to provide it to the table
onEntityReceived(data) {
this.exceptionErrorsLoader = false;
event.notify('data-fetched', {
page: this.state.pageIndex,
totalCount: data.totalCount,
data,
});
}
This is the get method that will get the data from server
get(query) {
const { oldQuery } = this.state;
const { additionalEntityPayload } = this.props;
const serverSideLink = this.getServerSideLink(query);
this.exceptionErrorsLoader = true;
this.setState({
query,
// ======== In Order To Save FIRST_QUERY (in case we need to refresh old data)
oldQuery: isEmpty(oldQuery) ? query : oldQuery,
pageIndex: query.page,
pageSize: query.pageSize,
}, () => {
if(!isEmpty(additionalEntityPayload)) {
return this.entity.get({
serverSideLink, additionalPayload: additionalEntityPayload });
}
this.entity.get({ serverSideLink });
});
}
The issue is I don't know how to control the search field or other field because they are not exposed
Thanks in Advance.

useState referring to stale value

I have a keeper app where I am adding notes and storing them in database. When I make a post request to the server, I am trying to fetch the _id from database, which will eventually help me to later delete the note ( if needed).
Here is my jsx file
function CreateMessage(props) {
const [currentGuest, setCurrentGuest] = useState({
guestName: '',
guestMessage: '',
id:''
});
function handleMessages(event) {
const {name, value} = event.target;
setCurrentGuest(prevGuest => {
return {
...prevGuest,
[name] : value
};
});
}
function submitMessage(event) {
//props.onAdd(currentGuest);
const params = {
guestName: currentGuest.guestName,
guestMessage: currentGuest.guestMessage,
}
axios
.post("http://localhost:8000/notes", params)
.then(res => {
console.log("The response is"+res.data._id);
console.log(res.status);
setCurrentGuest(prevGuest => {
console.log(res.data._id)
return {
...prevGuest,
id: res.data._id
};
});
console.log(currentGuest);
})
event.preventDefault();
}
return (
<div>
<form>
<input
name="guestName"
placeholder="Guest Name"
value={currentGuest.guestName}
onChange={handleMessages}
/>
<textarea
name="guestMessage"
placeholder="Write a Message"
rows="3"
value={currentGuest.guestMessage}
onChange={handleMessages}
/>
<button onClick={submitMessage}>Add</button>
</form>
</div>
);
}
The id is properly being fetched and displayed in ```console.log("The response is"+res.data._id"). But on first submit, the is always empty and stale id gets attached to the currentGuest object on next submit
function submitMessage(event) {
//props.onAdd(currentGuest);
const params = {
guestName: currentGuest.guestName,
guestMessage: currentGuest.guestMessage,
}
axios
.post("http://localhost:8000/notes", params)
.then(res => {
console.log("The response is"+res.data._id);
console.log(res.status);
setCurrentGuest(prevGuest => {
console.log(res.data._id)
return {
...prevGuest,
id: res.data._id
};
});
console.log(currentGuest);
})
event.preventDefault();
}
In the above snippet, after getting the response you're correctly changing the state but the problem is with where you're checking the changed state(console.log(currentGuest)). You're basically logging before the state is changed.
You can use useEffect hook and log the state inside it. This runs every time the currentGuest Changes.
useEffect(() => {
console.log(currentGuest)
}, [currentGuest])
Update
You can use the modified currentGuest inside the useEffect hook:
useEffect(() => {
console.log(currentGuest)
if(currentGuest.id) {
props.onAdd(currentGuest);
// You can also reset the state here as follows
setCurrentGuest({
guestName: '',
guestMessage: '',
id:''
});
}
}, [currentGuest]) // You might need to add the necessary dependencies to this array.

react-select when change view missing selected value

How can I keep selected options by react-select async? What I mean I have the following component which is inside of a form with multiple steps when I step back to the part where I use AsyncSelect I see placeholder and load options fire again(get all value from API) selected values. how to keep the selected value. this is my AsyncDropDown.js :
<AsyncSelect
styles={customStyles}
cacheOptions
loadOptions={loadOptions}
defaultOptions
onChange={handleChange}
isRtl={true}
isSearchable={false}
classNamePrefix='myDropDown'
placeholder={'(پیش فرض گروه فعال)'}
/>
and this loadOtions function :
const loadOptions = (selectedOption, callback) => {
let token = localStorage.getItem('Token')
let udid = localStorage.getItem('UUID')
let xml = `body of request`;
axios.post('myurl.com', xml, { headers: { 'Content-Type': 'text/xml;charset=UTF-8' } }).then(function (response) {
//console.log(response)
var options = {
attributeNamePrefix: "#_",
attrNodeName: "attr", //default is 'false'
textNodeName: "#text",
ignoreAttributes: true,
ignoreNameSpace: false,
allowBooleanAttributes: false,
parseNodeValue: true,
parseAttributeValue: false,
trimValues: true,
cdataTagName: "__cdata", //default is 'false'
cdataPositionChar: "\\c",
localeRange: "", //To support non english character in tag/attribute values.
parseTrueNumberOnly: false,
attrValueProcessor: a => he.decode(a, { isAttributeValue: true }),//default is a=>a
tagValueProcessor: a => he.decode(a) //default is a=>a
};
// Intermediate obj
var tObj = parser.getTraversalObj(response.data, options);
var jsonObj = parser.convertToJson(tObj, options);
if (jsonObj["soap:Envelope"]["soap:Body"].GetAllCategoriesResponse.GetAllCategoriesResult["diffgr:diffgram"].DocumentElement != null) {
var jsonDropDownDetails = jsonObj["soap:Envelope"]["soap:Body"].GetAllCategoriesResponse.GetAllCategoriesResult["diffgr:diffgram"].DocumentElement.CATEGORY
jsonDropDownDetails.map(item => {
const data = { value: item.CATEGORYNAME, label: item.CATEGORYNAME, index: item.CATEGORYID }
setDropDownOptions(dropDownOptions.push(data))
})
callback(dropDownOptions)
}
setIsLoading(false)
}).catch(function (error) {
console.log("erorr in DropDown : " + error)
})
};
and this is handelChange function :
const handleChange = selectedOption => {
setSelectedOption(selectedOption)
props.parentCallBack(selectedOption)
};
this is the Async Dropdown component use this component to the main view when in the main view go to the next view and get back to the main view missing selected Value and show placeHolder and call loadOptions function again and call API. how to solve this issue?
In order to retain values of your form fields when you click/next or previous:
Create a main form component say MasterForm and and render the form-steps say Step1 and Step2(which holds your input fields etc).
Maintain the value of your select in MasterForm and the values and onChangeHandlers the form steps.
I have created a working demo of react-select multi step form
MasterForm:
...
render() {
return (
<>
<Step1
value={this.state.step1SelectValue}
handleChange={e => {
this.setState({ step1SelectValue: e });
}}
currentStep={this.state.currentStep}
/>
<Step2
value={this.state.step2SelectValue}
handleChange={e => {
this.setState({ step2SelectValue: e });
}}
currentStep={this.state.currentStep}
/>
{this.previousButton()}
{this.nextButton()}
</>
);
}
...
Step1.js
...
<AsyncSelect
cacheOptions
loadOptions={this.loadOptions}
defaultOptions
onChange={this.props.handleChange}
isRtl={true}
isSearchable={false}
classNamePrefix="myDropDown"
placeholder={"(پیش فرض گروه فعال)"}
value={this.props.value}
...
Note:
You seem to not use search feature in your select. So I guess you can simply use normal Select and handle data fetching on your own in componentDidMount. See Step2 in my code demo
If you don't want to execute your loadOptions function for all the re-renders consider to use useMemo . Note that useMemo is available in functional components

Select React Component data option not appeard

I am facing an issue with representing data inside select-react Component, I had successfully getting data form server side (node js ) by componentDidMount()
componentDidMount(){
fetch('api/transporationTypes')
.then( res => res.json())
.then(trasnportation => this.setState({trasnportation }, () => console.log(trasnportation)));
}
but I cannot set loaded data inside React-Select Component I tried the below here below how this component works with static data.
render() {
const { selectedOption } = this.state;
return (
<Select
name="form-field-name"
value={selectedOption}
onChange={this.handleChange}
options={[
{ value: 'one', label: 'One' },
{ value: 'two', label: 'Two' },
]}
/>
);
}
}
but when I trying to load dynamic data with code below it represent No results Found see screenshot: http://prntscr.com/jqvp76.
alos printing the data via console.log('optionItems',optionItems) in dev tools print correctly http://prntscr.com/jqvq7v
How can I make option of select component works successfully
render() {
const { selectedOption } = this.state.selectedOption;
let optionItems = this.state.trasnportation.map((trans) =>
[ {value: `${trans.TransportationType}` , label : `${trans.TransportationType}`}]
);
console.log('optionItems',optionItems)
return (
<div className="row">
<h1>Choose Tranportation Type</h1>
<Select className="col-md-8"
name="form-field-name"
value={selectedOption}
onChange={this.handleChange1}
option={optionItems}
placeholder = "Select one of below"/>
</div>
);
}
}
Thanks -- Fadi
The items have wrong type:
let optionItems = this.state.trasnportation.map((trans) =>
[ {value: `${trans.TransportationType}` , label : `${trans.TransportationType}`}]
);
to
let optionItems = this.state.trasnportation.map((trans) =>
({value: `${trans.TransportationType}` , label : `${trans.TransportationType}`})
); // [] -> ()
It likely has something to do with the typos, in the code supplied there is:
trasnportation
transporation
transportation

Resources