replace array object at perticular index in setState - reactjs

I have an update button which gets me the updated object and I have the index at which I want to replace the array with this object:
updateVal = () => {
console.log("this.state::", this.state);
let alleditvals = {
username: this.state.uname_edit,
password: this.state.pass_edit,
address: this.state.address_edit
};
console.log("w::", this.state.add_data[this.state.count_edit]);
console.log("alleditvals::", alleditvals);
console.log("this.state.add_data[1]", this.state.add_data[1]);
this.state.add_data.map((a, i) => {
console.log("i::", i);
console.log("a::", a);
if (this.state.count_edit == i) {
console.log(this.state.add_data[i]);
}
});
}
the state is like this:
this.state = {
uname: "",
pass: "",
address: "",
salary: "",
add_data: [],
edit_obj: {},
edit_showui: false,
uname_edit: "",
pass_edit: "",
address_edit: "",
count_edit: null,
changed_after_edit: {},
errorfields: {
uname_err: "",
pass_err: "",
address_err: "",
salary_err: "",
valid_user: false,
valid_pass: false,
valid_address: false,
valid_salary: false
}
};
so my array is add_data which i want to replace with alleditvals at index this.state.count_edit. I have also checked on map function that if the index of current count matched the add_data index, please replace it, but don't know how to replace it.
if(this.state.count_edit == i){
console.log(this.state.add_data[i])
}

You can update your component state with the setState method, and use the array method map on your add_data array and replace the element at index count_edit with the alleditvals object.
updateVal = () => {
let alleditvals = {
username: this.state.uname_edit,
password: this.state.pass_edit,
address: this.state.address_edit
};
this.setState(({ count_edit, add_data }) => ({
add_data: add_data.map((item, index) =>
index === count_edit ? alleditvals : item
)
}));
}

Thanks to setState, you can update state easily.
class App extends Component {
constructor(props){
super(props)
this.state = {
allData: [],
index: 0
}
}
componentWillMount()
{
this.state.allData.push({ name1: "Test1", name2: "Test2"});
}
push1 = () =>
{
this.setState({
index: 1
})
}
push2 = () =>
{
this.setState({
index: 2
})
}
render() {
const list = this.state.allData.map((value, index) => {
return (
<div>
{ this.state.index === 1? value.name1 : value.name2 }
</div>
)
})
return (
<div>
<button onClick={this.push1}>Push 1</button><br/><br/>
<button onClick={this.push2}>Push 2</button><br/><br/>
<div>
{list}
</div>
</div>
);
}
}
In this example I pressed Push2 = Test2 shows. If you press Push1, Test1 will appear.

Related

React: disable a filtered item

