react async select trouble. Select creation in lopp - html

I have some troubles with asyncselect. Idea is to make a loop with many select fields with names as html array. Like fields[index], field_condition[index]. So for now it loads data into first select, but it doesn't set a value, and it's not loading second select(it take field it and loads data based on that field id), I've checked and all requests seems to be good. Two questions: How to make html arrays using asyncselect and why it doesn't load into second select?
Request for first asyncselect:
loadFields(inputValue) {
const { cookies, getFields } = this.props;
return new Promise(resolve => {
getFields({
headers: {
token: cookies.get('token')
},
per_page: 20,
page: 1,
dispatchAction: false,
resolve: (response) => {
resolve(filter(inputValue, response.data.results.map((field) => ({
...field,
value: field.id,
label: field.name,
}))));
},
reject: (error) => {
resolve(error);
}
});
});
}
Here is my second request that take additional argument(not working at all):
loadFieldConditions(inputValue, filter_id) {
const { cookies, getFieldConditions } = this.props;
return new Promise(resolve => {
getFieldConditions({
headers: {
token: cookies.get('token')
},
field_id: filter_id,
per_page: 20,
page: 1,
dispatchAction: false,
resolve: (response) => {
resolve(filter(inputValue, response.data.results.map((condition) => ({
...condition,
value: condition.id,
label: condition.name,
}))));
},
reject: (error) => {
resolve(error);
}
});
});
}
filter.fields.map((item, index) => {
return <div>
<div className="filter-item">
{/* <span class="missive-column-auto text-e">•</span> */}
<div className="filter-field">
<AsyncSelect
value={ fields[index] }
// onMenuClose={ closeDropdown.bind(null, item.field.id) }
onChange={ onChange.bind(null, 'fields['+index+']') }
className="react-select-container"
ref={(node) => this.fields[index] = node }
loadOptions={ loadFields }
classNamePrefix="react-select"
placeholder="Select field"
blurInputOnSelect={ true }
defaultOptions
cacheOptions
isClearable
/>
</div>
<div className="filter-field">
<AsyncSelect
value={ item.field_condition.id }
// onMenuClose={ closeDropdown.bind(null, item.field.id) }
// onChange={ onChange.bind(null, 'fields', item.field.id) }
className="react-select-container"
// ref={(node) => item.field.id = node }
loadOptions={(inputValue) => { loadFieldConditions(inputValue, item.field.id) }}
classNamePrefix="react-select"
placeholder="Select condition"
blurInputOnSelect={ true }
defaultOptions
cacheOptions
isClearable
/>
</div>

Related

React runs via Axios Mysql Query twice

I'm running into a double execution of my axios request in a functional react-app, which by random inserts 1 or 2 rows instead of always just 1 row into the database. Tried to wrap it in a useEffect-Hook...did not help. By logging the execution function it seems only to run once. But on the php-side it's kind of executed twice. The strange thing is, that I've implemented the same thing in two other parts of the app (just different items) and there the same code just works fine...any help very appreciated! Thx in advance!
Js-Code in React:
function ReservationObjectsDialogAdd() {
const appState = useContext(StateContext)
const appDispatch = useContext(DispatchContext)
const [name, setName] = useState()
const handleKeyPressAddDialog = e => {
if (e.which == 13) {
setReservationObject()
}
}
// add new category
async function setReservationObject() {
try {
// set new Category
const response = await Axios.post("/Main.php?cmd=setReservationObject", { name })
console.log(response.data)
appDispatch({ type: "getReservationObjects" })
appDispatch({ type: "setOpenAddDialog", data: false })
} catch (e) {
console.log(e.message)
console.log(lang.reservationObjectAddProblem)
}
}
return (
<Dialog open={appState.openAddDialog} onClose={e => appDispatch({ type: "setOpenAddDialog", data: false })} aria-labelledby="form-dialog-title">
<DialogTitle id="form-dialog-title">{lang.addTimeName}</DialogTitle>
<DialogContent>
<TextField onChange={e => setName(e.target.value)} autoFocus margin="dense" id="name" label={lang.timeName} type="text" fullWidth required={true} onKeyPress={handleKeyPressAddDialog} />
</DialogContent>
<DialogActions>
<Button onClick={e => appDispatch({ type: "setOpenAddDialog", data: false })} color="primary">
{lang.cancel}
</Button>
<Button onClick={setReservationObject} color="primary">
{lang.add}
</Button>
</DialogActions>
</Dialog>
)
}
export default ReservationObjectsDialogAdd
PHP-Side:
case "setReservationObject":
$conn = new DBConnection($host, $dbuser, $dbpassword, $db);
$post = json_decode(file_get_contents('php://input'), true);
$maxOrder = $conn->query("SELECT MAX(orderNumber) as maxorder FROM reservationObjects", [])->fetch(PDO::FETCH_ASSOC);
$maxOrder = $maxOrder['maxorder'] + 1;
$activeCategory = $conn->query("SELECT id FROM reservationCategories WHERE active=?", [1])->fetch(PDO::FETCH_ASSOC);
$conn->query("INSERT INTO reservationObjects (category,name,orderNumber,visible) values(?,?,?,?)", [$activeCategory['id'], $post['name'], $maxOrder, 1]);
break;
Here the rendering-code:
function ReservationObjects() {
const classes = useStyles()
const appState = useContext(StateContext)
const appDispatch = useContext(DispatchContext)
const [reservationObjects, setReservationObjects] = useState([])
const [images, setImages] = useState()
//sort categories
function onSortEnd({ oldIndex, newIndex }) {
let newReservationObjects = reservationObjects.map((el, i) => {
return el
})
newReservationObjects = arrayMove(newReservationObjects, oldIndex, newIndex)
setReservationObjects(newReservationObjects)
async function sortObjects(newReservationObjects) {
try {
// sort Data in DB
const response = await Axios.post("/Main.php?cmd=sortObjects", { reservationObjects: newReservationObjects })
appDispatch({ type: "getReservationObjects" })
appDispatch({ type: "getReservationItems" })
} catch (e) {
console.log(e.message)
console.log(lang.categorySortProblem)
}
}
sortObjects(newReservationObjects)
}
// sort events- part 1
function handleDragEndSortObjects(event) {
const { active, over } = event
if (active.id !== over.id) {
const tempReservationObjects = reservationObjects.map((el, i) => {
return el
})
let oldIndex = null
let newIndex = null
tempReservationObjects.map((el, i) => {
if (active.id == el.id) {
oldIndex = i
}
if (over.id == el.id) {
newIndex = i
}
})
onSortEnd({ oldIndex, newIndex })
}
}
function handleDragEndAssignObjects(event) {
console.log(event)
}
// in Sort-Mode check if the clicked target is a interface-entity
function shouldCancelStart(e) {
console.log("enter should cancel")
if (e.target.hasAttribute("isadmin")) {
if (e.target.attributes.isadmin.value) {
console.log("enter should cancel return false")
return false
}
}
if (e.target.hasAttribute("interface")) {
if (e.target.attributes.interface.value) {
console.log("enter should cancel return true")
return true
}
}
}
// initial loading of reservation objects
useEffect(() => {
async function getReservationObjects() {
try {
const response = await Axios.post("/Main.php?cmd=getReservationObjects", { isadmin: appState.isAdmin, category: appState.activeCategoryNumber }).then(response => {
setReservationObjects(response.data)
appDispatch({ type: "getReservationTimes" })
})
} catch (e) {
console.log(lang.reservationCategoriesProblem)
}
}
getReservationObjects()
}, [appState.getReservationObjectsTrigger])
//initial loading of images
useEffect(() => {
async function loadImages() {
try {
const response = await Axios.post("/Main.php?cmd=getImages")
//console.log(response.data)
setImages(response.data)
} catch (e) {
console.log(e.message)
}
}
loadImages()
}, [])
// handle mouse leave -> background Image
function handleObjectMouseLeave(e) {
appDispatch({ type: "setBackgroundImage", data: "" })
}
//handle mouse enter -> background Image
function handleObjectMouseEnter(e) {
if (e.target.hasAttribute("image")) {
let image = e.target.attributes.image.value
appDispatch({ type: "setBackgroundImage", data: image })
}
}
const sensors = useSensors(
useSensor(PointerSensor),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates
})
)
function Draggable(props) {
const { attributes, listeners, setNodeRef, transform, isDragging, over } = useDraggable({
id: props.id,
category: props.category
})
const style = transform
? {
transform: `translate3d(${transform.x}px, ${transform.y}px, 0)`
}
: undefined
return (
<div ref={setNodeRef} className="reservationArea__reservationObjectDraggable" style={style} {...listeners} {...attributes}>
<ReservationObject category={props.category} key={props.id} id={props.id} name={props.name} hover={appState.hoverObjectId == props.id ? "hovering" : ""} visible={props.visible} isadmin={appState.isAdmin.toString()} id={props.id} isactive={props.active} hovered={appState.reservationItems} image={props.image} onMouseEnter={handleObjectMouseEnter} onMouseLeave={handleObjectMouseLeave} />
</div>
)
}
function sortableVsDroppable() {
if (appState.objectsSortable) {
return (
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEndSortObjects}>
<SortableContext
items={reservationObjects.map(item => {
return item.id
})}
strategy={horizontalListSortingStrategy}
className="reservationArea__reservationObjects"
>
<div className="reservationArea__reservationObjects">
{reservationObjects.map((item, i) => (
<ReservationObject key={item.id} id={item.id} name={item.name} hover={appState.hoverObjectId == item.id ? "hovering" : ""} visible={item.visible} isadmin={appState.isAdmin.toString()} id={item.id} isactive={item.active} hovered={appState.reservationItems} image={item.image} onMouseEnter={handleObjectMouseEnter} onMouseLeave={handleObjectMouseLeave} />
))}
</div>
{appState.isAdmin ? (
<Link to="/" onClick={e => appDispatch({ type: "setOpenAddDialog", data: true })} className="reservationArea__addObject">
<AddCircleOutlineIcon />
</Link>
) : (
""
)}
</SortableContext>
</DndContext>
)
} else {
console.log("assignable")
return (
<>
<div className="reservationArea__reservationObjects">
{reservationObjects.map((item, i) => (
<Draggable key={item.id} category={item.category} id={item.id} index={item.id} name={item.name} hover={appState.hoverObjectId == item.id ? "hovering" : ""} visible={item.visible} isadmin={appState.isAdmin.toString()} id={item.id} isactive={item.active} hovered={appState.reservationItems} image={item.image} onMouseEnter={handleObjectMouseEnter} onMouseLeave={handleObjectMouseLeave} />
))}
</div>
{appState.isAdmin ? (
<Link to="/" onClick={e => appDispatch({ type: "setOpenAddDialog", data: true })} className="reservationArea__addObject">
<AddCircleOutlineIcon />
</Link>
) : (
""
)}
</>
)
}
}
return (
<div className="reservationArea__reservationObjectsContainer">
<ReservationObjectsImage />
{sortableVsDroppable()}
<ReservationObjectsDialogAdd />
<ReservationObjectsDialogEdit />
<ReservationObjectsDialogDelete />
</div>
)
}
export default ReservationObjects
Finally solved. It was in the php-part. I replaced the fetching of the activeCategory directly in mysql with a parameter that I sent with Axios. Somehow the activeCategory-fetching led to to this strange behaviour, that it randmly executed the insert-statement once or twice.

