editable json and setState in react - javascript

I try to display bunch of json object using map within a textarea. User can edit it, but I have to validate all the json is valid before it get pass to my server.
I'm having problem parsing it, where should I parse it? Parsing it on the onChange it a way but it's dangerous.
onChange = (e, idx) => {
this.setState({
data: this.state.data.map((o,i) => {
if(i === idx){
return JSON.parse(e.target.value) //dangerous
}
return o
})
})
}
https://codesandbox.io/s/880414y0m0

You can do something like this:
onChange = (e, idx) => {
let dataToSet = [...this.state.data]
let error = false
try {
dataToSet = dataToSet.map((o,i) => {
if(i === idx){
return JSON.parse(e.target.value) //dangerous
}
return o
})
} catch(e) {
error = true
}
this.setState({
data: dataToSet,
error,
})
}
Here is the codesandbox link which handles those scenario. codesandbox

Related

multiple http.post in Angular

I am trying to send an http.post request for each element of an array, my method works well, but when I subscribe, it does it for each of the requests, if someone could help me optimize this, I will I would really appreciate it, here I leave the snippets of my code.
component.ts
saveExclusion() {
this.indForm.value.Centers.forEach(element => {
for (const days of this.exclusionDays) {
delete days.horadesde;
delete days.horahasta;
delete days.id;
for (const key in days) {
if (days[key] === true) {
days[key] = true;
}else if (days[key] === false) {
delete days[key];
}
}
}
const valueForm = this.indForm.value;
valueForm.ResourceId = this.idResource;
valueForm.TimeZoneId = 'America/Santiago';
valueForm.CenterId = element;
this.exclusionFunc = false;
this.apiFca.saveNew(valueForm, this.exclusionDays)
.pipe(last()).subscribe((res: any) => {
console.log(res)
if (res === '200') {
this.successMessage = true;
this.exclusionDays = [];
this.indForm.reset();
this.ngOnInit();
setTimeout(() => {
this.successMessage = false;
}, 3000);
}
}, err => {
console.log('error', err);
});
});
}
service.ts
saveNew(exclusionData, daysBlock) {
let reason = '';
const dt = new Date();
const n = dt.getTimezoneOffset();
const tz = new Date(n * 1000).toISOString().substr(14, 5);
if (exclusionData.OtherReason) {
reason = exclusionData.ExclusionReason + ' ' + exclusionData.OtherReason;
} else {
reason = exclusionData.ExclusionReason;
}
if (exclusionData.ExclusionType !== 'Partial' ) {
daysBlock = [];
}
const data = {Exclusion: new ExclusionClass(
[],
reason,
exclusionData.ExclusionType,
exclusionData.Repetition,
exclusionData.CenterId,
exclusionData.ProfessionalName,
exclusionData.ResourceId,
daysBlock,
exclusionData.TimeZoneId,
'Exclude',
exclusionData.Unit,
exclusionData.ValidFrom + 'T' + exclusionData.ValidTimeFrom + ':00-' + tz,
exclusionData.ValidTo + 'T' + exclusionData.ValidTimeUntil + ':59.999-' + tz
)};
if (exclusionData.CenterId === '') {
delete data.Exclusion.CenterId;
}
return this.http
.post("url", data)
.pipe(
map((res: any) => {
return res.code;
})
);
}
greetings, and I look forward to your comments, thanks.
I'm not fully confident in my rxjs knowledge but it looks like, because of .pipe(last()), you are only watching the last request? I'd recommend you only set success if all completed without error, like
this.apiFca.saveNew(valueForm, this.exclusionDelays)
.subscribe(
res => {
console.log(res);
},
err => {
console.log(err);
},
() => {
this.successMessage = true;
// etc. etc. etc.
});
or maybe instead of using this.successMessage use something like this.saveState$ that would be the a BehaviorSubject object initialized with 'idle' (or some enum thereof) that your saveExclusion() function manages. That way, the beginning of your saveExclusion() function could
set const saveState$ = this.saveState$
assert that saveState$.getValue() === 'in process' or if not, do something about it,
saveState$.next('in process');
and you could change your subscribe line to
this.apiFca.saveNew(valueForm, this.exclusionDelays)
.subscribe(
res => {
if (res !== '200') {
saveState$.next('unexpected result')
} },
err => {
console.log(err);
saveState$.next('error');
},
() => {
if (saveState$.getValue() === 'in process') {
saveState$.next('success');
} }
);
And then you can subscribe to your component's saveState$ as well (though outside of the component you'd want to provide saveState$.asObservable() so values can't be injected by outside code). This affords some elegant event-driven code in your component initialization:
saveState$.pipe(filter(val => val === 'error'))
.subscribe(functionToTellYourUserThereWasAnError);
// if successful, we want other code to know, but immediately change it back to 'idle' even if other code errors
saveState$.pipe(filter(val => val === 'success')
.subscribe(val => saveState$.next('idle'));
// upon success, reset me
saveState$.pipe(filter(val => val === 'success'))
.subscribe(
val => {
this.exclusionDays = [];
// etc. etc.
// setTimeout not needed because set to 'idle' in a different thread.
}
)
Plus, I think your template could reflect and change the UI in response to changes in saveState$ as well, so your save button can be enabled/disabled based on whether or not saveState is 'idle', etc.

Create keydown or keypress event with enter for select to navigate through results with keyboards in vanilla javascript

I'm trying to create a way to use keyboard to sort through a results list from an API call. I've got the search portion working, just can't figure out how to create the eventlistener.
This is my autocomp.js
export default class Autocomp {
constructor(rootEl, options = {}) {
this.rootEl = rootEl;
this.options = {
numRes: 10,
data: [],
...options,
};
this.init();
}
createResEl(res) {
const fragment = document.createDocumentFragment();
res.forEach((res) => {
const el = document.createElement('li');
el.classList.add('res');
el.textContent = res.text;
// Pass the value to the onSelect callback
el.addEventListener('click', () => {
const { onSelect } = this.options;
if (typeof onSelect === 'function') onSelect(result.value);
});
// This is where I'm attempting to create KeyDown
el.addEventListener('keydown', () => {
const { onSelect } = this.options;
if (typeof onSelect === 'function') onSelect(result.value);
});
fragment.appendChild(el);
});
return fragment;
}
And this is my index.js
function getAPI(api) {
fetch(api)
.then((res) => {return res.json() })
.then(function(data) {
let arr = data.map(user => ({
text: user.login,
value: user.id,
}));
window.data = arr;
})
.then(function() {
new Autocomplete(document.getElementById('guser'), {
data,
onSelect: (UserId) => {
console.log('selected github user id:', UserId);
},
});
})
.catch(error => console.error(error))
};
And lastly my index.html where it is rendered:
<div class="users-group form-group">
<label>Github User:</label>
<div id="guser"></div>
</div>
<script src='./index.js'></script>
Right now I'm able to click on an item from my results list and it will console.log as per my onSelect for the click eventListener. However the keyboard portion doesn't work and I'm not quite sure about what changes to make.
The 'type' parameter for your EventListener is not correct. You were trying to implement the keydown event:
// This is where I'm attempting to create onKeyDown
el.addEventListener('keydown', () => {
const { onSelect } = this.options;
if (typeof onSelect === 'function') onSelect(result.value);
});
All 'type' parameters are case-sensitive.
The correct syntax for the onkeydown property (which is also case-sensitive) is:
target.onkeydown = functionRef;

setState from .map inside componentDidMount()

I am trying to find a solution to using setState on mapped items inside componentDidMount.
I am using GraphQL along with Gatsby with many data items returned but require that on specific pathname is === to slug the state is updated in the component to the matching littleHotelierId.
propertyInit = () => {
const pathname = location.pathname;
return (
<StaticQuery
query={graphql`
query {
allContentfulProperties {
edges {
node {
id
slug
information {
littleHotelierId
}
}
}
}
}
`}
render={data => {
data.allContentfulProperties.edges.map(({ node: property }) => {
if (pathname === property.slug) {
!this.isCancelled &&
this.setState({
littleHotelierId: property.information.littleHotelierId
});
}
return null;
});
}}
/>
);
};
Then I am pulling this into componentDidMount as
componentDidMount() {
this.propertyInit();
}
not relevant but as reference this.isCancelled = true; is added to componentWillUnmount.
I don't receive any errors but if I console.log(littleHotelierId) I get nothing.
I did at first think that it may be because return is null so tried giving the map a const and returning as
render={data => {
data.allContentfulProperties.edges.map(({ node: property }) => {
if (pathname === property.slug) {
const littleHotelier =
!this.isCancelled &&
this.setState({
littleHotelierId: property.information.littleHotelierId
});
return littleHotelier;
}
});
}}
but this was unsuccessful too.
The Goal is for componentDidMount to map items returned in the GraphQL data as
componentDidMount() {
if (path1 === '/path-slug1') {
!this.isCancelled &&
this.setState({
littleHotelierId: 'path-id-1'
});
}
if (path2 === '/path-slug2') {
!this.isCancelled &&
this.setState({
littleHotelierId: 'path-id-2'
});
}
... // other items
}
I think the issue is that GraphQL is fetching data as asynchronous and this request not completed as componentDidMount() is called. If I console.log the data it is not returning anything to the console. How can I fix this?
I think you need to create some filtered data as a result of a map function. After you have filtered data you do setState({data: data}). It is not good to do multiple setState.
If your GraphQL returns promise then you can write something like the following:
componentDidMount() {
this.fetchData()
.then(data => {
const filteredData = data.filter(element =>
element.someProperty === propertyValue
);
this.setState({ data: filteredData });
})
}

Getting backend data in front reactJS

I am getting data from the backend to display it in the font like this
componentDidMount() {
const response = this.props.store.privateImputationData;
console.log(response);
}
It displays null in the console, now if i do a setTimeout it works!
componentDidMount() {
setTimeOut(() => {
const response = this.props.store.privateImputationData;
console.log(response);
}, 500);
}
This how i m getting data from my store:
#computed get ImputationData() {
return this.privateImputationData || {};
}
loadImputation = (diplayedImputations) => {
HttpClient.postJSON(this.apiDataUrl, diplayedImputations).then((result) => {
this.privateImputationData = result;
this.loadAdditionalData();
});
}
How can i do it without setTimeout?
You can use the state object: State and Lifecycle. Whenever the state changes, whatever component uses it, get's updated too.
this.state = {privateImputationData: null} //or some default
So in your code:
#computed get ImputationData() {
return this.privateImputationData || {};
}
loadImputation = (diplayedImputations) => {
HttpClient.postJSON(this.apiDataUrl, diplayedImputations).then((result) => {
this.setState({privateImputationData: result});
this.loadAdditionalData();
});
}
To use the value:
this.state.privateImputationData;