I am creating an activity, where a user needs to match two words on click. Like on the picture below.
If words match they should get disabled.
My state is following
this.state = {
data: [],
mixedWords: [],
myanswers: [],
allPairs: [],
checked: false,
isCorrect: false,
isIncorrect: false
};
For example myanswers array maybe like this.
["more than", "более"]
mixedWords array is the following
[{translation: "more than", disabled: false},
{translation: "capital", disabled: false},
{word: "более", disabled: false},
{translation: "famous", disabled: false},
{word: "проживает", disabled: false},
{translation: "is living", disabled: false},
{word: "известный", disabled: false},
{word: "столице", disabled: false}
]
This function is responsible for modifying disabled property. But the problem is that it outputs only filtered items. How can I output mixedWords array with modifyed disabled property for specific items
const myFunction = (value) => {
const mixedWords = [...this.state.mixedWords]
const result = mixedWords.filter(word => word.translation === value || word.word === value );
const newResult = Object.assign({}, result[0], { disabled:true })
this.setState({
mixedWords:[newResult]
})
}
this.state.myanswers.forEach(myFunction)
Full code
/* eslint-disable no-extend-native */
import React, { Component } from "react";
//import click from "../data/media/click.wav";
//import correct from "../data/media/correct.wav";
//import denied from "../data/media/denied.mp3";
let _ = require("lodash");
class Quiz extends Component {
constructor (props) {
super(props);
this.state = {
data: [],
mixedWords: [],
myanswers: [],
allPairs: [],
checked: false,
isCorrect: false,
isIncorrect: false
};
}
componentDidMount() {
let mixedWords = [];
let allPairs = [];
this.props.data.quiz && this.props.data.quiz.map((item) => {
mixedWords.push({word:item.word, disabled:false},{ translation:item.translation,disabled:false});
allPairs.push(item.pair);
return (mixedWords, allPairs);
});
this.setState({
data: this.props.data.quiz,
mixedWords: _.shuffle(mixedWords),
allPairs
});
//console.log(this.props.data);
}
selectWords = (e) => {
let items = e.target.value;
let myanswers = this.state.myanswers.concat(items);
this.setState({ myanswers }, () => {
if (this.state.myanswers.length === 2) {
if (this.checkAnswers(this.state.myanswers, this.state.allPairs)) {
console.log("correct");
const myFunction = (value) => {
const mixedWords = [...this.state.mixedWords]
const result = mixedWords.filter(word => word.translation === value || word.word === value );
const newResult = Object.assign({}, result[0], { disabled:true })
this.setState({
mixedWords:[newResult]
})
}
this.state.myanswers.forEach(myFunction)
this.setState({
myanswers:[]
})
} else {
console.log("incorrect");
this.setState({
myanswers:[]
})
}
} else {
console.log('choose a pair');
}
});
};
checkAnswers = (answersArr, allPairs) => {
let bools = []
allPairs.forEach((arr) => {
this.arraysEqual(answersArr, arr);
//console.log(this.arraysEqual(answersArr, arr));
//console.log(arr, this.state.myanswers);
bools.push(this.arraysEqual(answersArr, arr))
});
if (bools.includes(true)) {
return true
}
};
arraysEqual = (a, b) => {
return a.sort().toString() === b.sort().toString()
};
render() {
console.log(this.state.mixedWords);
console.log(this.state.myanswers);
//console.log(this.state.allPairs);
//console.log(this.state.myanswers.join(" ") === this.state.answers.join(" "));
return (
<div>
<div className="tags are-medium">
{ this.state.mixedWords.map((item) => (
<button disabled={item.disabled} value={ item.word || item.translation } onClick={ (e) => { this.selectWords(e); } } className="tag is-warning">{ item.word || item.translation }</button>
)) }
</div>
</div>
);
}
}
export default Quiz;
selectWords = (e) => {
let items = e.target.value;
let myanswers = this.state.myanswers.concat(items);
this.setState({ myanswers }, () => {
if (this.state.myanswers.length === 2) {
if (this.checkAnswers(this.state.myanswers, this.state.allPairs)) {
console.log("correct");
const myFunction = (value) => {
this.setState({
mixedWords:this.state.mixedWords.map(word => word.translation === value || word.word === value ? Object.assign({}, word, { disabled:true }) : word)
})
}
this.state.myanswers.forEach(myFunction)
this.setState({
myanswers:[]
})
} else {
console.log("incorrect");
this.setState({
myanswers:[]
})
}
} else {
console.log('choose a pair');
}
});
};

how to push a new element into an array from the state

