Get the values of all input parameters in React - javascript

I'm creating a webapp that allows users to create custom input components. This is in the form of MUI Select and TextFields:
const CTextField = (
<TextField id="outlined-basic" variant="outlined" size="small" />
);
const CSelect = function(match) {
match = match.substring(1, match.length - 1);
const matches = match.split("/");
let menus = [];
matches.forEach(single => {
menus.push(<MenuItem value={single}>{single}</MenuItem>);
});
return (
<FormControl>
<Select>{menus}</Select>
</FormControl>
);
};
The issue I'm coming across is, how can I grab all the values placed or selected from the components. They're created by the end user, so there can be any number of them, and I'm trying to keep it in order with plain text.
What I think works is simply grabbing the raw data including html and stripping it away, then grabbing the values. But because I want this to be dynamic, that'd end up being slow the larger the text is. Is there an efficient method of grabbing all the raw text as well as the values of the input components?

Don't worry about how many elements you have - that's why Arrays are there:
const { useState } = React
const App = (props) => {
const [inputFieldsList, setInputFieldsList] = useState([]);
return (
<div>
<h1>Input field list:</h1>
<button
onClick={() => {
const newVal = [...inputFieldsList, '']
setInputFieldsList(newVal)
}}>ADD INPUT FIELD</button>
{inputFieldsList.map((item, i) => (
<div>
<input
type="text"
onChange={(e) => {
const newList = [...inputFieldsList]
newList[i] = e.target.value
setInputFieldsList(newList)
console.log(inputFieldsList)
}}
/>
</div>
))}
</div>
);
}
const rootElement = document.getElementById('app')
ReactDOM.render(<App />, rootElement)
<script crossorigin src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
<div id="app"></div>

You should use useState for storing the values of the input, and onChange function to set your state when a user types in the input.
const Hello = () => {
const[values, setValues] = useState({
input1: '',
input2: ''
});
const changeHandler = () => {
setValues(
...values,
[event.target.name]: event.target.value
);
}
return (
<>
<input type="text" value={values.input1} name="input1" onChange={changeHandler} />
<input type="text" value={values.input2} name="input2" onChange={changeHandler} />
</>
);
}

Related

How Do I Display a Number of Input Elements, Equal to a Selected Number (React.js)

I have some difficulties with a little peace of React code that I am writing.
The Goal:
You select, for example, the number 4 from a selection field. Which will result in having 4 (the same amount as the selected number) input fields appear below.
The Problem:
I can't seem to make the equal amount of inputs appear in my DOM.
image:
wrong_result
My Code:
calc.js
useEffect(() => {
setLevelInputs([LevelInputs, {id: 0}]);
for (let i = 0; i < BarracksLevel.length; i++) {
const number = i
setLevelInputs(...LevelInputs, {id: number});
}
setDisplayBoxes(
<>
{ LevelInputs.map((item, index) => {
return (
<>
<input key={item.id} type="number" onChange={(e) => {setFighterLevels({...FighterLevels, [e.target.value]: e.target.value})}} name={"input_"+item.id} className="TailwindCSS"></input>
</>
);
})}
</>
)
}, [BarracksLevel]);
I might have overlooked something very simple, but thought it wouldn't hurt to ask!
Thanks in advance,
An enthusiastic Rookie
const { useState, useEffect } = React
const App = () => {
const [count, setCount] = useState(1);
const [inputs, setInputs] = useState([<input />])
useEffect(() => setInputs(
Array.from({length: count}).map(_ => <input />)
), [count])
return <div><input type="number" value={count} onChange={e => setCount(e.target.value)} />{inputs}</div>
}
ReactDOM.render(<App/>, app)
<script crossorigin src="https://unpkg.com/react#17/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.production.min.js"></script>
<div id=app>

Input values are syncing up and I'd rather they not

