Not able to populate the select drop down with JSON in reactJS - json

I'm trying to fetch key "name" and its corresponding value using JSON in the drop down "Select" of reactJS. But it does not work when I'm executing my code. I have attached my code, Kindly have a look!
I have tried using componentDidMount to fetch the data by making API call. I am a beginner so I'm trying to figure out what ways can make it work.
class devName extends Component {
state = {
names: [],
selectedName: "",
validationError: ""
}
componentDidMount() {
fetch("http://127.0.0.1:8080/dashboard/get_names")
.then((res) => {
return res.json();
})
.then(data => {
let namesFromAPI = data.map(name => { return { value: name, display: name } })
this.setState({ names: [{ value: '', display: '(Select the site)' }].concat(namesFromAPI) });
}).catch(err => {
console.log(err);
});
}
render() {
return (
<div className="wrapper">
<div className="form-wrapper">
<Toolbar />
<form className="form">
<label className="label1">Select Name from the drop down</label> <hr />
<div>
<Select
value={this.state.selectedName}
onChange={(e) => this.setState({selectedName: e.target.value, validationError: e.target.value === "" ? "You must select a site name" : ""})}>
{/* placeholder="Loading Site List..." > */}
{this.state.names.map((name) => <option key={name.value} value={name.display}>{name.display} </option>)}
</Select>
<div style={{color: 'red', marginTop: '5px'}}>
{this.state.validationError}
</div>
<br />
</div>
<label className="label1">Create New Name From Template</label>
<hr />
<div className="addButton">
<button type="Submit">Add</button> <hr />
</div>
<div className="submitButton">
<button type="submit">Submit</button>
</div>
</form>
</div>
</div>
);
}
}
export default devName;
JSON CODE LOOKS LIKE:
[
{
"type": "state",
"id": 2131,
"temporaryEle": {},
"name": "First Unit#1234 TON" .. and so on
}
]
There is no error message. Though the desired result should be "Select Drop Down populates with : " Select one of the name please"
First Unit#1234 TON
Second Unit#8934 QON
Third Unit#6534 JON

Edit:
I've read one of your comments and it seems like you're having CORS issues with fetch.
Add the baseurl as proxy in your package.json file
// Package.json
{
...other configs,
"proxy": "http://127.0.0.1:8080"
}
Then you'd call your fetch like so: fetch('/dashboard/get_names').then((res) => {...})
Resource for you to look at:
https://create-react-app.dev/docs/proxying-api-requests-in-development/
Old:
According to your JSON data, you might have to destucture a level deeper on your object while mapping the data in componentDidMount
let namesFromAPI = data.map(({name}) => {
return { value: name, display: name }
})
// OR
let namesFromAPI = data.map(name => {
return { value: name.name, display: name.name }
})

Use destucturing on name parameter, you can implicitly return object from arrow function when you wrap it in () data.map(({name}) => ({ value: name, display: name }))

Related

How can I select a state property object name dinamically to change one of its properties(childs?) on react?