I'm trying to push in elements into an array called 'this.state.tags'. On the console, I see the elements pushing into the array. However, when I add something, the array comes out blank, when I add more items I only the see the previous items I've added. I'm not seeing the newest item I've pushed in.
I've done Object.assign([], this.state.tags) from the child component Grades.js. Then I pushed in 'this.state.newTag' and I've reset the state to that new result.
//From Grades.js, the child component
state = {
toggle: null,
newTag: '',
tags: []
}
addTags = (event) => {
event.preventDefault();
let newTagArr = Object.assign([], this.state.tags)
newTagArr.push(this.state.newTag)
this.setState({
tags: newTagArr
})
// this will pass on to the parent
this.props.filterTags(this.state.tags)
}
render() {
const { tags } = this.state
let tagList = tags.map((item, index) => {
return (
<li key={index} className="tagListItem">{item}</li>
)
})
return(
<div>
<ul className="tagList">{tagList}</ul>
<form onSubmit={this.addTags}>
<input
placeholder="Add a tag"
name="newTag"
onChange={this.handleInput}
style={{border: '0', borderBottom: '1px solid #000'}}
/>
</form>
</div>
)
}
// From App.js, the parent component
state = {
students: [],
filteredStudents: [],
filteredByTag: [],
search: '',
tag: '',
toggle: false
}
componentDidMount() {
axios.get('https://www.hatchways.io/api/assessment/students')
.then(result => {
let newArr = Object.assign([], result.data.students);
let newResult = newArr.map(elem => {
return {city: elem.city, company: elem.company, email: elem.email,
firstName: elem.firstName.toLowerCase(), lastName: elem.lastName.toLowerCase(),
grades: elem.grades, id: elem.id, pic: elem.pic, skill: elem.skill}
})
this.setState({
students: newResult
})
})
.catch(err => console.log(err))
}
tagFiltering = (param) => {
console.log(param)
this.state.students.push()
}
I expect the output to be ["tag1", "tag2", "tag3"]
Not ["tag1", "tag2"], when I've already typed in tag1, tag2 and tag3
Use ES2015 syntax :
this.setState({
tags: [...this.state.tags , this.state.newTag]
})
In react the state is immutable meaning that we have to provide new state object every time, we call the setState method.

Function .filter() in ReactJs