ChildComponent displays different fragments depending on the index passed in. This works fine, but if I have input element on multiple fragments and I put a value in one it gets automatically copied to the others. Why is this happening and how can I stop it?
const { Fragment } = React;
const fragments = (onChangeHandler) =>
[
<input type="text" id="screen1_input1" onChange={onChangeHandler} />,
<input type="text" id="screen2_input1" onChange={onChangeHandler} />
];
const ChildComponent = ({ index, fragments }) => {
const onChange = e => {
const { target: {id, value} } = e;
console.log(id, value);
const newData = {
...contentData,
[e.target.id]: e.target.value
}
setContentData(newData)
};
return (
<Fragment>
<h2 className="screens">{fragments(onChange)[index]}</h2>
</Fragment>
);
};
const ParentComponent = props => {
return <ChildComponent index={1} fragments={fragments}/>;
};
ReactDOM.render(<ParentComponent />, document.getElementById("react"));
Give them unique keys like so:
const fragments = (onChangeHandler) =>
[
<input key="key1" type="text" placeholder="input 1" id="screen1_input1" onChange={onChangeHandler} />,
<input key="key2" type="text" placeholder="input 2" id="screen2_input1" onChange={onChangeHandler} />
];
Here a Sandbox to demonstrate it: https://codesandbox.io/s/keen-sun-vsk3e?file=/src/App.js:709-710
React uses the key prop to understand the component-to-DOM Element relation, which is then used for the reconciliation process. It is therefore very important that the key always remains unique, otherwise there is a good chance React will mix up the elements and mutate the incorrect one.
Reference: https://stackoverflow.com/a/43892905/1927991

React Controlled Form with Child /Parent component

I'm building a controlled form with dynamic fields.
The Parent component get data from a redux store and then set state with the values.
I don't want to make it with too much code lines so I turn the dynamic fields into a component.
States stay in the parent component and I use props to pass the handlechange function.
Parent :
function EditAbout(props) {
const [img, setImg] = useState("");
const [body, setBody] = useState(props.about.body);
const [instagram, setInstagram] = useState(props.about.links.instagram);
const [linkedin, setLinkedIn] = useState(props.about.links.linkedin);
const [press, setPress] = useState(props.about.press)
const handleSubmit = (e) => {
// Submit the change to redux
};
// set states with redux store
useEffect(() => {
setBody(props.about.body);
setInstagram(props.about.links.instagram);
setLinkedIn(props.about.links.linkedin);
setPress(props.about.press);
}, []);
const handleChangeChild = (e, index) => {
e.preventDefault();
let articles = press
const {value, name } = e.target
if (name === "title") {
articles[index].title = value;
} else {
articles[index].link = value;
}
setPress(articles)
console.log(articles[index])
}
return (
<Box>
<h1>CHANGE ABOUT ME</h1>
<Input
label="Image"
name="img"
type="file"
variant="outlined"
margin="normal"
onChange={(e) => setImg(e.target.files)}
/>
<Input
label="body"
value={body}
name="body"
onChange={(e) => setBody(e.target.value)}
variant="outlined"
multiline
rowsMax={12}
margin="normal"
/>
<Input
label="instagram"
value={instagram}
name="instagram"
variant="outlined"
margin="normal"
onChange={(e) => setInstagram(e.target.value)}
/>
<Input
label="Linkedin"
value={linkedin}
name="linkedin"
variant="outlined"
margin="normal"
onChange={(e) => setLinkedIn(e.target.value)}
/>
<Child press={press} onChange={handleChangeChild} />
{props.loading ? (
<CircularProgress color="black" />
) : (
<Button onClick={handleSubmit} variant="contained">
Send
</Button>
)}
</Box>
);
}
Child :
function Child(props) {
const { press, onChange } = props;
const inputsMarkup = () =>
press.map((article, index) => (
<div key={`press${index}`} style={{ display: "flex" }}>
<input
name="title"
value={press[index].title}
onChange={(e) => onChange(e, index)}
/>
<input
name="link"
value={press[index].link}
onChange={(e) => onChange(e, index)}
/>
<button>Delete</button>
</div>
));
return (
<div>
<h1>Press :</h1>
{inputsMarkup()}
</div>
);
}
Everything is fine when I'm typing in the Parent inputs. But when I'm using Child fields state update for one character but come back at its previous state right after.
It also doesn't display the character change. I can only see it in the console.
Thanks you in advance for your help
The problem is that you're mutating the state directly. When you create the articles variable (let articles = press) you don't actually create a copy and articles doesn't actually contain the value. It's only a reference to that value, which points to the object’s location in memory.
So when you update articles[index].title in your handleChangeChild function, you're actually changing the press state too. You might think that's fine, but without calling setPress() React will not be aware of the change. So, although the state value is changed, you won't see it because React won't re-render it.
You need to create a copy of the press array using .map() and create a copy of the updated array element. You can find the updated handleChangeChild() below:
const handleChangeChild = (e, index) => {
e.preventDefault();
const { value, name } = e.target;
setPress(
// .map() returns a new array
press.map((item, i) => {
// if the current item is not the one we need to update, just return it
if (i !== index) {
return item;
}
// create a new object by copying the item
const updatedItem = {
...item,
};
// we can safely update the properties now it won't affect the state
if (name === 'title') {
updatedItem.title = value;
} else {
updatedItem.link = value;
}
return updatedItem;
}),
);
};

