react hooks: input onChange first character can not be deleted - javascript

I got some inputs to create/update a formular,
1) if value={question}, everything works except that I can not delete the last character (= first character of the input)
2) if I dont mention value, it's all good except when I want to change questions orders with Chevron icon button, database is well changed but input value is still displayed at the last place.
<input
type="text"
value={question}
placeholder="blabla"
onChange={event => {
event.preventDefault();
const value = event.target.value;
setUpdatedQuestion(value);
}}
/>
I tried to add if (event.target.value == "" || event.target.value) to onChange but does not work either
OK I found something, but it is a McGyver tip, not very clean : adding one space before all new add question ahah. But then I can't see my placeholder anymore :/
FYI : question is coming from a questions.map, setUpdatedQuestion do update questions, newOrder also do update questions
//in QuestionsContent.js (questions is a questions tab)
const QuestionsContent = props => {
return (
<form onSubmit={onSubmit}>
{isLoading === false && questions.length > 0
? questions.map((question, i) => {
return (
<div key={i}>
<QuestionLine
{...question}
questions={questions}
setQuestions={setQuestions}
setNewOrder={setNewOrder}
/>
</div>
);
})
: null}
<button
onClick={event => {
event.preventDefault();
setAddQuestion({
question: null,
type: "texte"
});
}}
>
Add a question
</button>
</form>
);
};
//in QuestionLine.js:
const QuestionLine = ({
question,
setNewOrder,
setQuestions,
questions
}) => {
const [updatedQuestion, setUpdatedQuestion] = useState("");
// * UPDATE QUESTION *******************************
useEffect(() => {
if (updatedQuestion !== "") {
const tab = [];
for (let j = 0; j < questions.length; j++) {
if (j === i) {
const newObject = {
question: updatedQuestion,
type: type
};
console.log("adding updatedQuestion ===>", newObject);
tab.push(newObject);
} else {
tab.push(questions[j]);
}
}
setQuestions(tab);
setUpdatedQuestion("");
}
}, [updatedQuestion]);
return (
<div >
{/* QUESTION */}
<input
type="text"
value={question}
placeholder="blabla"
onChange={event => {
event.preventDefault();
const value = event.target.value;
setUpdatedQuestion(value);
}}
/>
</div>
);
};
thanks for your precious help

The problem probably comes from the condition in the useEffect : when you want to delete the last character of the string, the state of updatedQuestion is empty and therefore the condition is not executed

Related

React : Display an error message when clicking on the "submit" button if the user enters text

