How to check each state value automatically and setState - reactjs

How to check if each state has value then combine all values?
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
inputvalue : '',
allval: ''
}
}
onChangeOfInput =(name,value) =>{
this.setState({
[name]: value
});
}
getValues = () =>{
console.log(this.state);
if(this.state.Title1) {
this.setState({
allval: this.state.allval+this.state.Title1
});
}
}
render() {
return (
<div className="hello">
<Input onChangeOfInput={this.onChangeOfInput}
placeholder="Title 1" name="Title1" />
<br/>
<Input placeholder="Title 2" name="Title2" onChangeOfInput={this.onChangeOfInput} />
<br/>
<Input placeholder="Title 3" name="Title3" onChangeOfInput={this.onChangeOfInput}/>
<br/>
<Input placeholder="Title 4" name="Title4" onChangeOfInput={this.onChangeOfInput}/>
<br/>
<button onClick={this.getValues}>Get value</button>
</div>
)
}
}
class Input extends React.Component {
constructor(props) {
super(props)
this.state = {
inputvalue: ''
}
}
handleChange(e) {
this.setState({
inputvalue: e.target.value
});
this.props.onChangeOfInput(this.props.name,e.target.value)
}
render() {
return (
<input
type="text"
placeholder={this.props.placeholder}
value={this.state.inputvalue}
onChange={this.handleChange.bind(this)}
/>
)
}
}
ReactDOM.render(<App />, document.querySelector("#app"))
<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="app"></div>
jsfiddle: https://jsfiddle.net/vxm2ojLz/
The issue is here, I need to check each value state.Title1, state.Title2, state.Title3, state.Title4 if they are not empty, then I want to combine all values if it is not empty and assign the combined values to allVal, how to combine all values to allval? Thanks

You need to be doing something like this.
getValues = () => {
console.log(this.state);
let combinedString = "";
Object.keys(this.state)
.map( igKey => {
if(this.state[igKey] != "" && igKey.includes('Title')){
combinedString = combinedString +''+ this.state[igKey];
return combinedString
}
});
this.setState({allval:combinedString})
console.log(combinedString);
}
working fiddle https://jsfiddle.net/2nhc6drm/
hope this helps!

Try handling getValues like this:
getValues = () =>{
console.log(this.state);
let result = [];
Object.keys(this.state).forEach(key => {
if (key.includes('Title') && this.state[key]) result.push(`${key}: ${this.state[key]}`);
})
this.setState({
allval: result.join('; ')
})
}

Please Update getValues method :-
For concatination,it will ignore the keys allval and inputval.
getValues = () => {
let allval = ''
for(let key of Object.keys(this.state)){
if(key==='allval' || key==='inputval'){
continue;
}
else{
let value=this.state[key];
console.log(value);
if(value===''){
}
else{
allval=allval+value;
}
console.log(allval);
}
}
this.setState({allval:allval})
}
Working SandBox :- https://codesandbox.io/s/vqoxo9w1wy
Hope this helps,
Cheers !!