Store Values from Material UI's form in TypeScript

What's the best way to store values typed into the text fields here?
const AddUserPage = () => (
<div>
<PermanentDrawerLeft></PermanentDrawerLeft>
<div className='main-content'>
<form className="ROOT" noValidate autoComplete="off">
<TextField id="standard-basic" label="Standard" />
</form>
</div>
</div>
);
export default AddUserPage;
I want to find a way such that I can use the stored values in my GraphQL mutations as well, without having to modify the const() structure of my page. I don't want to use the Class Component Extend or function structure here.
What is your const() structuremakes:
=> (This is the auto return syntax.)
If you want to store/reuse your value, you will have to define some state/variable to store the data.
You can also do it in upper component like:
import React, { useState } from "react";
const Parent = props => {
const [state, setState] = useState({ text: "" });
return <AddUserPage value={state.text} onChange={e => setState(prev => ({ ...prev, text: e.target.value || "" }))} />
}
const AddUserPage = ({ value = "" , onChange }) => (
<div>
<PermanentDrawerLeft></PermanentDrawerLeft>
<div className='main-content'>
<form className="ROOT" noValidate autoComplete="off">
<TextField id="standard-basic" value={value} onChange={onChange} label="Standard" />
// value, and Onchange comes from an upper component
</form>
</div>
</div>
);

Children input actions in react parent

I have Form component which should get form elements like inputs, buttons etc. How i can detect onChange or onClick events or value if they passed like children? For example i change the second input and how detect that i change exactly second input but not another
I need to detect all actions in Form component
const Form = ({children, ...rest}) => {
const onChangeHandler = () => {
//detect change input
}
return (
<div style={{display: 'flex', flexWrap: 'wrap'}}>
{children}
</div>
)
}
const App = () => {
return <Form>
<input style={{width: '100%'}}/>
<input style={{width: '100%'}}/>
<input style={{width: '100%'}}/>
</Form>
}
ReactDOM.render(<App/>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id="root"/>
How about using the React.cloneElement method?
const Form = ({children, ...rest}) => {
const onChangeHandler = (e) => {
const value = e.target.value
const id = e.target.id
console.log(value, id)
}
return (
<div style={{display: 'flex', flexWrap: 'wrap'}}>
{React.Children.map(children, (child, index) => {
return React.cloneElement(child, {
onChange: onChangeHandler
})
})}
</div>
)
}
const App = () => {
return <Form>
<input id="input1" />
<input id="input2" />
<input id="input3" />
</Form>
}
ReactDOM.render(<App/>, document.getElementById('root'))
Update: yes sorry it was untested, there's no way the input would know about the Form onChangeHandler so you just map it to the onChange in cloneElement.
I've added id attributes to each input so that you can see how the value for each input is change in the onChangeHandler. If you are wanting to save the value to state you can then use this id as a key in something like Redux.
React.cloneElement is what you need for this.
From the docs,
Clone and return a new React element using element as the starting
point. The resulting element will have the original element’s props
with the new props merged in shallowly. New children will replace
existing children. key and ref from the original element will be
preserved.
import React from 'react'
import ReactDOM from 'react-dom'
const Form = ({children, ...rest}) => {
const onChangeHandler = e => {
//detect change input
const value = e.target.value
const name = e.target.name
console.log('You have changed ', name, ' with value ', value)
}
return (
<div style={{display: 'flex', flexWrap: 'wrap'}}>
{React.Children.map(children, (child, index) => {
return React.cloneElement(child, {
onChange: onChangeHandler,
name: `input${index + 1}`,
})
})}
</div>
)
}
const App = () => {
return (
<Form>
<input />
<input />
<input />
</Form>
)
}
ReactDOM.render(<App />, document.getElementById('root'))
Demo

Categories