Getting the initial state after unchecked checkbox input with React

I am building a filter section where I am fetching data through my API and setting up a new state ‘[result]’. The process is simple since data are fetched on click event and with certain condition. Data are stocked in an array since the user is able to filter several input checked.
My goal is to remove a particular index when I unchecked a checkbox input and if array is empty then reset my state to the data it fetched when the page first loaded.
I heard lodash could help me building my feature though I don't know how to implement on my react-app.
How could I build this process ?
Here’s my code :
handleFilterButtons = event => {
var filterIcons = event.target.getAttribute("name");
var valueInput = event.target.value;
var apiFilters = `https://backend.greatsaigon.com/api/v1/en/directories/${
this.state.directory
}/venues`;
axios
.get(apiFilters)
.then(response => {
let result;
let prevResult = [];
let toggleButton;
response.data.forEach(req => {
if (filterIcons === "Hair") {
if (req.options.beauty.hair.treatment !== "0") {
req.options.beauty.hair.treatment.forEach(res => {
if (res === valueInput && toggleButton !== undefined) {
result = req;
this.state.stockResult.unshift(result);
}
});
}
} else if (filterIcons === "Nails") {
if (req.options.beauty.nails.treatment !== "0") {
req.options.beauty.nails.treatment.forEach(res => {
if (res === valueInput) {
result = req;
this.state.stockResult.unshift(result);
}
});
}
} else if (filterIcons === "Spa & Massage") {
if (req.options.beauty.spa.treatment !== "0") {
req.options.beauty.spa.treatment.forEach(res => {
if (res === valueInput) {
result = req;
this.state.stockResult.unshift(result);
}
});
}
}
});
this.setState({
result: _.uniq(this.state.stockResult),
isToggle: toggleButton
});
})
.catch(function(error) {
console.log(error);
});
};

Categories

Resources