I create a project in React. I would like that when the user clicks on the "submit" button it shows him an error message if in the input "FormDebut" he enters "text" type characters.
But I can't do this :'( , could you help me please? Thanks in advance !
FormulaireReservations.js :
export default function FormulaireReservation() {
return (
<div className="Formulaire">
<Logo/>
<h2><center>Formulaire de réservation</center></h2>
<FormDebut/>
<center><input type="submit" value="Submit" /></center>
</div>
);
}
Component FormDebut.js :
const [value, setValue] = React.useState("");
const onChange = (e) => {
const formattedValue = formatInput(e.target.value);
if (formattedValue.length < 6) {
setValue(formattedValue);
}
}
const formatInput = (input) => {
if (input.length > 2 && input[2] !== ":") {
return input.substring(0, 2) + ":" + input.slice(2);
}
return input;
}
return (
<div className="Form">
<div>Début : </div>
<input placeholder="00:00" type="text" onChange={onChange} value={value}/>
</div>
)
}
export default FormDebut; ```
Test this out in a new document, and then from there you can implement it into your work. You can have a parameter were user has to input a number between 1-10 if they do not then you can display an error message. If number is between 1-10 then you can redirect them. isNAN (is not a number) is a condition and is used for validating the input of the user. The function will check if the input is not a number, it will check if the input is less than 1 or more than 10, if all these conditions are true then it will display an error.
<p>Please input a number between 1 and 10:</p>
<input id="numb">
<button type="button" onclick="myFunction()">Submit</button>
<p id="demo"></p>
<script>
function myFunction() {
// Get the value of the input field with id="numb"
let x = document.getElementById("numb").value;
// If x is Not a Number or less than one or greater than 10
let text;
if (isNaN(x) || x < 1 || x > 10) {
text = "Input not valid";
} else {
text = "Input OK";
}
document.getElementById("demo").innerHTML = text;
}
</script>
I'm not sure if I understood your requirements, but seems that when you click submit button you want to check if the input is numeric value. If that's the case, this should work:
https://codesandbox.io/s/infallible-browser-74r5d7?file=/src/App.js
I moved the value state into the Form itself and passing it as a prop to its child. I also added a new state variable called error that is passed to the FormDebut. On click of the submit button, I strip the : from the value and check if I can parse it to a number. If not, I set the error, otherwise I clear out the error.
Hope that helps
Here is something you can modify your components:
in your FormulaireReservations.js:
export default function FormulaireReservation() {
const [value, setValue] = React.useState("");
const [errorMsg, setErrorMsg] = React.useState("");
const handleSubmission = () => {
if (isNaN(value)) {
setErrorMsg('Input should not be a text')
}
}
React.useEffect(() => {
setErrorMsg('')
}, [value])
return (
<div className="Formulaire">
<Logo />
<h2><center>Formulaire de réservation</center></h2>
<FormDebut value={value} setValue={setValue} msg={errorMsg} />
<center><input type="button" onClick={handleSubmission} value="Submit" /></center>
</div>
);
}
and in your FormDebut.js :
function FormDebut({ value, setValue, msg }) {
const onChange = (e) => {
const formattedValue = formatInput(e.target.value);
if (formattedValue.length < 6) {
setValue(formattedValue);
}
}
const formatInput = (input) => {
if (input.length > 2 && input[2] !== ":") {
return input.substring(0, 2) + ":" + input.slice(2);
}
return input;
}
return (
<div className="Form">
<div>Début : </div>
<input placeholder="00:00" type="text" onChange={onChange} value= {value} />
{ msg && <p>{msg}</p> }
</div>
)}export default FormDebut;
So, basically, the value and errorMsg state is passed to another component that contains the input field. Each time changes on the input field will also update the state declared in the parent component. When a user clicks on submit button, it calls a function that is checking if the value is a number or not. Based on that the errorMsg state is updated. And in the FormDebut component, error msg will be shown just after the input field if any msg was set.

Increment and decrement counter according to select or unselect tag

I am working with the next js to handle a question form, I have multiple questions that have multiple answers, and the user may select one or multiple tags for a question.
if a user selects one or more tag to answer a question it must increment the counter just one time, If a user unselect a tag it must not decrement until unselect the last tag of the related question.
1 - When the user selects a tag of a question at first increment the counter, but it must not increment when I select the second tag in the same question.
2 - When the user unselects tags of question it must not decrement if any one of them is selected it must not decrement until the last one is unselected.
Now when selecting the first tag it increments the counter but after that, if I select or unselect, it does not decrement or increment.
Please help me to handle this.
My code:
<TageQuestion
key={item.attributes.Number}
id={item.attributes.Number}
data={item}
name={`${item.attributes.Number}`}
errors={errors}
touched={touched}
handleSelectOption={handleSelectOption}
/>
The component:
<div className="mt-3">
{Object.keys(attributes.options).map(item => (
<div className="inline" key={item}>
<input
{...feild}
{...props}
className="hidden"
id={item}
value={item}
type="checkbox"
name={`${name}[answer]`}
/>
<label
htmlFor={item}
id={item + "lable"}
onClick={e => {
selectTage(e.target);
handleSelectOption(`question${id}`, item);
}}
className="inline-flex items-center px-3 py-0.5 rounded-full text-sm font-medium bg-gray-100 text-gray-800 mr-3 mb-2 cursor-pointer"
>
{item}
</label>
</div>
))}
</div>
The function:
function handleSelectOption(title, item) {
setCompareTagTitle(title);
if (title !== compareTagTitle) {
setTagItem([...tagItem, item]);
setCounter(++counter);
} else if (title === compareTagTitle) {
setCounter(counter);
} else {
setCounter(--counter);
}
}
Data that come from the backend:
we should consider 2 steps first in child component TagQuestions then in parent component [type].js
step1:
//TagQuestions.js
const [intialRender, setIntialRender] = useState(true);
// if component render for first time
const selectTag = (e) => {
// we should change intialRender to false to say that user select somthing
....
if (tage.length > 0) {
const isExit = tage.filter((item) => item.name === el.innerHTML);
if (isExit.length === 0) {
setTage([...tage, { name: el.innerHTML }]);
if (intialRender) {
setIntialRender(false);
}
}
} else {
setTage([...tage, { name: el.innerHTML }]);
if (intialRender) {
setIntialRender(false);
}
}
....
}
//use effect function to if user unselect all tags after selctions
useEffect(() => {
if (tage.length === 0 && !intialRender) {
//call parent function and set empty param to true.
handleSelectOption(`question${id}`, "", true);
}
}, [tage]);
step2(parent component):
const [compareTagTitle, setCompareTagTitle] = useState([]);
function handleSelectOption(title, item, empty = false) {
if (empty) {
const filter = compareTagTitle.filter(
(value) => value.toString() !== title.toString()
)
setCompareTagTitle(filter);
setCounter(--counter);
} else {
if (!compareTagTitle.includes(title)) {
setCompareTagTitle([...compareTagTitle, title]);
setCounter(++counter);
}
}
}

array removes the last element instead of the removed one react useState

I am trying to get this
https://www.youtube.com/watch?v=QyoSW2cP-RE
My code is here
https://codesandbox.io/s/ecstatic-shannon-gz3c2?fontsize=14&hidenavigation=1&theme=dark
I am trying to delete element when the input field is empty, and it is working in the array (the 2 buttons in the bottom are for logging the array and it's working fine) but in the ordered list something is going wrong, it is deleting the last element and letting the removed one empty not deleting it, what's the problem I don't understand ://
<ol>
{prosArr.map((e, key) => {
if (e !== '') {
console.log(e);
return (
<li key={key}>
<input
type="text"
onBlur={(el) => {
if (el.target.value === '') {
prosArr[prosArr.indexOf(e)] = '';
setProsArr(prosArr.filter((e) => e !== ''));
}
}}
defaultValue={e}
/>
</li>
);
} else {
return '';
}
})}
<li>
<input
onBlur={(e) => {
e.target.value !== '' && setProsArr([...prosArr, e.target.value]);
e.target.value = '';
}}
type="text"
/>
</li>
</ol>;
EDIT: I edited and copied the functionality code here, but there is the full working code on the sandbox link

How to make a function that can be edited and saved

In this situation when I add a todo item to the list and try to edit the added todo, it will only add one new letter and typing the next letter it will only replace the latest letter all the time. For instance, the word is hello, no matter how many letters you type there will be only one letter added (for instance hellop, hellow, hellol, hellop). Check the console.log to understand the problem.
link to snippet
const addTodo: AddTodo = newTodo => {
newTodo.trim() !== "" && setTodos([...todos, { text: newTodo, complete: false, edit: false }]);
}
const getEditText: GetEditText = getEditedTodo => {
console.log('getEditText ' + getEditedTodo);
}
const saveEditedTodo: SaveEditedTodo = currentTodo => {
console.log('saveEditedTodo ' + JSON.stringify(currentTodo.text));
}
return (
<div>
<input type="input" onChange={(e) => getEditText(e.target.value)} value={todo.text} />
<span onClick={() => saveEditedTodo(todo)}>Save</span></div> :
<li>
)
You are not assigning the new value to variable todo.next
You need to update the variable to which value is binded for input field. So please try to create a fiddler for these kind of questions

Redux-Form old/incorrect field values when triggering field swap inside FieldArray

I am using Redux-Form 6.8.0 for my project. I am creating traveling application that calculates distance and duration of trip. Trip can have one or many stops. Stops can be picked from Google Place AutoComplete
and after picking desired place from google places application calculates distance with Google Direction Service .
I managed to perform all calculations when user selects place from dropdown but when user swap stops/fields (used fields.swap(indexA:Integer, indexB:Integer)) incorrect results are displayed. Inside calculation method I "debugged" values and when user perform fields swapping, values (indexes) doesn't change at all but field array changes are rendered correctly.
Also, I am adding new elements in redux-form store because I need some values submitted even if they doesn't have fields at all. Not sure if that's good approach so I am open for suggestions.
Does someone knows where is the problem?
Here is my code:
Price calculation:
updateStopDistanceAndTime = () => {
let self = this;
if (this.props.fields.length >= 2) {
let waypoints = [];
this.props.fields.getAll().map(function (value, index) {
console.log(value);
if (value.place) {
waypoints.push({
place: value.place,
placeId: value.placeId
});
}
});
calculateTimeAndValueBetweenStops(waypoints).then(function (result) {
if (Object.keys(result).length > 0) {
let currentValues;
result.map(function (value, index) {
currentValues = self.props.fields.get(index);
if (value.end_address !== value.start_address) {
currentValues.distance = value.distance;
currentValues.duration = value.duration;
// self.props.changeFieldValue('busRentForm', index + ".distance", value.distance);
// self.props.changeFieldValue('busRentForm', index + ".duration", value.duration);
}
else {
// self.props.changeFieldValue('busRentForm', index + ".distance", null);
// self.props.changeFieldValue('busRentForm', index + ".duration", null);
currentValues.distance = null;
currentValues.duration = null;
}
self.props.fields.remove(index);
self.props.fields.insert(index, currentValues);
});
}
});
}
}
Field Array:
render() {
const {
stop,
index,
dayIndex,
fields,
isDragging,
connectDragSource,
connectDropTarget,
} = this.props
return connectDragSource(connectDropTarget(
<div key={index} className="">
<div>
<div className="col-md-6 col-xs-7">
<Field name={`${stop}.place`}
type="text"
component={renderStopInput}
icon={String.fromCharCode(97 + index)} // (97) is A in ascii table
label={(index === 0) ? "Starting location" : (index === fields.length - 1) ? "Destination location" : "Stop location"}
placeholder=""
index={index}
/>
<span className={index !== fields.length - 1 ? "vertical-dash" : null}></span>
{(index !== 0 && index !== fields.length - 1) ?
<button className="btn btn-fab btn-rearrange" type="button" onClick={() => {
fields.swap(index, index + 1);
}}><i className="fa fa-exchange"></i></button>
:
null}
</div>
<div className="col-md-1 stack">
{(index !== fields.length - 1 && index !== 0) ?
<button className="btn-remove" type="button" onClick={() => fields.remove(index)}>
<span className="icon-Cancel"></span>
</button>
: null}
</div>
{(index !== fields.length - 1 && fields.get(index)) ?
<StopInfo
departureTime={fields.get(index).time }
distance={fields.get(index).distance}
duration={fields.get(index).duration}
/>
: null}
</div>
</div>
));
}

Categories

Resources