Checkbox is NOT check on click (React)

I created a todo list by using react. I get some problem that I want to create checkbox but my checkbox it does not work and I cannot solve :( I don't know what's wrong with that.
I set the data for each task and then I need to change the completed of some task, but it cannot click and change the completed task
This is my code
class App extends React.Component {
constructor() {
super()
this.state = {
todos: todoData,
}
this.handleChange = this.handleChange.bind(this)
}
handleChange(id) {
this.setState(prevState => {
const updatedTodos = prevState.todos.map(todo => {
if(todo.id === id) {
todo.completed = !todo.completed
// console.log(todo.completed)
}
return todo
})
return {
todos: updatedTodos
}
})
}
render() {
const todoItem = this.state.todos.map(item => <TodoItem key={item.id} item={item}
handleChange={this.handleChange}/>)
return (
<div>
<h1 className="header">My Todo Lists</h1>
{todoItem}
</div>
)
}
}
function TodoItem(props) {
let textItem = props.item.completed === true ?
<del>{props.item.text}</del> : props.item.text
return (
<div className="list">
<input
type="checkbox"
checked={props.item.completed}
onChange={() => props.handleChange(props.item.id)}
/>
<p className="item">{textItem}</p>
</div>
)
}
And this is my data
const todoData = [
{
id: 1,
text: "Practice coding",
completed: false
},
{
id: 2,
text: "Grocery shopping",
completed: true
},
{
id: 3,
text: "Wash the dishes",
completed: true
},
{
id: 4,
text: "Take out the trash",
completed: false
},
{
id: 5,
text: "Teach my brother homework",
completed: false
}
]
Thank you for helping :)
Looks like on your handleChange you are mutating the existing state on your map transformation. you must return a new state instead.
Replace your handleChange with the following code:
handleChange(id) {
this.setState((prevState) => {
const updatedTodos = prevState.todos.map((todo) => {
return {
...todo,
completed: todo.id === id ? !todo.completed : todo.completed
};
});
return {
todos: updatedTodos
};
});
}