I have a class with this state properties.
state = {
step: 1,
clientName: '',
icecreamFlavors: {
vanilla: false,
chocolate: false,
mintChocolateChip: false
},
toppings: {
sprinkles: false,
caramel: false,
whippedCream: false
someOtherProperty1: '',
someOtherProperty2: ''
};
Short version of the question without needing to read all:
how can a make the last part of this statement "dynamic". so change:
this.state.icecreamFlavors to this.state.<variable-here>
This properties are updated from a html form that consists of two types of fields text and checkboxes. To update the text properties I use this method.
handleChange = (input: any) => (e: { target: { value: any } }) => {
this.setState({ [property]: e.target.value });
};
and for the checkboxes I'm using this other method that works only for "icecreamFlavors" and that's what I want to fix (as i want to create a generic method instead of repeating the same method for every property with children):
handleCheckboxChange = (parentProperty: any, property: any) => (e: { target: { checked: any} }) => {
this.setState({[parentProperty]: {...this.state.icecreamFlavors, [property]: e.target.checked} })
};
This works as intended and lets me update the "icecreamFlavors" bools, but i want to make the "handleCheckboxChange" generic to be able to use it with any other property (Eg: toppings).
The html field looks like this (extract with only the checkboxes for simplicity):
<input
type="checkbox"
name="vanilla"
onChange={handleCheckboxChange('icecreamFlavors','vanilla')}
/>
<br />
<input
type="checkbox"
name:"chocolate"
onChange={handleCheckboxChange('icecreamFlavors','chocolate')}
/>
<br />
<input
type="checkbox"
name:"mintChocolateChip"
onChange={handleCheckboxChange('icecreamFlavors','mintChocolateChip')}
/>
<br />
<input
type="checkbox"
name:"sprinkles"
onChange={handleCheckboxChange('toppings','sprinkles')}
/>
<br />
<input
type="checkbox"
name:"caramel"
onChange={handleCheckboxChange('toppings','caramel')}
/>
What I want to achieve is to change the "handleCheckboxChange" method to work something like this (this code does not run...)
handleCheckboxChange = (parentProperty: any, property: any) => (e: { target: { checked: any} }) => {
this.setState({[parentProperty]: {...this.state.parentProperty, [property]: e.target.checked} })
};
so that ...this.state.icecreamFlavors is dynamic like ...this.state.parentProperty
I have made some attempts like this.setState({[parentProperty]: {...[parentProperty], [property]: e.target.checked} }) or this.setState({[parentProperty]: {parentProperty, [input]: e.target.checked} }) but none of my attempts gives me the result I want.
If any more context or information is required please let me know.
Thanks in advance!!!
Can u try this
const newObj = this.state[parentProperty];
newObj[input] = e.target.checked;
this.setState({[parentProperty]: newObj});

Why do I see [object Object] in my React input field instead of placeholder text?

I got the problem when implementing form input validation in React.
Once I open the form, there is always [object object] appear as the default input.
Here is the display:
Ideally, I want to have the below display:
And here is my code, any idea how shall I fix it?
const ErrorValidationLabel = ({ txtLbl }) => (
<label htmlFor="" style={{ color: "red" }}>
{txtLbl}
</label>
);
const txtFieldState = {
value: "",
valid:true,
}
class Setting extends Component {
constructor() {
this.state = {
name: {...txtFieldState, fileName:"name", required:true, requiredTxt:"Device ID is required"} ,
}
this.handleNameChange = this.handleNameChange.bind(this);
this.handleNameSearch = this.handleNameSearch.bind(this);
handleNameChange(event) {
this.setState({ name: event.target.value });
}
handleNameSearch(event) {
//http request call logic
}
}
render() {
const renderNameError = this.state.name.valid? "" : <ErrorValidationLabel txtLbl={this.state.name.requiredTxt} />;
return (
<form onSubmit={this.handleNameSearch}>
<label >
Enter Your Device Id:
</label>
<Input name="name" type="text" placeholder="ex:12345678910" value={this.state.name} onChange={this.handleNameChange} required/>
{renderNameError}
<Input type="submit" value="Search Device" />
</form>
);
}
You're setting the value of your input to this.state.name, but this.state.name is an object.
this.state = {
name: {...txtFieldState, fileName:"name", required:true, requiredTxt:"Device ID is required"}
}
You should set it to this.state.name.value.
I should warn you, however, that the code this.setState({ name: event.target.value }) will overwrite your entire name object, which likely isn't what you want.
You're using a spread operator already, so I would suggest
this.setState(state => ({ name: { ...state.name, value: event.target.value } }))`.

How to properly use an autocomplete input field based on a database source in Vue?

I want to use an input field (or something similar) that suggests an autocomplete, based on records from a data source.
In Vue I retrieve an array from a database table containing 3000+ records:
data(){
return{
inboundRelation_Trelation_data: [],
}
},
mounted(){
axios.get('/app/wms/allRelations', {
params: {
"dbConn": this.connString,
}
})
.then(response => this.inboundRelation_Trelation_data = response.data);
},
Based on this data, I want an autocomplete input field and/or dropdown. I've found 2 approaches online.. 1:
<select v-model="form.CUSTOMER_NAME">
<option v-for="(relation, index) in inboundRelation_Trelation_data" :value="relation.RELATIONCODE" v-text="relation.COMPANYNAME + ' | ' + relation.RELATIONCODE"></option>
</select>
This populates a dropdown, but my users experience this as tedious, as they need to type their letters quickly, otherwise after a small pause (like <0.5s), the next typed letter will start a new search and the results are inconsistent.
The other approach is using a data-list:
<input list="allRelations" type="text" #focus="$event.target.select()" v-model="form.CUSTOMER_NAME">
<datalist id="allRelations">
<option v-for="(relation, index) in inboundRelation_Trelation_data" :value="relation.RELATIONCODE" v-text="relation.COMPANYNAME + ' | ' + relation.RELATIONCODE"></option>
</datalist>
This works perfectly for small amounts of data. But when dealing with 100+ records (or 3000+ in this case), the whole browser freezes upon typing a letter. For some reason this is a very resource-heavy implementation. I've found some people with similar issues, but no solutions.
At the end of the day, I just want my users to be able to search in a huge list of 3000+ records. How do I approach this?
You can use vue-autosuggest package by this github link :
https://github.com/darrenjennings/vue-autosuggest
I am using this package and my data loads as my expect.
This is the template that you can use:
<template>
<div class="autosuggest-container">
<vue-autosuggest
v-model="form.CUSTOMER_NAME"
:suggestions="filteredOptions"
#focus="focusMe"
#click="clickHandler"
#input="onInputChange"
#selected="onSelected"
:get-suggestion-value="getSuggestionValue"
:input-props="{
class: 'form-control',
id: 'autosuggest__input',
field: 'CUSTOMER_NAME',
placeholder: 'Enter customer name for auto suggest',
}"
>
<div
slot-scope="{ suggestion }"
style="display: flex; align-items: center"
>
<img
:style="{
display: 'flex',
width: '25px',
height: '25px',
borderRadius: '15px',
marginLeft: '10px',
}"
:src="suggestion.item.avatar"
/>
<div style="{ display: 'flex', color: 'navyblue'}">
{{ suggestion.item.CUSTOMER_NAME }}
</div>
</div>
</vue-autosuggest>
</div>
And the Script section:
<script>
export default {
data() {
return {
searching: false,
query: '',
selected: '',
suggestions: [],
}
},
computed: {
filteredOptions() {
return [
{
data: this.suggestions.filter((option) => {
return (
option.name.toLowerCase().indexOf(this.query.toLowerCase()) > -1
)
}),
},
]
},
},
methods: {
clickHandler() {
//
},
onSelected(item) {
this.selected = item.item
},
async onInputChange(text = '') {
this.searching = true
await this.$axios
.get(`/app/wms/allRelations`,{
params: {
"dbConn": this.connString,
})
.then((res) => {
this.suggestions = res.data.data
})
.catch((e) => console.log(e))
.finally(() => (this.searching = false))
},
getSuggestionValue(suggestion) {
return suggestion.item.name
},
focusMe(e) {
this.onInputChange()
},
},
}
</script>
If still your browser freeze, you have to change your API response limit to something like descended 10 items.

React: Passing a Prop in w/ event for setState

I'm working with a nested state object that I have been updating with onChange functions, like so:
const [someState, setSomeState] = useState({
customer: [
{
name: "Bob",
address: "1234 Main Street",
email: "bob#mail.com",
phone: [
{
mobile: "555-5555",
home: "555-5555"
}
]
}
]
});
const updateSomeStatePhone = e => {
e.persist();
setSomeState(prevState => {
prevState.customer[0].phone[0].mobile = e.target.value;
return {
...prevState
};
});
};
<p>Update Mobile Number<p>
<select
value={someState.customer[0].phone[0].mobile}
onChange={updateSomeStatePhone}
>
<option value="123-4567">"123-4567"</option>
</select>
This gets the trick done. Currently however, if I want to update multiple state properties via a large form with dropdowns/input fields etc, I have to hard code 6 different onChange handlers for those fields.
Instead, I would prefer to have only one onChange handler, and pass in the state from the form field for the state property that I am changing, but I can't figure out the syntax:
const updateSomeState = (e, prop) => {
e.persist();
setSomeState(prevState => {
prevState.prop = e.target.value;
return {
...prevState
};
});
};
<p>Update Mobile Number<p>
<select
value={someState.customer[0].phone[0].mobile}
onChange={updateSomeState(e, prop)}
>
<option value="123-4567">"123-4567"</option>
</select>
I've tried using different types of syntax to chain the passed in 'prop' value to prevState:
prevState.prop = e.target.value;
prevState.(prop) = e.target.value;
${prevState} + '.' + ${prop} = e.target.value; // Dumb, I know
But the function never recognizes the "prop" that I pass in from the function. I'm sure there must be a simple way to do this. Any help would be greatly appreciated.
Does it have to be a single useState hook? I would recommend using useReducer or simplifying it a bit with multiple useState hooks.
Multiple useState hooks
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const [name, setName] = React.useState("");
const [address, setAddress] = React.useState("");
const [email, setEmail] = React.useState("");
const [mobile, setMobile] = React.useState("");
const [home, setHome] = React.useState("");
const getResult = () => ({
customer: [
{
name,
address,
email,
phone: [
{
mobile,
home
}
]
}
]
});
// Do whatever you need to do with this
console.log(getResult());
return (
<>
<input
value={name}
placeholder="name"
onChange={e => setName(e.target.value)}
/>
<br />
<input
value={address}
placeholder="address"
onChange={e => setAddress(e.target.value)}
/>
<br />
<input
value={email}
placeholder="email"
onChange={e => setEmail(e.target.value)}
/>
<br />
<input
value={mobile}
placeholder="mobile"
onChange={e => setMobile(e.target.value)}
/>
<br />
<input
value={home}
placeholder="home"
onChange={e => setHome(e.target.value)}
/>
</>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Single useReducer (with simplified state)
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
const reducer = (state, action) => {
const { type, value } = action;
switch (type) {
case "SET_NAME":
return { ...state, name: value };
case "SET_ADDRESS":
return { ...state, address: value };
case "SET_EMAIL":
return { ...state, email: value };
case "SET_MOBILE":
return { ...state, phone: [{ ...state.phone[0], mobile: value }] };
case "SET_HOME":
return { ...state, phone: [{ ...state.phone[0], home: value }] };
default:
throw Error(`Unexpected action: ${action.type}`);
}
};
const initialState = {
name: "",
address: "",
email: "",
phone: [
{
mobile: "",
home: ""
}
]
};
function App() {
const [state, dispatch] = React.useReducer(reducer, initialState);
// Do what you need with state
console.log(state);
return (
<>
<input
value={state.name}
placeholder="name"
onChange={({ target: { value } }) =>
dispatch({ type: "SET_NAME", value })
}
/>
<br />
<input
value={state.address}
placeholder="address"
onChange={({ target: { value } }) =>
dispatch({ type: "SET_ADDRESS", value })
}
/>
<br />
<input
value={state.email}
placeholder="email"
onChange={({ target: { value } }) =>
dispatch({ type: "SET_EMAIL", value })
}
/>
<br />
<input
value={state.phone.mobile}
placeholder="mobile"
onChange={({ target: { value } }) =>
dispatch({ type: "SET_MOBILE", value })
}
/>
<br />
<input
value={state.phone.home}
placeholder="home"
onChange={({ target: { value } }) =>
dispatch({ type: "SET_HOME", value })
}
/>
</>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
useReducer is a better choice for doing this. Examples all over the internet.
Why you shouldn't use useState to pass an object is because it doesn't act like setState. The underlying object reference is the same. Therefore, react will never trigger a state change. In case you want to use the same useState for objects. You may have to implement your own version to extend that (example below ) or you can directly use useReducer hook to achieve the same.
Here's an example with useState for you to notice the state update on every change.
const [form, setValues] = useState({
username: "",
password: ""
});
const updateField = e => {
setValues({
...form,
[e.target.name]: e.target.value
});
};
Notice the ...form in there. You can do it this in every update you want or you can use your own utility or useReducer as I mentioned.
Now coming to your code, there are other concerns.
You are using your phone as an array which can be an object. Or better yet separate properties will do as well. No harm.
If you have customers as an array, you have to loop through the records. Not just update the index by hardcoding. If there's only one customer better not keep the array but just an object. Assuming it is an array of customers, and you are looping through it, here's how to update mobile.
const updatedCustomers = state.customers.map(item => {
const { phone } = item;
return { ...item, phone: { mobile: e.target.value }};
// returns newCustomer object with updated mobile property
});
// Then go ahead and call `setSomeState ` from `useState`
setSomeState(...someState, { customer: updatedCustomers });// newState in your case is
Instead, I would prefer to have only one onChange handler, and pass in
the state from the form field for the state property that I am
changing, but I can't figure out the syntax
If you haven't figured that out from the first example. Here's how in short steps.
Give your HTML element a name attribute.
Then instead use the [e.target.name]
return { ...item, phone: { [e.target.name]: e.target.value }};
Use lodash's _.set helper.
const updateSomeState = (e, prop) => {
e.persist();
setSomeState(prevState => {
let customers = [...prevState.customers] // make a copy of array
let customer = {...customers[0]} // make a copy of customer object
_.set(customer, prop, e.target.value)
customers[0] = customer;
return {
...prevState, customers
};
});
};
BTW, in your existing updateSomeStatePhone you are modifying prevState object which is supposed to be immutable.

How to use Filter with ReactJS to prevent duplicates in an array from being displayed

I have a ReactJS page with three dropdown list, two of the dropdown list are displaying duplicate values. The values are being consumed from a json file. I researched using filter to remove the duplicates, but I'm unsure as to how I'm to apply it to my array when using React JS along with Fetch.
I created a function which employs the filter method, but I'm uncertain as to how I'm to implement it onto data: [], which contains the data consumed from the json file. This is the sample json file: https://api.myjson.com/bins/b1i6q
This is my code:
import React, { Component } from "react";
class Ast extends Component {
constructor() {
super();
this.state = {
data: [],
cfmRateFactor: "10"
};
} //end constructor
change = e => {
this.setState({
[e.target.name]: e.target.value
});
}; //end change
removeDups(array) {
return array.reduce((result, elem) => {
if (!result.some((e) => e.clientName === elem.clientName)) {
result.push(elem);
}
return result;
} , []);
}
componentWillMount() {
fetch("https://api.myjson.com/bins/b1i6q", {
method: "GET",
headers: {
Accept: "application/json",
"Content-type": "application/json"
}
/*body: JSON.stringify({
username: '{userName}',
password: '{password}'
})*/
}) /*end fetch */
.then(results => results.json())
.then(data => this.setState({ data: data }));
} //end life cycle
render() {
console.log(this.state.data);
return (
<div>
<div className="container">
<div className="astContainer">
<form>
<div>
<h2>Memeber Selection:</h2>
{["clientName", "siteName", "segmentName"].map(key => (
<div className="dropdown-padding">
<select key={key} className="custom-select">
{this.state.data.map(({ [key]: value }) => (
<option key={value}>{value}</option>
))}
</select>
</div>
))}
</div>
<div className="txt_cfm">
<label for="example-text-input">Modify CFM Rate Factor:</label>
<input
class="form-control"
type="textbox"
id="cfmRateFactor"
name="cfmRateFactor"
value={this.state.cfmRateFactor}
onChange={e => this.change(e)}
/>
</div>
<div>
<div>
<button type="submit" className="btn btn-primary">
Submit
</button>
</div>
</div>
</form>
</div>
</div>
</div>
);
}
}
export default Ast;
Could I please get some help with this? I'm still very new to using React JS.
You could use Map, it's a data structure for keeping key-value pairs. It will give you best performance for large data.
removeDuplicates(arr) {
const map = new Map();
arr.forEach(v => map.set(v.abc_buildingid, v)) // having `abc_buildingid` is always unique
return [...map.values()];
}
// this hook is better to start fetching data than componentWillMount
componentDidMount() {
fetch("https://api.myjson.com/bins/b1i6q", {
method: "GET",
headers: {
Accept: "application/json",
"Content-type": "application/json"
}
})
.then(results => results.json())
.then(data => this.setState({ data: this.removeDuplicates(data) })); // use the defined method
} //end life cycle
filter won't solve your problem. But reduce will.
You could have the following :
function removeDups(array) {
return array.reduce((result, elem) => {
if (!result.some((e) => e.abc_buildingid === element.abc_buildingid)) {
result.push(elem);
}
return result;
} , []);
}