I am dynamically creating a form according to the button clicks, so when the user presses the button, the form generates with a new id. SO, i created a state for the createform counter like this,
const [formmakersno, setFormmakersno] = useState([0]);
and inside my render, I have something like this :
{formmakersno.map((no) => {
return formDetails;
})}
My "formDetails" is a constant with jsx, it just returns the form elements like a textfield and a button. Everything works fine, Everytime i click the button the form generates as expected, But, I want to pass the counter number every-time it creates a new form, as a prop, since i cannot do this when it is just a const which returns a jsx, I converted it into an arrow function so that i can pass props and passed it like this :
{formmakersno.map((no) => {
return <formDetails value={no} />;
})}
And i could access the props too, but the problem is that , everytime I type inside the textfield for each letter the textfield goes out of focus, because its a component it renders everytime because I am calling an Onchange method with a state change inside it. Is there a workaround for this ?
EDIT : I created this simple example to further illustrate the problem I am facing :
export const CreatePush2 = () => {
const [titles, setTitles] = useState(["", ""]);
const [numbers, setNumbers] = useState([0, 1, 2]);
const handleChange = (e, id) => {
const { value } = e.target;
let titles_1 = [...titles];
let titles_11 = titles_1[id];
titles_11 = value;
setTitles(titles_1);
console.log(titles);
};
const InputCreator = ({ id }) => {
return (
<form>
<input type="text" onChange={(e) => handleChange(e, id)} value={titles[id]} />
<br />
</form>
);
};
return (
<div>
{numbers.map((number) => {
return <InputCreator key={number.toString()} id={number} />;
})}
</div>
);
};
export default CreatePush2;
The textfield loses focus with every letter typed.
Related
I'm trying to achieve the following:
1.) Create a variable X containing some text.
2.) Create HTML element (paragraph) which displays text from variable.
3.) Have an HTML Input field to change the variable X content.
4.) Once content of the variable X changes, display updated value/text in the paragraph where it was previously displayed.
I have the following code which seems to log into the console the new value after change, but the paragraph is not updated with the new variable's value:
import './App.css';
import Input from '#material-ui/core/Input'
function App() {
let X = 'My text 1';
const handleChangeInputField = (event) => {
valueOfInputField = event.target.value
console.log(valueOfInputField)
}
return (
<div className="App">
<Input onChange={ handleChangeInputField } />
<p>{ X }</p>
</div>
);
}
export default App;
Any ideas why my <p> element is not updated when the variable X's value is changed?
Thank you
Reactjs listens for state changes, not variable value changes. You should use useState hook here. Read more about it here: https://reactjs.org/docs/state-and-lifecycle.html
const [x, setX] = useState('Some text');
...
const handleInputChange = (event) => {
setX(event.currentTarget.value);
}
...
By the way, your handleInputChange function should be a useCallback hook IMO:
const handleInputChange = useCallback((event) => {
setX(event.currentTarget.value);
}, []);
This question already has answers here:
Why does calling react setState method not mutate the state immediately?
(9 answers)
Closed 1 year ago.
I was using useState hook and it seems that the hook is not working properly. When I click on one of my radio buttons initially it logs nothing, but after my second click it logs the previous one that I clicked. Every time I click on any button It logs the button that I previously clicked. #sidenote: (The 'info' that I've imported is an array of objects and each 'incorrect_answers' property has an array as value) . Here is my code:
import React, {useState} from 'react'
import { info } from "./data";
const Quiz = () => {
const [i, setI] = useState(0);
const [value, setValue] = useState('');
const {correct_answer, incorrect_answers} = info[i]
const arr = [correct_answer, ...incorrect_answers].sort((a, b) => a.length - b.length);
console.log(arr)
const handleSubmit = (e) => {
e.preventDefault();
setI(i + 1);
}
const handleChange = (e) => {
setValue(e.target.value);
console.log(value);
}
return (
<div className="quiz">
<form className='quiz__form' onSubmit={(e) => handleSubmit(e)}>
<div className="form__body" >
{arr.map((item, index) => {
return (
<div className="form__item" key={index}>
<label htmlFor={`option-${index}`}>
<input
type="radio"
name="options"
id={`option-${index}`}
value={item}
onClick={(e) => handleChange(e)}
/> {item}
</label>
</div>
)
})}
</div>
</form>
</div>
)
}
Can anyone tell me where I am wrong?
setValue is asynchronus. So you will have to wait to see the change. One thing you can do to see the change is add this following code
useEffect(() => {
console.log(value)
}, [value])
This way you can see when the value of the value changes
The setState function in reactjs (setValue in your case) is an asynchronous function, it update the state at the end of the event handler (handleChange), so the value variable wil still refer the previous state inside the event handler (handleChange).
Try to add console.log before the returned JSX, then click on input radio you see log after the event handler was executed
const handleChange = (e) => {
setValue(e.target.value);
console.log(value);
}
console.log("value after handle event",value);
return (
For more information about hou reactjs update state please check here
I would like to do something like this to start the function of creating a new component after clicking on the button. Moreover I want the div element with its content to appear every time I click a button. (in my case my own component)
For example when i want to create questionnaire and add questions i need a button who let me add input for this question. You know, duplicate components.
I created 3 functions, but I don't know which one is closest to the solution. Can you help me :c ? 3 days i trying do this. Thanks in advance for your time.
const Adding = () => {
const components = {
button : Button
}
components.map((question, index) => (
<Button key={index}/>
))
}
//customElementsRegistry.define('input-text', Button , { extends: 'div' });
const AddQ = () => {
let counter = 0;
const newEl = document.createElement("input-text"); // ta funkcja tworzy tyle komponentów ile chce ale tylko np divy
newEl.innerText = `przykładowy tekst ${++counter}`;
const div = document.querySelector("#ankieta");
div.appendChild(newEl);
}
function AddQuestion(){
const el = React.createElement("Button", {}, 'My First React Code'); // ta funkcja renderuje mi tylko jeden w tym wypadku button
ReactDOM.render(
el,
document.getElementById('ankieta'))
}
While using React, whenever possible, avoid using original DOM functions. Instead, use React's internal mechanisms to generate and modify the DOM:
// React functional component
const App = () => {
// Maintain a clicked state using `useState`
const [counter, setCounter] = React.useState(0);
// On click, increment counter
const onClick = () => setCounter(counter + 1);
// Generate the array of texts
// Use `useEffect` to only recalculate when `counter` changes
const inputTexts = React.useEffect( () =>
(new Array(counter))
.map( (_, index) => `przykładowy tekst ${index}` ),
[counter]);
return (
<>
<Button onClick={ onClick }>
{/* The DIV where you append the generated inputs */}
<div id="ankieta">
{
// Generate the inputs from the texts generated earlier
inputTexts
.map( (value, index) => <input key={index} value={value} />)
}
</div>
</>
)
}
Note: The generated inputs are not being updated and has a fixed value. This means that their value cannot be modified. If you need to be able to modify them, you'll need to create the corresponding onChange function and save the value to some state: instead of using const inputTexts = useEffect(...) you'll need to do some more sophisticated state management, or manage it using a self-managed sub-component.
This is a tricky question and I have been having a hard time figuring out this. First of all I'm using useContext to make a global state. The global state will hold and serve an array with objects. I have a form rendered together with every object. The form will have an input with an value.
My goal is to be able to find the object and update the value. For example the input in Item1 will update "20" to whatever new number that are being inputted, same with Item2
What is happened now I that every time I submit an input, a whole new array are being created instead on updated. I know its a whole easier way to achieve this, but I need this array to be in a global state.
here's link to my sandbox
https://codesandbox.io/s/muddy-wildflower-h5hhw?file=/src/App.js
Thanks!
You need to specify which Array Item you want to update. First of all you need to specify which item you need to update. For this i've passed the value id through the card props
<Card
name={value.name}
value={value.Itemvalue}
key={value.id}
id={value.id}
/>
And i've used map to update the specific object
const updatedData = value.map((obj) => {
if (obj.id === id) {
return { ...obj, Itemvalue: itemValue };
} else return obj;
});
updateValue(updatedData);
And here is the working Link
The problem is you're adding a new element everything the form is updated. Which is different from what you need. Pass id to the update function, so that you can update that particular item.
Your code
const addValue = (event) => {
event.preventDefault();
// You're not updating existing element, instead adding a new value.
updateValue((prevItems) => [...prevItems, { Itemvalue: itemValue }]);
};
// ...
<form onSubmit={addValue}>
<input
type="text"
name="price"
Value={itemValue} // --> Typo here
onChange={updateItemValue}
/>
<button>Submit</button>
</form>
You should pass the id as props instead of getting it from context. So <Add /> knows exactly where to update the value.
Corrected code
import React, { useContext, useState } from "react";
import { ValueContext } from "./ValueContext";
const Add = ({ id }) => { // --> Passing id from <Card />
const [itemValue, setItemValue] = useState("");
const [value, updateValue] = useContext(ValueContext);
const updateItemValue = (event) => {
setItemValue(event.target.value);
};
const addValue = (event) => {
event.preventDefault();
updateValue((prevItems) => {
const index = prevItems.findIndex(x => x.id === id);
const updatedItem = {
...prevItems[index],
Itemvalue: itemValue
}
return [
...prevItems.slice(0, index),
updatedItem, // --> Updating the item, instead of creating new one
...prevItems.slice(index + 1)
]
});
};
return (
<form onSubmit={addValue}>
<input
type="text"
name="price"
Value={itemValue}
onChange={updateItemValue}
/>
<button>Submit</button>
</form>
);
};
export default Add;
So I have two components: an Input component which is basically just a button that sets the current status of the input value to active and then sends a value object to its parent component Question:
import React, { useState, useEffect } from 'react';
import './Input.css';
const Input = (props) => {
// input state:
const title = props.title;
const index = props.index;
const [active, setActive] = useState(false);
const [inputValue, setInputValue] = useState({index, title, active});
// sets active status based on what status is in Question component
// the logic there would only allow 1 radio input to be active as opposed to checkboxes where we have multiple active
useEffect(() => {
setActive(props.active);
}, [props.active]);
// stores activity status of single input and re-runs only when 'active' changes (when clicking the button)
useEffect(() => {
setInputValue({index, title, active});
}, [active]);
// returns updated input value to Question component
useEffect(() => {
return props.selected(inputValue);
}, [inputValue]);
return (
<div className='input'>
<button
data-key={title}
className={props.active ? 'highlight' : ''}
onClick={() => setActive(active => !active)}
>
{title}
</button>
</div>
);
}
export default Input;
And Question checks if the current question type (which it receives from another parent component) is a 'radio' button type in which case you can only have one option. So currently I set it up like this:
import React, { useState, useEffect } from 'react';
import s from './Question.css';
import Input from './Input/Input';
const Question = (props) => {
// create intitial state of options
let initialState = [];
for (let i=0; i < props.options.length; i++) {
initialState.push(
{
index: i,
option: props.options[i],
active: false,
}
)
}
// question state:
let questionIndex = props.index;
let questionActive = props.active;
let questionTitle = props.question;
let questionType = props.type;
let [questionValue, setQuestionValue] = useState(initialState);
let [isAnswered, setIsAnswered] = useState(false);
useEffect(() => {
console.log(questionValue);
}, [questionValue]);
// stores currently selected input value for question and handles logic according to type
const storeInputValue = (inputValue) => {
let questionInputs = [...questionValue];
let index = inputValue.index;
// first set every input value to false when type is radio, with the radio-type you can only choose one option
if (questionType === 'radio') {
for (let i=0; i < questionInputs.length; i++) {
questionInputs[i].active = false;
}
}
questionInputs[index].active = inputValue.active;
setQuestionValue([...questionInputs]);
// set state that checks if question has been answered
questionValue.filter(x => x.active).length > 0 ? setIsAnswered(true) : setIsAnswered(false);
}
// creates the correct input type choices for the question
let inputs = [];
for (const [index, input] of props.options.entries()) {
inputs.push(
<Input
key={index}
index={index}
title={input}
active={questionValue[index].active}
selected={storeInputValue}
/>
);
}
// passes current state (selected value) and the index of question to parent (App.js) component
const saveQuestionValue = (e) => {
e.preventDefault();
props.selection(questionValue, questionIndex, questionTitle);
}
return (
<div className={`question ${!questionActive ? 'hide' : ''}`}>
<h1>{props.question}</h1>
<div className="inputs">
{inputs}
</div>
<a className={`selectionButton ${isAnswered ? 'highlight' : ''}`} href="" onClick={e => saveQuestionValue(e)}>
<div>Save and continue -></div>
</a>
</div>
);
}
export default Question;
With this setup when I click on an input it sends that to the Question component and that one returns a prop.active to Input so it highlights the input value. But when I click a new input it re-renders twice since it listens to the active state changing in Input, and sets all inputs to false.
My question is: how can I set up the logic in this code to act like a radio input so it only sets the currently selected input to active instead of first setting every input to active = false?
You should not be duplicating the value of state in two different components. There should always only be a single source of truth for what a value is in state. This is one of the most important patterns of React and is even mentioned in the official documentation.
Instead, you should lift shared state up so that it lives at the "closest common ancestor". Your <Input> components should not have any internal state at all - they should be pure functions that do nothing except render the current value and provide a callback to update said value. But, they do not store that value themselves - it is passed to them as a prop.
All of the logic for which input is active and what value it has should live in the parent component and be passed down from there. Whenever you are setting state in a child component and then somehow passing that state back up to a parent is a warning flag, because in React the data should flow down.