How to close autocomplete dropdown when clicked outside?

How to close autocomplete dropdown when clicked on any outside area? Currently i am calling my custom autocomplete component(child presentation component) 3 times with different labels from another vue page(parent) called departmentDetail.vue.
Example:
departmentDetail.vue
<b-field label="Custom Business Unit ">
<AutoComplete :method="getAsyncDataBusinessUnit" title='businessUnit' :autocompleteData="dataBusinessUnit" viewname='DepartmentDetail'>
</AutoComplete>
</b-field>
<b-field label="Custom Managers">
<AutoComplete :method="getAsyncData" title='manager' :autocompleteData="dataManager" viewname='DepartmentDetail'>
</AutoComplete>
</b-field>
<b-field label="Custom Location">
<AutoComplete :method=" getAsyncDataLocation" title='location' :autocompleteData="dataLocation" viewname='DepartmentDetail'>
</AutoComplete>
</b-field>
AutoComplete.vue (Custom component created by me)
<template>
<div class="autocomplete">
<input style="font-size: 12pt; height: 36px; width:1800px; " type="text" v-model="objectData[title]" #focus="getAsyncDataBusinessUnit" #input="getAsyncDataBusinessUnit"/>
<ul v-show="isFetching" >
<li v-for="(dataBusinessUnit, i) in dataBusinessUnit" :key="i" #click="setResult(dataBusinessUnit)" >
<template v-if="title!='manager'">
<div class="container">
<p>
<b>ID:</b>
{{dataBusinessUnit.id}}
</p>
<p>
<b>Description:</b>
{{dataBusinessUnit.description}}
</p>
</div>
</template>
<template v-else>
<div class="container">
<p>
<b>ID:</b>
{{dataBusinessUnit.id}}
</p>
<p>
<b>First Name:</b>
{{dataBusinessUnit.firstName}}
</p>
<p>
<b>Last Name:</b>
{{dataBusinessUnit.lastName}}
</p>
</div>
</template>
</li>
</ul>
</div>
</template>
<script>
import { viewMixin } from "../viewMixin.js";
import schemaData from '../store/schema';
import debounce from "lodash/debounce";
import api from "../store/api";
const ViewName = "AutoComplete";
var passedview;
export default {
name: "AutoComplete",
props: {
method: {
type: Function
},
title: String,
viewname:String,
autocompleteData: {
type: Array,
required: true
}
},
data() {
return {
// results: [],
dataBusinessUnit: [],
results: [],
isFetching: false
// vignesh: this.objectData[this.title]
};
},
computed: {
viewData() {
return this.$store.getters.getViewData('DepartmentDetail')
},
objectData() {
return this.$store.getters.getApiData(this.viewData.api_id).data
},
sessionData() {
return this.$store.getters.getSessionData()
},
isLoading() {
return this.$store.getters.getApiData(this.viewData.api_id).isLoading
},
newRecord() {
return this.$route.params.id === null;
},
getTitle() {
return this.title
}
},
mounted() {
},
methods: {
setResult(result) {
this.updateValue(result.id,this.title);
// localStorage.setItem(this.title,result.id );
this.isFetching = false;
},
updateValue(newValue, fieldName) {
var val;
var schema = schemaData[this.viewData.schema];
if(typeof schema!=='undefined' && schema['properties'][fieldName]['type'] == 'date'){
val = this.formatDate(newValue);
} else {
val = newValue;
}
this.$store.dispatch('updateDataObjectField', {
key: this.viewData.api_id,
field: fieldName,
value: val
});
},
getAsyncDataBusinessUnit: debounce(function(name) {
console.log('getAsyncDataBusinessUnit you typed'+name.target.value);
if (!name.target.value.length) {
// this.results = [];
// this.dataBusinessUnit = [...this.results];
// this.isFetching = false;
// return;
}
// this.isFetching = true;
api
.getSearchData(this.sessionData.key,`/businessunit/`,{ filter: `{id}like'%${name.target.value}%'` })
.then(response => {
this.results = [];
if (!response.length)
{
console.log('inside if ')
this.isFetching = false;
}
else{
console.log('inside else')
response.forEach(item => {
this.results.push(item);
});
// this.dataBusinessUnit=this.results
this.dataBusinessUnit = [...this.results];
this.isFetching = true;
}
console.log('length of dataBusinessUnit is '+(this.dataBusinessUnit).length)
console.log('contents of dataBusinessUnit array '+JSON.stringify(this.dataBusinessUnit))
})
.catch(error => {
//this.dataBusinessUnit = [];
throw error;
})
.finally(() => {
// this.isFetching = true;
});
}, 500),
},
components: {
}
};
</script>
Screenshot of the Department screen
And why is it that when the page loads sometimes the values dont show up in these input fields? But upon focus or if i type anything then sudddenly the value shows up?Any idea why this is happening?
About your last question:
"And why is it that when the page loads sometimes the values dont show up in these input fields? But upon focus or if i type anything then sudddenly the value shows up?Any idea why this is happening?"
It looks like you are usesing API requests on computed props.
Computed props are pre renderd values. If your API works async then the computed is renderd "empty" before the full request is resolved.
you could try data props and set them with the API setter in Mounted() or Created().
EDIT:
It could look something like this:
data() {
return {
// results: [],
dataBusinessUnit: [],
results: [],
viewData: [],
objectData:[],
sessionData:[],
isLoading: [],
isFetching: false
// vignesh: this.objectData[this.title]
};
},
computed: {
newRecord() {
return this.$route.params.id === null;
},
getTitle() {
return this.title
}
},
mounted() {
this.viewData = this.$store.getters.getViewData('DepartmentDetail');
this.objectData = this.$store.getters.getApiData(this.viewData.api_id).data;
this.sessionData = this.$store.getters.getSessionData();
this.isLoading = this.$store.getters.getApiData(this.viewData.api_id).isLoading;
},

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;
} , []);
}