I'd recommend to use reduce for combinde the values, and use the functional setState to avoid double state change:
class App extends React.Component {
state = {
allVal: '',
title1: '',
title2: ''
}
getValues = (prevState, name, newVal) => {
return Object.keys(prevState)
.reduce((acc, key) => {
if (key === 'allVal') return acc;
if (key === name) return acc + newVal;
return acc + prevState[key];
}, '')
}
handleChange = ({ target: { name, value } }) => {
this.setState(prevState => ({
[name]: value,
allVal: this.getValues(prevState, name, value)
}))
}
render(){
const { title1, title2, allVal } = this.state;
return (
<div>
<input name="title1" onChange={this.handleChange} value={title1} /><br />
<input name="title2" onChange={this.handleChange} value={title2} /><br />
allVal: <span>{allVal}</span>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<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="root"></div>

Related

How do I use startsWith with a form submission when filtering array data from an API in React?

Sorry for the noob question, just starting out in React. So my page accesses a list of counties formatted as an Array from an API using this component:
class FetchRandomCounty extends React.Component {
state = {
loading: true,
county: null,
};
async componentDidMount() {
const url = "http://localhost:5000/api/counties";
const response = await fetch(url);
const data = await response.json();
this.setState({ county: data, loading: false });
}
render() {
return (
<div>
{this.state.loading || !this.state.county ? (
<div> loading... </div>
) : (
<div>
<div>
{" "}
{this.state.county
.filter((item) => item.startsWith("J"))
.map((item) => (
<li key={item}>{item}</li>
))}{" "}
</div>
</div>
)}
</div>
);
}
}
The fetched data is an Array that looks like this:
["Aransas", "Austin", "Bastrop", "Bee", "Brazoria", "Burleson", "Caldwell", "Calhoun", "Chambers", "Colorado", "Comal", "De Witt", "Fayette", "Fort Bend", "Galveston", "Goliad", "Gonzales", "Grimes", "Guadalupe", "Hardin", "Harris", "Jackson", "Jasper", "Jefferson", "Jim Wells"]
And the output currently is this:
Jackson
Jasper
Jefferson
Jim Wells
How do I prompt the user to enter a letter and filter the array to only display data that starts with that letter? Right now I am using item.startsWith('J')) and would like to make it respond to user input. This is my User input component:
class LetterForm extends React.Component {
constructor(props) {
super(props);
this.state = { value: "" };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({ value: event.target.value });
}
handleSubmit(event) {
alert("A letter was submitted: " + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
StartsWith:
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
If you want to keep FetchRandomCounty and LetterForm as separate components, you could control the selected letter with a prop to FetchRandomCounty and control it from the parent, and introduce a onChange callback prop for LetterForm that you use to change the stored selected letter in the parent.
Example
const data = [
"Aransas",
"Austin",
"Bastrop",
"Bee",
"Brazoria",
"Burleson",
"Caldwell",
"Calhoun",
"Chambers",
"Colorado",
"Comal",
"De Witt",
"Fayette",
"Fort Bend",
"Galveston",
"Goliad",
"Gonzales",
"Grimes",
"Guadalupe",
"Hardin",
"Harris",
"Jackson",
"Jasper",
"Jefferson",
"Jim Wells"
];
class FetchRandomCounty extends React.Component {
state = {
loading: true,
county: null
};
componentDidMount() {
// const url = "http://localhost:5000/api/counties";
// const response = await fetch(url);
// const data = await response.json();
this.setState({ county: data, loading: false });
}
render() {
const { loading, county } = this.state;
const selectedLetter = this.props.selectedLetter.toLowerCase();
return (
<div>
{loading || !county ? (
<div> loading... </div>
) : (
<div>
<div>
{county
.filter((item) => item.toLowerCase().startsWith(selectedLetter))
.map((item) => (
<li key={item}>{item}</li>
))}
</div>
</div>
)}
</div>
);
}
}
class LetterForm extends React.Component {
state = { value: "" };
handleChange = (event) => {
this.setState({ value: event.target.value.slice(0, 1) });
};
handleSubmit = (event) => {
event.preventDefault();
this.props.onChange(this.state.value);
};
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
StartsWith:
<input
type="text"
value={this.state.value}
onChange={this.handleChange}
/>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
class App extends React.Component {
state = { selectedLetter: "J" };
updateLetter = (selectedLetter) => {
this.setState({ selectedLetter });
};
render() {
return (
<div>
<FetchRandomCounty selectedLetter={this.state.selectedLetter} />
<LetterForm onChange={this.updateLetter} />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Do a conditional display button form

hello I am novice on react, I have this form and I would like that my add button appears only when there is an input on the input.
I tried this in my render. thanks
class App extends Component {
state= {
value:''
}
handleChange=(e)=>{
e.preventDefault()
this.setState({value: e.currentTarget.value})
}
handleAdd=(e)=>{
e.preventDefault()
let value= [ ...this.state.value]
value.push(this.state.value)
}
render () {
let button;
if(this.handleChange){
button=<button>Add</button>
}
return (
<div className="formulaire">
<form onSubmit={this.handleAdd}>
<label>
<p>Name:</p>
<input value={this.state.value} onChange={this.handleChange}/>
</label>
{button}
</form>
</div>
)
}
}
You can use this.
render () {
return (
<div className="formulaire">
<form onSubmit={this.handleAdd}>
<label>
<p>Name:</p>
<input value={this.state.value} onChange={this.handleChange}/>
</label>
{
this.state.value !== ''&&
( <button>Add</button> )
}
</form>
</div>
)
}
}
I think it could be worked on your case.
Here are two ways you can handle this.. (make sure to expand the snippets and run them to see the code and how it works).
This is the more straight forward way:
const { Component } = React;
class App extends Component {
state = {
value: "",
added: []
};
handleChange = e => {
e.preventDefault();
this.setState({ value: e.currentTarget.value });
};
handleAdd = e => {
e.preventDefault();
this.setState({
added: [...this.state.added, this.state.value],
value: ""
});
};
render() {
let button;
let items;
if(this.state.value) {
button = <button>Add</button>
}
if(this.state.added && this.state.added.length > 0) {
items = (
<div>
<h3>Added Items:</h3>
<ul>{this.state.added.map(i => <li>{i}</li>)}</ul>
</div>
)
}
return (
<div className="formulaire">
<form onSubmit={this.handleAdd}>
<label>
<p>Name:</p>
<input value={this.state.value} onChange={this.handleChange} />
</label>
{button}
</form>
{items}
</div>
);
}
}
ReactDOM.render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.9.0/umd/react-dom.production.min.js"></script>
This is the exact same as above, only using different (more efficient) syntax:
const { Component } = React;
class App extends Component {
state = {
value: "",
added: []
};
handleChange = e => {
e.preventDefault();
this.setState({ value: e.currentTarget.value });
};
handleAdd = e => {
const { value, added } = this.state;
e.preventDefault();
this.setState({
added: [...added, value],
value: ""
});
};
render() {
const { value, added } = this.state;
let button = value && <button>Add</button>;
let items = added && added.length > 0 && (
<div>
<h3>Added Items:</h3>
<ul>{added.map(i => <li>{i}</li>)}</ul>
</div>
);
return (
<div className="formulaire">
<form onSubmit={this.handleAdd}>
<label>
<p>Name:</p>
<input value={value} onChange={this.handleChange} />
</label>
{button}
</form>
{items}
</div>
);
}
}
ReactDOM.render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.9.0/umd/react-dom.production.min.js"></script>

react will not allow me to push two elements

I'm trying to pass 2 elements within the todo array, however it just returns the term element value. I see no error in the console.
It seems that the
items: [...this.state.items, this.state.term, this.state.name ]
only accepts two parameters.
I'm currently following this
https://reactjs.org/docs/handling-events.html
const { Component } = React;
class App extends Component {
constructor(props) {
super(props);
this.state = {
term: '',
name: '',
items: []
};
}
onChange = (event) => {
this.setState({name: event.target.value, term: event.target.value});
}
onSubmit = (event) => {
event.preventDefault();
this.setState({
term: '',
name: '',
items: [
...this.state.items,
this.state.term,
this.state.name
]
});
}
render() {
return (
<div>
<form className="App" onSubmit={this.onSubmit}>
<input value={this.state.term} onChange={this.onChange}/>
<input value={this.state.name} onChange={this.onChange}/>
<button>Submit</button>
</form>
<pre>{JSON.stringify(this.state.items)}</pre>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<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="root"></div>
I suspect the issue you're asking about is the fact that your onChange event updates both term and name and whatever you type into one input goes into the other. Here's how you can resolve that:
Add a name attribute to your input that corresponds to the key in the state.
Access the value of name in onChange and update the value accordingly.
Solution
const { Component } = React;
class App extends Component {
constructor(props) {
super(props);
this.state = {
term: '',
name: '',
items: []
};
}
onChange = (event) => {
const { name, value } = event.target;
this.setState({ [name]: value });
}
onSubmit = (event) => {
event.preventDefault();
this.setState({
term: '',
name: '',
items: [
...this.state.items,
this.state.term,
this.state.name
]
});
}
render() {
return (
<div>
<form className="App" onSubmit={this.onSubmit}>
<input name="term" value={this.state.term} onChange={this.onChange}/>
<input name="name" value={this.state.name} onChange={this.onChange}/>
<button>Submit</button>
</form>
<pre>{JSON.stringify(this.state.items)}</pre>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<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="root"></div>
I dont think your this.setState has any issue for your example.
You seem to be using this.onChange wrong.
I changed its implementation so that both input can have their own respective handler.
onChange = key => event => {
this.setState({ [key]: event.target.value }); };
Also changed input callbacks to pass key value from the render function below.
render() {
const { term, name, items } = this.state;
return (
<div>
<form className="App" onSubmit={this.onSubmit}>
<input value={term} onChange={this.onChange('term')} />
<input value={name} onChange={this.onChange('name')} />
<button>Submit</button>
</form>
{items.join(", ")}
</div>
);
}
}
Your prior code was problematic which was rendering same value for name and term with whatever you type in any of your input boxes.

React Checkbox Group - Set initial state from API

I have a CheckboxGroup component which takes in an options array prop and generates CheckboxInput components. On page load I make a call to an API which returns an array of pre-selected checkboxes (delivered to the value prop). Depending on the logged in user, this call can return an empty array or a selection of previously selected checkbox options.
The following code successfully takes the response of the API call and sets the relevant checkboxes to 'checked'. The issue I have is that this code doesn't allow me to make changes to the checkboxes after page load (clicking a checkboxes has no effect).
I think there is also some disconnect between the initial selectedCheckboxes state and the value of the API call but I read that setting props as initial state is an anti-pattern (e.g. selectedCheckboxes: props.value,).
export default class CheckboxGroup extends Component {
constructor(props) {
super(props);
this.state = {
selectedCheckboxes: [],
};
}
addCheckboxToSelected = (id) => {
if (this.state.selectedCheckboxes.includes(id)) {
// Remove checkbox from array and update state
const filteredArray = this.state.selectedCheckboxes.filter(item => item !== id);
this.setState({ selectedCheckboxes: filteredArray });
} else {
// Add checkbox to array and update state
this.setState({ selectedCheckboxes: this.state.selectedCheckboxes.concat(id) });
}
}
checkIfSelected = (checkboxValue) => {
const preSelectedCheckboxes = this.props.value;
let selectedBool = false;
preSelectedCheckboxes.some(function(object) {
if (object.id === checkboxValue) {
selectedBool = true;
}
return false;
});
return selectedBool;
}
render() {
const { label, name, options } = this.props;
return (
<div className="form-group form-inline">
<span className="checkboxgroup-heading">{label}</span>
<div className="form-group-container">
{options.map(object => (
<CheckboxInput
key={object.value}
name={name}
label={object.label}
onChange={this.addCheckboxToSelected}
value={object.value}
checked={this.checkIfSelected(object.value)}
/>
))}
</div>
</div>
);
}
}
This is the stateless CheckboxInput component
const CheckboxInput = ({ name, label, onChange, value, checked }) => {
return (
<div className="field form-group filter-input">
<input
type="checkbox"
id={value}
name={name}
value={value}
onChange={() => onChange(value)}
checked={checked}
/>
<label htmlFor={value} className="form-control">{label}</label>
</div>
);
};
Check the following code snippet. This might help. Let me know if you have questions.
const CheckboxField = ({checked, onChange}) => {
return (
<input type="checkbox" checked={checked} onChange={ev => onChange(ev.target.checked)} />
);
};
class App extends React.Component {
constructor() {
super();
this.state = {
options: [{id: "1", checked: true}, {id: "2", checked: false}]
};
}
handleCheckboxChange(checked, option) {
const {options} = this.state;
var cOptions = [...options];
for(var i in cOptions) {
if(cOptions[i].id == option.id) {
cOptions[i].checked = checked;
}
}
this.setState({
options: cOptions
}, () => console.log(options));
}
render() {
const {options} = this.state;
return (
<div>
{
options.map(option => {
return (
<CheckboxField key={option.id} checked={option.checked} onChange={value => this.handleCheckboxChange(value, option)} />
)
})
}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<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>

Input does not show entered value in react

class IndividualPsid extends Component {
constructor(props) {
super(props);
this.state = {
editData: false,
newSkuid: this.props.SkuId
}
this.updateState = this.updateState.bind(this);
}
updateState(e) {
const psid = e.target.value;
this.setState({ newSkuid: psid }, () => {
this.props.onPsidChange(this.props.id, this.state.newSkuid);
});
}
render() {
let member = '';
if (this.props.editingProp) {
member = (
<div>
<input value={this.state.newSkuid} key={this.props.SkuId + uuidv4()} onChange={this.updateState}
className="skuid col-xs-7" />
</div>
)
}
else {
member = (
<div key={this.props.SkuId + uuidv4()} className="skuid col-xs-7" >{this.props.SkuId}</div>
)
}
return (
<div className="row" >
<div className="skuname col-xs-5">{this.props.SkuName}</div>
{member}
</div>);
}
}
export default IndividualPsid;
Above is my child component code(Psid.js). When I click on Edit button, the input box shows , then I type something in the input box it does not show the typed number but when I click on save it shows the updated part. So basically according to my knowledge this.state.newSkuid does not update in the value of input. And below is my parent file (Category.js) that renders the IndividualPsid.
edit(skuList) {
if (this.state.editing == false) {
this.setState({
text: 'SAVE',
editing: true
});
}
else {
this.setState({
text: 'EDIT',
editing: false
});
this.props.edit_menu_items_api(this.state.changedSkus);
}
this.render();
}
render() {
return (
<button className="edit" onClick={() =>
this.edit(this.props.categoryData.productList[0].brandProductSkuList)}>
{this.state.text}</button>
)
}
It works if I put the logic of diplaying the input box on edit button click in componentWillReceiveProps function.
Here is my code of my child component.
componentWillMount() {
this.member = <div key={this.props.skuId} className="skuid col-xs-7" >{this.props.skuId}</div>
}
componentWillReceiveProps(nextProps) {
if (this.props.editingProp !== nextProps.editingProp && nextProps.editingProp) {
this.member = <div className="skuid col-xs-7">
<input defaultValue={this.state.newSkuid} key={this.props.skuId} onChange={this.updateState}
onBlur={() => { this.props.onPsidChange(this.props.id, this.state.newSkuid) }} />
</div>
} else if (this.props.editingProp !== nextProps.editingProp && !nextProps.editingProp) {
this.member = <div key={this.props.skuId} className="skuid col-xs-7" >{this.props.skuId}</div>
}
this.setState({ editData: nextProps.editingProp });
}
render() {
return (
<div className="row" >
<div className="skuname col-xs-5">{this.props.skuName}</div>
{this.member}
</div>);
}

Resources