How to set the value of inputs with ReactJS hooks? - javascript

I want to fill the inputs value of a form with default values once the modal is opened
I did it with pure javascript using document.getElementById(textId).value='some value as follow:
for(var i=0; i<details_data.length;i++){
let textId='text'+i;
let amountId='amount'+i;
document.getElementById(textId).value=details_data[i].text
}
This worked fine. but I want to know how to do it with React since I don't believe this is a best practice.
what i tried is to set the input value like this:
<input name='text' id={textId} value={el.text} onChange={details_handler.bind(index)}/>
But this woudn't let me change the value of the input. it just set the default value, and when i type in the input the value woudn't change as I want.
This is my code
const [details_data,set_details_data]=useState([
{'text':'','amount':''}
])
// called this function on `onChange` and store the data in `details_data`
function details_handler(e){
let name=e.target.name;
let value=e.target.value;
details_data[this][name]=value;
set_details_data(details_data);
}
JSX:
(In my case user can add inputs as many as he wants,That's why I put the inputs in a the mapping loop)
{
details_data.map((el,index) => {
let textId='text'+index;
let amountId='amount'+index;
return (
<div key={index}>
<input name='text' id={textId} value={el.text} onChange={details_handler.bind(index)}/>
<input name='amount' id={amountId} onChange={details_handler.bind(index)}/>
</div>
);
})
}

useEffect(() => {
if(detailsProps) {
set_details_data(detailsProps);
}
}, [detailsProps])
where your detailsProps (data from the api) will look something like this
detailsProps = [
{'text':'text1','amount':'100'},
{'text':'text2','amount':'200'}
]
onChange Function
const details_handler = (event, index) => {
const items = [...details_data];
items[index][event.target.name] = event.target.value;
set_details_data(items);
}
your view
{
details_data.map((el,index) => {
return (
<div key={index}>
<input name='text' value={el.text} onChange={(e) => details_handler(e, index)}/>
<input name='amount' value={el.amount} onChange={(e) => details_handler(e, index)}/>
</div>
);
})
}

Related

create the input tag by entering the required number

I have an input wuth "ok" button on a page and I want to write a number in my input, then by pressing the button, Input tags should be prepared for me according to the amount of the number I had entered
For example, if I enter the number 4 in my input and then click the OK button, 4 input tags will be created for me.
How can I write this code in react js?
I tried the folloing code but it's not working... .
import {useState} from "react";
const makeInputComponent = () => {
const [numberOfProcess, setNumberOfProcess] = useState(null)
const returnInput = ()=>{
return <input type="text" />
}
const makeInput = () => {
for (let i = 0; i < Number(numberOfProcess); i++) {
returnInput()
console.log(i)
}
}
return (
<div>
<label> enter your number </label>
<input type="text" value={numberOfProcess} onChange={(event)=>setNumberOfProcess(event.target.value)} />
<button onClick={ makeInput } > ok </button>
</div>
)
}
export default makeInputComponent ;
You can try this code.
const [numInputs, setNumInputs] = useState(0)
const createInputs = () => {
const inputArray = []
for (let i = 0; i < numInputs; i++) {
inputArray.push(<input type="text" key={i} />)
}
return inputArray
}
return (
<div>
<input
type="number"
value={numInputs}
onChange={(e) => setNumInputs(+e.target.value)}
/>
<button onClick={createInputs}>OK</button>
{createInputs()}
</div>
)
Solution:
Here is what you can do, take the value from the input and when button is pressed with that input value create a array of that length and then map that array for creating input box.
export default function App() {
const[val,Setval]=useState("")
const[inputbox,Setinputbox]=useState([])
const handleClick=()=>{
const array=new Array(val*1).fill(0)
Setinputbox(array)
}
return (
<div className="App">
<input type="number" value={val} onChange={e=>Setval(e.target.value)}/>
<button onClick={handleClick}>Submit</button>
<div>
{inputbox.map((val,index)=>{
return(
<input key={index} type="text"/>
)
})}
</div>
</div>
);
}

Cannot read the array property. Objects are not valid as a React child

I am following a tutorial exercise and I got the following error
Objects are not valid as a React child
I know this error is related to the object as I am trying to access the object but it needs an individual item of an object but not sure.
Why cannot the map loop over each item in the array?
Following is my code
var template = <h1>Indecision App</h1>;
var app = {
title: 'Indecision App',
subtitle: 'yo',
options: []
}
let count = 0;
function checkSubtitles (subtitle){
if(subtitle){
return <p>{subtitle}</p>
}else{
return undefined
}
}
function reset(){
count = 0;
reRenderApp();
}
function increaseCount(){
count++;
reRenderApp();
}
function onSubmitHandle(e){
e.preventDefault();
const options = e.target.elements.options;
app.options.push(options);
reRenderApp();
e.target.elements.options.value = ''
}
function removeAll(){
app.options = [];
reRenderApp();
}
function reRenderApp(){
var templateTwo = (
<div>
<h1>{app.title}</h1>
{checkSubtitles(app.subtitle)}
<p>Count: {count}</p>
<p>Array Length: {app.options.length > 0 ? app.options.length : '0 Items'}</p>
<ol>
{app.options.map((item)=>{
return <li key={item}>{item}</li>
})}
</ol>
<hr></hr>
<form onSubmit={onSubmitHandle}>
<input type="text" name="options" />
<input type="submit" value="Push to the Array" />
<input type="reset" value="Empty my list" onClick={removeAll} />
</form>
<button onClick={()=>{
increaseCount();
}}>Increase Count</button>
<button onClick={()=>{
reset();
}}>Reset Count</button>
</div>
)
ReactDOM.render(templateTwo, appRoot)
}
var appRoot = document.getElementById('app');
reRenderApp();
<body>
<div id="app"></div>
<script src="https://unpkg.com/react#16.0.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.0.0/umd/react-dom.development.js"></script>
<script src="./app.js"></script>
</body>
</html>
The main problem is, as you mentioned: Objects are not valid as a React child
But, what is happening?
If we go into:
function onSubmitHandle(e){
e.preventDefault();
// Line 1
const options = e.target.elements.options;
app.options.push(options);
reRenderApp();
// Line 2
e.target.elements.options.value = ''
}
So in Line 1, you're pushing options into the options array.
But, then in Line 2, we can notice options has an attribute (so, it's an object)
So, if you change Line 1, from:
const options = e.target.elements.options;
To this:
const options = e.target.elements.options.value;
It'd work.
Also, to check what I'm saying you have 2 options:
option 1: console.log
function onSubmitHandle(e){
e.preventDefault();
const options = e.target.elements.options;
console.log({ options })
app.options.push(options);
reRenderApp();
e.target.elements.options.value = ''
}
option 2: make that option a valid child of react with JSON.stringify()
<ol>
{app.options.map((item, index)=>{
return <li key={index}>{JSON.stringify(item)}</li>
})}
</ol>
You can do
{app.options.length && app.options.map((item)=>{
return <li key={item}>{item}</li>
})}
But you must be sure that "item" here is not an object as you can't render an object
The reason for this is that your options array is going to be filled with elements as you're pushing the input element with the name of "option" into your array - this elements are objects in JS which you can't render out as list items.
Use React State to store anything that's going to change in the UI - in this case your list of options> So rather than doing
var app = {
title: 'Indecision App',
subtitle: 'yo',
options: []
}
let count = 0;
Do:
const [options, setOptions] = React.useState([]);
const [count, setCount] = React.useState(0);
Title and subtitle are probably not going to change, so just put them in h1 & h2 elements - if they are, then use the state pattern again.
Get rid of the two inputs with types of "submit" & "reset" just use normal button elements instead.
You'll also need an onchange event on your input where the text will go in and each time the onchange event is fired (i.e, when a user types) you'll need to save the input text
const [inputText, setInputText] = React.useState('');
const handleChange = (e) => {
const {value} = e.target;
setInputText(value)
}
<input type="text" value={inputText} onChange={handleChange}/>
Then in your onHandleSubmit function, just have
const onHandleSubmit = () => {
setOptions([...options, inputText]);
setInputText('')
}
This should work

How to Handle Multiple Checkboxes in typescript

I am trying to handle multiple checkboxes as follows:
sendFileNameToBackEnd = (filename: string[]) => {
console.log(filename)
this.vizsualizaForFileName(filename)
}
render() {
const handleChange = (checked: boolean, event: React.FormEvent<HTMLInputElement>) => {
let listoffilename : Array<string> = []
const target = event.currentTarget;
const name = target.name;
if(checked && name !== '')
{
listoffilename.push(name)
this.setState({
listoffilename1: listoffilename
});
}
console.log(this.state.listoffilename1)
};
return (
<DataList aria-label="Checkbox and action data list example" isCompact >
{ this.state.fileListData ?
this.state.fileListData.map((fd) =>
<DataListItem aria-labelledby="check-action-item1">
<DataListItemRow>
<DataListCheck
id="controlled-check"
aria-labelledby="check-action-item"
isChecked={isChecked1}
onChange={handleChange}
name={fd}/>
<DataListItemCells
dataListCells={[
<DataListCell key="primary content">
<span id="check-action-item1">{fd ? fd : "Loading..."}</span>
</DataListCell>
]}
/>
</DataListItemRow>
</DataListItem>
):
<>
<EmptyState>
<Title headingLevel="h4" size="lg">Empty state</Title>
</EmptyState>
</>
}
</DataList>
<Button variant="primary" onClick={ () => {this.sendFileNameToBackEnd(this.state.listoffilename1)}}>Visualize</Button>
);
}
In the above code, when I select any checkbox, I can get its value, but when I choose multiple checkboxes I only getting one value in an array.
In 'this.state.fileListData' I have n number of data and I am displaying a checkbox using a map and one button called 'Visualize'. When we select some of the values from the checkbox and click on the 'Visualize' button, I want to send all selected values to the backend for another operation.
When I select multiple checkboxes then it only sends one value in it not all selected values.
Guide me if I am doing something wrong.

Call function on default when checkboxes are rendered

this.state.pageData.map((elem) =>
elem.map((ele) => {
this.props.AllSelectedFlag ? (
<Td>
{' '}
<Input type="checkbox" value={ele.id} checked={true} onClick={(e) => this.selectHandle(e, ele)} />
</Td>
) : (
<Td>
<Input type="checkbox" value={ele.id} onClick={(e) => this.selectHandle(e, ele)} />
</Td>
);
}),
);
Basically I have to check all the checkboxes when select all button is pressed, I am changing the state of AllSelectedFlag when button is pressed, but the problem is onClick button is not working when the condition is true.
Any other way to solve this?
Approach 1: If you have "checked" property
You don't need to render and apply check properties for checkboxes, you just need to set "checked" property for all elements on "all" selection and reset.
And you can add event onChange on each checkboxes that will be used for individual check/uncheck part.
Refer to example: https://codesandbox.io/s/vvxpny4xq3
Approach 2: if you dont have "checked" property in json
Maintain local array with "ids" inside and oncheck/uncheck add/remove from it and use it for handling check related cases
const [isCheckAll, setIsCheckAll] = useState(false);
const [isCheck, setIsCheck] = useState([]);
const [list, setList] = useState([]);
const handleSelectAll = e => {
setIsCheckAll(!isCheckAll);
setIsCheck(list.map(li => li.id));
if (isCheckAll) {
setIsCheck([]);
}
};
const handleClick = e => {
const { id, checked } = e.target;
setIsCheck([...isCheck, id]);
if (!checked) {
setIsCheck(isCheck.filter(item => item !== id));
}
};
Refer to example: https://codesandbox.io/s/react-select-all-checkbox-jbub2

React autoFocus sets cursor to beginning of input value

I have a controlled input that has a value initially showing.
I have set that input to autoFocus but the cursor appears at the beginning of the input when I am wanting it to appear at the end. I understand this might be because the autoFocus is added before the value is but I'm not 100% sure.
What would be the best way to accomplish the cursor initializing at the end of the input field?
var Test = React.createClass({
getInitialState: function() {
return {
teamId: 'fdsfds'
};
},
render: function() {
return (
<input type="text" autoFocus value={this.state.teamId} onChange={this.setTeamId} />
);
},
setTeamId: function(event) {
this.setState({ teamId: id });
},
});
ReactDOM.render(
<Test />,
document.getElementById('container')
);
https://jsfiddle.net/69z2wepo/34486/
One solution:
<input
type="text"
autoFocus
value={this.state.teamId}
onChange={this.setTeamId}
onFocus={function(e) {
var val = e.target.value;
e.target.value = '';
e.target.value = val;
}}
/>
https://jsfiddle.net/o3s05zz4/1/
Adaptation of this answer: https://stackoverflow.com/a/2345915/1589521
This actually works:
componentDidMount() {
const input = this.input;
const length = input.value.length;
input.focus();
input.setSelectionRange(length, length);
}
render() {
return (
<input ref={ref => this.input = ref} ... >
)
}
PS. If you need to support IE8 and below you'll need to use IE-specific checks.
This way the text of the input will be selected, ready to edit
<input
type="text"
defaultValue="Untitled"
autoFocus
onFocus={e => e.currentTarget.select()}
/>
Setting the input value inside componentDidMount seems to do the trick, but it feels like a hack:
componentDidMount: function(){
this.inputElement.value = this.state.teamId;
},
render: function() {
var that = this;
return <input ref={function(ref){that.inputElement = ref;}} type="text" autoFocus value={this.state.teamId} onChange={this.setTeamId} />;
},
https://jsfiddle.net/yuk13tuu/
In TypeScript:
private inputDOM: HTMLInputElement | null;
public componentDidMount() {
if (this.inputDOM != null) {
this.inputDOM.value = '';
this.inputDOM.value = this.state.newRegionName;
}
}
public render() {
return <input ref={(ref: HTMLInputElement | null) => this.inputDOM = ref} type="text" autoFocus={true} value={this.state.inputValue} />;
}
Looks like the html attribute autofocus doesn't take any parameters to specify where the cursor should start. See mdn documentation.
Sitepoint has a great tutorial explaining your options for setting cursor position from within an input box.
As for the reacty side of things, you'll simply put your jQuery (or other cursor related code) in the componentDidMount lifecycle method.

Categories