React Map json data and create a dropdown

I have json data passed as props from the Redux store, the json has this format:
{
"label1.label2.label3": {
"option1":[
],
"option2": "value",
"option3": [
],
...
}
...
}
I need to split the label part and create a nested menu from the data as follows:
label1
label2
label3
option1
value of option1
option2
value of option2
option3
...
I couldn't figure out how to map into the data, my component code looks like this:
class MyDropdown extends React.Component {
componentDidMount(){
//A component dispatches ACTIONS
this.props.dispatch(fetchDataWithRedux());
}
render(){
const {error, loading, elements} = this.props;
if (loading){
return <div>Loading...</div>;
}
if (error){
return <div> Error! {error.message}</div>;
}
console.log(this.props.elements); //outputs "undefined"
return(
<div>
{this.props.elements && this.props.elements.map(this.createMenu)}
</div>
);
}
createMenu() {
return this.props.elements.map((item) => {
return(
);
});
}
}
const mapStateToProps = state => ({
elements: state.items,
loading: state.loading,
error: state.error
});
export default connect(mapStateToProps)(MyDropdown);
I'm getting real close with this. It's pretty tricky to get the nesting right though.
const data = {
"label1.label2.label3": {
"option1":"value1",
"option2": "value2",
"option3": "value3"
},
"label1.label2.label3.label4": {
"option1":"value1",
"option2": "value2"
},
}
class App extends React.Component {
createMenu = () => {
const options = Object.keys(data).reduce( (options, key, i) => {
const d = data[key];
let levels = key.split('.').map( (label, i) => {
return {
label: label,
options: []
};
});
levels[levels.length -1].options = Object.keys(d).map( option => {
return {
label: option,
value: d[option]
}
});
options.push(levels);
return options;
}, []);
const mapOptions = o => {
let menu = [];
o.forEach( (option, i) => {
if(!option.options.length) {
// Nest next option
menu.push(
<li>
{option.label}
</li>
);
}else {
// display options
menu.push(
<li>
{option.label}
{option.options.map( nested => {
return <li>{nested.label}: {nested.value}</li>
})}
</li>
);
}
});
return menu;
};
return options.map( option => {
return mapOptions(option);
});
};
render() {
return (
<div>
{this.createMenu()}
</div>
)
}
}
ReactDOM.render(<App/>, document.getElementById('root'));
li li {
margin-left: 15px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>