I have a bootstrap input which renders a list of value onChange of the input. And if a user selects a value from the list i want to show it in my input field. But it should have a ability to type again on the input field if necessary (to select another value). I'm using react-bootstrap and my approach is as follows
searchConsumer = (e) => {
const {consumer} = this.props;
let consumerValue = e.target.value;
const data = {
"filterText": consumerValue,
"page": 1,
"maxRecords": this.state.maxRecords
}
consumer(data);
}
selectConsumer(name){
this.setState({
selectedValue:name
})
}
renderConsumerList(){
const {consumers} = this.props;
if(consumers.consumerData.length > 0) {
return consumers.consumerData.map(item =>{
return (
<div className="consumer_search_item" onClick={()=>this.selectConsumer(item.name)}>{item.name}</div>
)
})
}
}
<Form.Control type="search" onChange={(e)=>this.searchConsumer(e)} value={consumers.consumerData.length > 0 ? selectedValue : ''} className="modal_input" placeholder="Search any consumer by name" required />
<div className="consumer_result_container">{this.renderConsumerList()}</div>
I can set the value successfully if i select a value from the list. But if i want to change it i cannot change it because the value is already set in the input field and does not let me delete or edit the value. How can i fix this issue?
I think what happens is when onChange (searchConsumer) is called, the new value that is typed is not updating state:
searchConsumer = ( e ) => {
const { consumer } = this.props;
let consumerValue = e.target.value;
const data = {
"filterText": consumerValue,
"page": 1,
"maxRecords": this.state.maxRecords
}
consumer( data );
// Update state because new value is typed
this.setState({
inputValue: consumerValue
})
}
Form.Control value should reflect the state:
<Form.Control type="search" onChange={ ( e ) => this.searchConsumer( e ) } value={ consumers.consumerData.length > 0 ? this.state.inputValue : '' } />
Related
i am using react.i have 2 inputs that by clicking a button dynamically ganerats.
this the code:
useEffect(() => {
const myFields = fieldsArray.map((field) => {
return (
<div key={field} className="col-12 m-auto d-flex">
<input id={field} name={`question${field}`} onChange={handleChangeFields} placeholder='سوال' className="form-control col-4 m-2" />
<input id={field} name={`answer${field}`} onChange={handleChangeFields} placeholder='جواب' className="form-control col-4 m-2" />
</div>
)
})
setFields(myFields)
}, [fieldsArray])
i need to save value of 2 inputs together in an opjects like this :
[{question : '',answer : ''}]
and the new 2 input's value are going to update the above array like this:
[{question : '',answer : ''}, {question : '',answer : ''}]
i can save each input's value separately like this :
const handleChangeFields = (e) => {
const { name, value } = e.target
setFieldsValue([...fieldsValue, { [name]: value }])
}
but i want each 2 input's value together
i need to save each 2 inputs together.
how do i do that?
This is a general example of how I think you can tie in what you're currently doing, and add in as little new code as possible. This logs the new data state when the button is clicked.
Have two states. One for collecting the updated form information (form), and another for the combined form data array (data).
Have a form with a single onChange listener (event delegation) so you can catch events from all the inputs as they bubble up the DOM. That listener calls the handleChange function which updates the form state.
Have a button with an onClick listener that calls handleClick which takes the current form state and adds it to the data state.
I would be a bit wary of storing JSX in state. You should have that map in the component return and only updating the actual field data with basic information.
One final issue - your inputs cannot have the same id. ids must be unique. I'd get rid of them altogether.
const { useEffect, useState } = React;
function Example() {
// Initialise two states. `data` is an array
// and `form` is an object
const [ data, setData ] = useState([]);
const [ form, setForm ] = useState({});
// Add the form data to the `data` array
function handleClick() {
setData([ ...data, form ]);
}
// We're using event delegation so we need to
// check what element we changed/clicked on
// - in this case the INPUT element. We can then
// update the form state with the name/value pair of that input
function handleChange(e) {
const { nodeName, name, value } = e.target;
if (nodeName === 'INPUT') {
setForm({ ...form, [name]: value });
}
}
// Just logs the data state after a change
useEffect(() => console.log(data), [data]);
return (
<form onChange={handleChange}>
<input name="question" type="text" />
<input name="answer" type="text" />
<button
type="button"
onClick={handleClick}
>Update form
</button>
</form>
);
};
ReactDOM.render(
<Example />,
document.getElementById('react')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
this problem solved for me :
function App() {
const [results, setResults] = useState([{ question: "", answer: "" }])
// handle input change
const handleInputChange = (e, index) => {
const { name, value } = e.target
const list = [...results]
list[index][name] = value
setResults(list)
}
// handle click event of the Remove button
const handleRemoveClick = (index) => {
const list = [...results]
list.splice(index, 1)
setResults(list)
}
// handle click event of the Add button
const handleAddClick = () => {
setResults([...results, { question: "", answer: "" }])
}
// handle submit to servers
const handlesubmit = () => {
// axios
console.log(results)
}
return (
<div className="App">
{results?.map((result, index) => {
return (
<div key={index}>
<input
name="question"
value={result.question}
onChange={(e) => handleInputChange(e, index)}
/>
<input
name="answer"
value={result.answer}
onChange={(e) => handleInputChange(e, index)}
/>
{results.length !== 1 && (
<button onClick={() => handleRemoveClick(index)}>
Remove
</button>
)}
{results.length - 1 === index && (
<button onClick={handleAddClick}>add</button>
)}
</div>
)
})}
<div style={{ marginTop: 20 }}>{JSON.stringify(results)}</div>
<button onClick={handlesubmit}>submit</button>
</div>
)
}
export default App
i am facing problem with antd select item. i am generating select item dynamically i am using default value to show checked/preDefined value but my app has mutilple language support so when i change my app language default value not changing. i am attaching screenshoots to give you full understanding enter image description here
here is my code snippest
<StyledSelectLarge
placeholder={item.settings_help_text}
defaultValue={preSelectDropDowns(item,syslanguage)}
onChange={(val) => {
const current = findSetting(item)
if(current)
{
const payload = {
id: current.id,
setting_option_id: val,
};
const updatedValues = values?.filter((item3) => item3?.id !== current?.id);
fieldValue('dropdownValues', [...updatedValues, payload])
}
touchedValue('dropdownValues', true);
}}
>
{item?.settingOptions?.map((item2) => (
<StyledSelectLarge.Option
key={item2?.id}
value={item2?.id}
item={item2}
>
{item2?.translation?.[0]?.translated_text}
</StyledSelectLarge.Option>
))}
</StyledSelectLarge>
You will have to replace defaultValue with value and set it inside onChange every time
function DropDown(props) {
const [dropDownValue, setDropDownValue] = useState(preSelectDropDowns(item, syslanguage));
return (
<StyledSelectLarge
placeholder={item.settings_help_text}
value={dropDownValue}
onChange={val => {
const current = findSetting(item);
if (current) {
const payload = {
id: current.id,
setting_option_id: val,
};
const updatedValues = values?.filter(item3 => item3?.id !== current?.id);
fieldValue('dropdownValues', [...updatedValues, payload]);
}
touchedValue('dropdownValues', true);
setDropDownValue(val) // We set the new value here
}}
>
{item?.settingOptions?.map(item2 => (
<StyledSelectLarge.Option key={item2?.id} value={item2?.id} item={item2}>
{item2?.translation?.[0]?.translated_text}
</StyledSelectLarge.Option>
))}
</StyledSelectLarge>
);
}
I have a form with key and value input fields. I want the user to be able to edit key value pairs. Value simply edits the value, but key needs to update the key to what ever he is typing. I got the value change working, but I am not sure how to edit the key as this needs to update, remove and keep track of the key changed.
So lets say this is the current state:
{"name":"Joe"}
Now the user wants to edit name to firstname in the textfield.
As he types state will look like this with each keypress:
{"f":"Joe"}
{"fi":"Joe"}
{"fir":"Joe"}
{"firs":"Joe"}
{"first":"Joe"}
{"firstn":"Joe"}
etc
Here is my code:
function KeyValuePair({ initialPair }) {
const [pairs, setPairs] = React.useState(initialPair ? initialPair : {});
const handleInputUpdate = e => {
const { name, value, id } = e.target;
if (name === "key") {
// ??
}
if (name === "value") {
setPairs({ ...pairs, [id]: value });
}
};
}
<Grid item lg={3}>
<TextField
name="key"
label="Key"
id={key}
value={key}
margin="normal"
onChange={handleInputUpdate}
fullWidth
/>
</Grid>
<Grid item lg={3}>
<TextField
name="value"
label="Value"
margin="normal"
id={key}
value={pairs[key]}
onChange={handleInputUpdate}
fullWidth
/>
</Grid>
UPDATE
I have got it working like this:
const handleInputUpdate = e => {
const { name, value, id } = e.target;
if (name === "key") {
const keyValue = pairs[id];
let oldState = pairs;
delete oldState[id];
setPairs({ ...oldState, [value]: keyValue });
}
if (name === "value") {
setPairs({ ...pairs, [id]: value });
}
};
But this loses focus when typing because the old element is removed and new key rendered. Need to keep focus so you can keep typing.
As your key is editable, you'll need to rely on another property that you can rely on whilst your key value pairs change - like a separate id, or their index position in an array.
Going down the array route, your entries would look something like this:
const initialEntries = [
{ key: "myKey", value: "myValue" },
{ key: "myOtherKey", value: "myOtherValue" },
{ key: "otherOtherKey", value: "otherOtherValue" }
];
If you map over your entries you can use the index:
{entries.map((entry, index) => (
<div>
<span>{index}</span>
<input
name="key"
onChange={ev => updateKey(index, ev)}
value={entry.key}
/>
<input
nanme="value"
onChange={ev => updateValue(index, ev)}
value={entry.value}
/>
</div>
))}
And then handle the updates:
const updateKey = (id, ev) => {
const newKeyValue = ev.target.value;
const updatedEntries = entries.map((entry, index) =>
index === id ? { ...entry, key: newKeyValue } : entry
);
setEntries(updatedEntries);
};
const updateValue = (id, ev) => {
const newValueValue = ev.target.value;
const updatedEntries = entries.map((entry, index) =>
index === id ? { ...entry, value: newValueValue } : entry
);
setEntries(updatedEntries);
};
Here's a working codesandbox that shows roughly how you can do it: https://codesandbox.io/s/sleepy-cherry-n0518
I wrote a Checkbox component, with React, that is reused to generate a list of checkboxes. I know that a react element is different from a DOM in terms of state. But how do I check if at least 1 checkbox has been selected by the user on form submit in React?
I have searched in SO and Google but all examples are either in jQuery or vanilla JS. For my case I want a React example.
component
const Checkbox = ({ title, options, id, name, onChange }) => {
return (
<div className="checkbox-group">
<h4>{title}</h4>
{options.map(option => (
<label htmlFor={`${name}-${index}`} key={`${name}-${index}`}>
<input
type="checkbox"
name={name}
value={option}
onChange={onChange}
id={`${name}-${index}`}
/>
<span> {option}</span>
</label>
))}
</div>
);
};
class Form extends Component {
...
handleChange(event) {
let newSelection = event.target.value;
let newSelectionArray;
newSelectionArray = [
...this.state.sureveyResponses.newAnswers,
newSelection,
];
this.setState(prevState => {
return {
surveyResponse: {
...this.state.surveyResponse,
newAnswers: newSelectionArray,
}
)
}
handleSubmit() {
// I'm guessing this doesn't work in React as it's using its own state!
let checkboxes = document.querySelectorAll('[name="quiz-0"]:checked');
for (let chk in checkboxes) {
if (chk.checked) {
return true;
}
}
alert('Please choose at least one answer.');
return false;
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<h4>Choose</>
{this.state.surveyQuiz.map((quiz, index) => {
return (
<div key={index}>
<Checkbox
title={quiz.question}
options={quiz.answers}
name={`quiz-${index + 1}`}
onChange={this.handleChange}
/>
</div>
);
})};
<button>Save answer(s)</span>
</form>
);
}
}
When the user submits the form, it should check if at least a checkbox is checked, if not none is checked then prevent the form to submit, i.e. return false!
You should also maintain the checked property in surveyResponse.newAnswers state. Then, you'll be able to check if any of them is true. For eg.:
const nA = this.state.surveyResponse.newAnswers
const isChecked = nA.some(c => c.checked == true)
if(isChecked) {
//...if any of them has checked state
}
Assuming example newAnswers data:
[
{answer:'foo', checked: false},
{answer:'bar', checked: true},
...
]
The following shows how to change your handleSubmit to read form data:
handleSubmit(event) {
event.preventDefault();
const form = event.target;
const data = new FormData(form);
for (let name of data.keys()) {
const input = form.elements[name];
console.log(input);
console.log('value:', input.value);
}
}
I have an input that's populated with values returned from an API that's stored into Redux state:
state = {
popoverScenarioName: null,
}
scenarioNameChange = (e) => (
this.setState({popoverScenarioName: e.target.value})
)
// ....
<StyledInput
placeholder="Scenario Name"
onChange={(e) => scenarioNameChange(e)}
onFocus={(e) => e.target.select()}
value={this.state.scenarioName || database.inputs.scenarioName}
/>
When I click into the input and hit backspace to clear the entire field, it always repopulates the value with database.inputs.scenarioName.
I've tried setting state to something like
state = {
popoverScenarioName: null || this.props.database.inputs.scenarioName,
}
but that didn't seem to work neither. My other guess would be to write a dispatch to change database.inputs.scenarioName directly?
state = {
popoverScenarioName: null,
}
scenarioNameChange = (e) => (
this.setState({popoverScenarioName: e.target.value})
)
clickHandler = () => {
//...doing something here
setState({popoverScenarioName: null})
}
<StyledInput
placeholder="Scenario Name"
onChange={(e) => scenarioNameChange(e)}
onFocus={(e) => e.target.select()}
value={this.state.popoverScenarioName}
/>
<button onClick={this.clickHandler}>add todo</button>
where is your click handler?
after click clear your form
this one going from redux state "this.props.database."?
I achieved it with this prop:
scenarioName={this.state.popoverScenarioName === null ? database.inputs.scenarioName : this.state.popoverScenarioName}