There is JSON called by fetch() request looks like this:
[{
"Info": "",
"Val": "3"
},
{
"Info": "",
"Val": "5"
},
{
"Info": "",
"Val": "1"
},
{
"Info": "",
"Val": "1"
}]
My purpose is to filter data according a filed called Val.
library = library.filter(item =>
item.Val==FilterVal
)
Let me make an example to explain what I want to do.
Look at this input :<input value={this.state.FilterVal} onChange={this.handlerFilter} />
FilterVal is going to be a number for example 1 or some numbers separated by comma 1,5,4 .
For example user types 1 on input ,the result must return the objects that Valis 1. For the next time type 1,5,4must return me the objects that Val are 1 and 5 and 4.
Here is a piece of my code:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
library: null,
FilterVal: "",
}
}
componentDidMount() {
fetch('/json.bc', {
method: 'POST',
})
.then(response => response.text())
.then(text => {
const Maindata = JSON.parse(text.replace(/\'/g, '"'))
this.setState(state => ({
...state,
data: Maindata
}), () => {
this.reorganiseLibrary()
})
}).catch(error => console.error(error))
})
}
reorganiseLibrary = () => {
const { FilterVal} = this.state;
let library = data;
if (FilterVal !== "") {
library = library.filter(item =>
item.Val==FilterVal
)
}
library = _.chunk(library);
this.setState({
library
})
}
handlerFilter = evt =>
this.setState(
{
FilterVal: evt.target.value
},
() => {
this.reorganiseLibrary();
}
)
renderLibrary = () => {
const { library} = this.state;
if (!library || (library && library.length === 0)) {
return <div>nodata</div>
}
return library.map((item, i) => (
<div>
<span>{item.name}</span>
<span>{item.Val}</span>
</div>
))
}
render() {
const { library} = this.state;
return (
<div>
{this.renderLibrary()}
<input value={this.state.FilterVal} onChange={this.handlerFilter} />
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('Result'))
Just adjust your filter:
const values = FilterVal.split(',').map(v => Number(v))
library = library.filter(item => values.includes(item.Val))
You can use FilterVal.includes(item.Val). This is working solution.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
library: null,
FilterVal: ""
};
}
componentDidMount() {
var originalArray = [
{
Info: "",
Val: "3"
},
{
Info: "",
Val: "3"
},
{
Info: "",
Val: "2"
},
{
Info: "",
Val: "4"
},
{
Info: "",
Val: "4"
},
{
Info: "",
Val: "5"
},
{
Info: "",
Val: "2"
},
{
Info: "",
Val: "1"
}
];
this.setState({ data: originalArray });
}
reorganiseLibrary = () => {
let FilterVal = this.state.FilterVal;
let library = this.state.data;
if (FilterVal !== "") {
FilterVal = FilterVal.split("");
library = library.filter(item => {
if (FilterVal.includes(item.Val)) return item;
});
} else {
library = null;
}
// library = _.chunk(library);
this.setState({
library
});
};
handlerFilter = evt =>
this.setState(
{
FilterVal: evt.target.value
},
() => {
this.reorganiseLibrary();
}
);
renderLibrary = () => {
const { library } = this.state;
if (!library || (library && library.length === 0)) {
return <div>nodata</div>;
}
return library.map((item, i) => (
<div>
<span>{item.name}</span>
<span>{item.Val}</span>
</div>
));
};
render() {
const { library } = this.state;
return (
<div>
{this.renderLibrary()}
<input value={this.state.FilterVal} onChange={this.handlerFilter} />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("Result"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='Result' />

How to properly change state value of array of objects?

Imagine this variable:
let myArray = [
{
name: "animal",
value: "",
},
{
name: "fruit",
value: "",
},
(...)
];
myArray is set in stone - it is hard-coded and its length wont change, but it is a lengthy array of 10 elements. A user will only update myArray objects values via html input. Based on above, can myArray be considered as a state in Svelte?
Is below example the correct way of changing myArray state in Svelte?
(...)
myArray.forEach(element => {
if (element.name === name) element.value = value;
});
I have a button state that its disabled attribute depends on all elements in myArray having some value. Can I use Sveltes $: btnIsDisabled reactive statements to achieve that and how?
<button type="submit" disabled={btnIsDisabled}>
Submit me
</button>
I'm assuming you plan on using your array as the component-state. And that you have an input corresponding to each field.
Try something like this: https://codesandbox.io/s/magical-fog-tfq3q
class App extends React.Component {
state = {
favorites: [
{ name: "animal", value: "" },
{ name: "city", value: "" },
{ name: "song", value: "" },
{ name: "place", value: "" },
{ name: "food", value: "" },
{ name: "sport", value: "" }
],
emptyFields: null
};
handleOnChange = event => {
const { favorites } = this.state;
let updatedArr = favorites.map(favorite => {
if (favorite.name === event.target.name) {
return {
...favorite,
value: event.target.value
};
} else {
return favorite;
}
});
let emptyFields = updatedArr.filter(favorite => {
return favorite.value.length === 0;
});
this.setState({
...this.state,
favorites: updatedArr,
emptyFields: emptyFields
});
};
createFavoriteInputs = () => {
const { favorites } = this.state;
return favorites.map(favorite => {
return (
<div key={favorite.name}>
<label>{favorite.name} :</label>
<input
value={favorite.value}
name={favorite.name}
onChange={this.handleOnChange}
/>
</div>
);
});
};
render() {
const { emptyFields } = this.state;
return (
<div>
{this.createFavoriteInputs()}
<button
disabled={!emptyFields || emptyFields.length > 0 ? true : false}
>
Submit
</button>
{!emptyFields ||
(emptyFields.length > 0 && (
<div>
The following fields are required:
<ul>
{this.state.emptyFields.map(field => {
return <li key={field.name}>{field.name}</li>;
})}
</ul>
</div>
))}
</div>
);
}
}
So now with the emptyFields state, we have a button that is disabled if there are any emptyFields.
handleOnChange() helps us navigate the right state-value to update in our array, creating a new array in our state whenever we make an update to one of the inputs on the form.

ReactJS - setting an inline style equal to a property on state is not working. What's going on?

I'm trying to get a FormWarning to display when users input incorrect information, but it seems to have disappeared on me. I'm trying to control whether or not it displays with this.state.formWarning.display - when the validateInputs function runs, if it determines an input is invalid, it should change the value of display from 'none' to 'block'. I'm trying to set the style for the Component to having a display that matches this.state.formWarning.display, but I am getting an error. Is my belief that you can set the styles for a component inline via an object not correct? Getting bugs regardless. ie
export default class FormOne extends React.Component {
constructor(props) {
super(props)
this.state = {
formOne: {
shippingAddress: {
firstName: '',
lastName: '',
address1: '',
city: '',
state: '',
zip: '',
country: 'US'
},
phone: '',
email: ''
},
formWarning: {
text: '',
invalidInputID: '',
display: 'block'
},
isSubmitted: false,
submitting: false
}
this.styles = this.props.styles || {}
}
componentWillReceiveProps(nextProps) {
if(nextProps.state.stepOne &&
nextProps.state.stepOne.formOneResponse) {
let formOneResponse = nextProps.state.stepOne.formOneResponse
formOneResponse.status === "delayed" || formOneResponse.status === "success"
? this.setState({isSubmitted: true})
: alert(formOneResponse.errorMessage)
this.setState(state => ({submitting: false}))
}
}
validateInputs = (inputs) => {
let { email, phone, shippingAddress } = inputs,
shippingKeys = Object.keys(shippingAddress)
console.log('validate inputs is firing')
for(let i = 0; i < Object.keys(shippingAddress).length; i++) {
let key = shippingKeys[i], input = shippingAddress[key]
if(!input) {
return this.showFormWarning(key)
}
}
if(!phone) return this.showFormWarning('phone')
if(/\S+#\S+\.\S+/.test(email)) return
this.showFormWarning('email')
return true
}
showFormWarning = key => {
clearTimeout(this.warningTimeout)
console.log('showformwarnign is firing')
this.setState(state => ({
formWarning: {
...state.formWarning,
text: 'Please fill out this field',
invalidInputID: key,
display: 'block'
}
}))
this.warningTimeout = setTimeout(() => {
this.setState(state => ({
formWarning: {
...state.formWarning,
display: 'none'
}
}))
}, 5000)
return false
}
saveInputVal = (event) => {
let { formOne: tempFormOne } = this.state,
input = event.currentTarget
console.log('saveinputvals is firing')
if(input.name === 'phone' || input.name === 'email') {
this.setState(state => ({
formOne: {
...state.formOne,
[input.name]: input.value
}
}))
} else {
this.setState(state => ({
formOne: {
...state.formOne,
shippingAddress: {
...state.formOne.shippingAddress,
[input.name]: input.value
}
}
}))
}
}
submit = (event) => {
event.preventDefault()
if(!this.validateInputs(this.state.formOne)) return
this.setState(state => ({submitting: true}))
this.props.saveShippingData(this.state.formOne)
this.props.stepOneSubmit(this.state.formOne)
}
render() {
if (this.state.isSubmitted) return <Redirect to="/order" />
let CustomTag = this.props.labels ? 'label' : 'span',
{ inputs, saveInputVal, styles, state } = this,
{ formWarning, submitting } = state,
{ invalidInputID, text, display } = formWarning
return (
<div style={this.styles.formWrapper}>
{
typeof this.props.headerText === 'string'
? ( <h2 style={this.styles.formHeader}>
{this.props.headerText}</h2> )
: this.props.headerText.map((text) => {
return <h2 key={text} style={this.styles.formHeader}
className={'header'+this.props.headerText.indexOf(text)}>{text}</h2>
})
}
<form onSubmit={this.submit} style={this.styles.form}>
<FormOneInputs inputs={inputs} saveInputVal={saveInputVal}
CustomTag={CustomTag} styles={styles} />
<button style={this.styles.button}>{this.props.buttonText}
</button>
</form>
<Throbber throbberText='Reserving your order...' showThrobber=
{submitting} />
<FormWarning style={display: {this.state.formWarning.display}} invalidInputID={invalidInputID} text={text}/>
</div>
)
}
}
You don't need to set any CSS class. The approach is as follows:
(1) Given a component you want to render or not render depending on a variable
(2) Make a helper method that checks for the condition and returns the actual component if you want it rendered. Otherwise, do nothing (basically returns undefined)
(3) Call that method from wherever you want the component to possibly appear.
Concrete example:
class FormOne extends React.Component {
// (...) all other things omitted to focus on the problem at hand
renderFormWarning() {
if (formIsInvalid) {
return <FormWarning ... />;
}
// else won't do anything (won't show)
}
render() {
return (
{/* ... */}
{this.renderFormWarning()}
);
}
}
In the above example, replace formIsInvalid with some statement that will tell you if the form is invalid. Then, if that condition is true, it will return the FormWarning component. Otherwise, no form warning will be shown. From the render() method, all you need do is call that helper method.

Resources