There is a field in which the static path of file formation is registered. I have made this field editable in case the user wants to change the path for the formation according to his desire.
<div className="w3-col l8 m8 s8">
<input
className="w3-input"
type="text"
name="file_name"
defaultValue={`${test_path}`}
disabled={false}
/>
</div>
Так же есть кнопка, при клике которой вызывается функция формирования файла по заданному пути
<Btn_enabled
func={() => create_file(`${test_path}.xmind`)}
name="Create file"
/>
How can I transfer the modified path by the user to the button?
You need to introduce a state for your input component.
And just reference it (by passing it through props, state manager, etc).
For example, input value state named value:
// With hooks
function InputValueToButton() {
const [value, setValue] = useState(testPath);
return (
<div className="App">
<input
onChange={e => setValue(e.target.value)}
type="text"
value={value}
/>
<Btn_enabled func={() => create_file(`${value}.xmind`)}/>
</div>
);
}
If you are using a version of React that does not support hooks, you can achieve the same thing using classes:
// React classes
class InputValueToButton extends React.Component {
state = {
value: testPath
};
render() {
return (
<div className="App">
<input
onChange={e => this.setState({ value: e.target.value })}
type="text"
value={this.state.value}
/>
<button>{this.state.value}</button>
</div>
);
}
}
Related
I am creating an invoice generator for my personal project so I can learn and understand the MERN stack better. I currently have this as part of my state. I currently have my className linked to my CSS, as I learned that could be manipulated to use create the handleChange function, so I wasn't sure if I needed to completely change that. I have tried working with index, by mapping each object to it's index, but it's just been confusing.
state = {
...
itemValues: [{
description: "",
rate: "",
quantity: "",
amount: "",
tax: ""
}],
...
}
and this as my mapped value
itemValues.map((item, i) => {
let descId = `description-${i}`;
let rateId = `rate-${i}`;
let quanId = `quantity-${i}`;
let amonId = `amount-${i}`;
let taxId = `tax-${i}`;
return(
<div key={i} className="item-row">
<input
className="item item-description"
type="text"
id={descId}
name={descId}
placeholder="Item description"
data-id={descId}
onChange={this.handleChange('desc')}
/>
{/* */}
<input className="item item-rate"
type="text"
id={rateId}
name={rateId}
placeholder="Item rate"
data-id={rateId}
/>
{/* */}
<input className="item item-quantity"
id="item-quantity"
type="text"
placeholder="item quantity"
data-id={quanId}
/>
{/* */}
<input className="item item-amount"
id="item-amount"
type="text"
placeholder="item amount"
data-id={amonId}
/>
{/* */}
{this.state.taxation === "None" ? "" : <input class="item item-tax"
id="item-tax"
type="text"
placeholder="tax(%)"
data-id={taxId}
/>}
<input type='button' value='remove' onClick={this.removeClick}
/>
</div>
)
})
}
I'm not sure how to set my handleChange function, to allow dynamic inputs to be saved.
Since is a mapping into a global component like itemValuesComponent for example, you need more than one function or an index as well to set items values into the state.
You can try the follow:
const handleChange = (location, property) => (e) => {
this.setState(prevState => ({
...prevState,
itemValues: [...prevState.itemValues.slice(0, location),
{
...prevState.itemValues[location],
[property]: e.target.value,
}
, ...prevState.itemValues.slice(location)]
}))
}
and when you need it:
<input
...
onChange={this.handleChange(i,'description')}
/>
Please, let me know if this work.
Try to use React Hooks, I'm pretty sure that it will simplify your code.
Don't get this confused with checking each radio button I have on the page. I want to implement a check all button that sets the value of a nested object state equal to a certain value. I am storing each question in a nested state. Ex.
formQuestions({
kitchen: [question,question2,question3],
living: [question,question2,question3]
})
Four radio buttons are being made for each question. Now one radio button can only be selected at once. Each radio button has its' own value. Ex. `"Good", "Fair", "Poor", "N/A".
When a radio button is selected a state is generated dynamically for that section and question. Ex.
formAnswers({
kitchen: {
question: "Good"
question2: "Poor"
}
})
The goal here is the button that I want to create that checks only one value for each question Ex. clicks button question: "Good", question2: "Good" etc..
For me to set the state of a dynamic value I would need the "Section name" lets call it Name and the "Question" we'll call it question. That would give me access to the value like so formAnswers[Name][question]: value
I am trying to set that state from a component called SectionHeader. These contain the buttons.
SectionHeader.js
import { FormAnswersContext, FormQuestionsContext } from "../../Store";
function SectionHeader({ title, name }) {
const [formAnswers, setFormAnswers] = useContext(FormAnswersContext);
const [formQuestions, setFormQuestions] = useContext(FormQuestionsContext);
return (
<div>
<h1 className={styles["Header"]}>{title}</h1>
<div className={styles["MarkAllWrapper"]}>
<button className={styles["MarkAll"]}>
Mark all items as "Good" in this section
</button>
<br />
<button className={styles["MarkAll"]}>
Mark all items as "N/A" in this section
</button>
</div>
</div>
);
}
The parent of Section Header and the rest of the form code excluding the child radio buttons which I have explained, are in another component LivingRoom.js
LivingRoom.js
import { FormQuestionsContext, FormAnswersContext } from "../../Store";
function LivingRoomForm({ Name }) {
const [expanded, setExpanded] = useState(false);
const [formQuestions, setFormQuestions] = useContext(FormQuestionsContext);
const [formAnswers, setFormAnswers] = useContext(FormAnswersContext);
const array = formQuestions.living;
const onChange = (e, name) => {
const { value } = e.target;
setFormAnswers((state) => ({
...state,
[Name]: { ...state[Name], [name]: value },
}));
};
const handleOpen = () => {
setExpanded(!expanded);
};
return (
<div>
<Button
className={styles["CollapseBtn"]}
onClick={handleOpen}
style={{ marginBottom: "1rem", width: "100%" }}
>
<p>LIVING ROOM INSPECTION</p>
<FontAwesome
className="super-crazy-colors"
name="angle-up"
rotate={expanded ? null : 180}
size="lg"
style={{
marginTop: "5px",
textShadow: "0 1px 0 rgba(0, 0, 0, 0.1)",
}}
/>
</Button>
<Collapse className={styles["Collapse"]} isOpen={expanded}>
<Card>
<CardBody>
{array ? (
<div>
<SectionHeader title="Living Room Inspection" name={Name} />
<div
className={styles["LivingRoomFormWrapper"]}
id="living-room-form"
>
{array.map((question, index) => {
const selected =
formAnswers[Name] && formAnswers[Name][question]
? formAnswers[Name][question]
: "";
return (
<div className={styles["CheckboxWrapper"]} key={index}>
<h5>{question}</h5>
<Ratings
section={Name}
question={question}
onChange={onChange}
selected={selected}
/>
</div>
);
})}
</div>
<br />
<ImageUploader name="living" title={"Living Room"} />
</div>
) : (
<div></div>
)}
</CardBody>
</Card>
</Collapse>
</div>
);
}
If there is anything I am missing please let me know, I would be happy to share it. Cheers
Edit: for anyone that needs the radio buttons component.
Ratings.js
import React from "react";
import { FormGroup, CustomInput } from "reactstrap";
function Ratings({ selected, section, question, onChange }) {
return (
<div>
<FormGroup>
<div>
<CustomInput
checked={selected === "Good"}
onChange={(e) => onChange(e, question)}
type="radio"
id={`${section}_${question}_Good`}
value="Good"
label="Good"
/>
<CustomInput
checked={selected === "Fair"}
onChange={(e) => onChange(e, question)}
type="radio"
id={`${section}_${question}_Fair`}
value="Fair"
label="Fair"
/>
<CustomInput
checked={selected === "Poor"}
onChange={(e) => onChange(e, question)}
type="radio"
id={`${section}_${question}_Poor`}
value="Poor"
label="Poor"
/>
<CustomInput
checked={selected === "N/A"}
onChange={(e) => onChange(e, question)}
type="radio"
id={`${section}_${question}_NA`}
value="N/A"
label="N/A"
/>
</div>
</FormGroup>
</div>
);
}
I do not completely understand your question, I am sorry but I think this will help you.
Here is an implementation of radio buttons using react -
class App extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
handleChange = e => {
const { name, value } = e.target;
this.setState({
[name]: value
});
};
render() {
return (
<div className="radio-buttons">
Windows
<input
id="windows"
value="windows"
name="platform"
type="radio"
onChange={this.handleChange}
/>
Mac
<input
id="mac"
value="mac"
name="platform"
type="radio"
onChange={this.handleChange}
/>
Linux
<input
id="linux"
value="linux"
name="platform"
type="radio"
onChange={this.handleChange}
/>
</div>
);
}
}
After a few attempts, I was able to figure out the solution to this issue.
The key here was to figure out a way to get gather each question so that it may be used as a key when setting the state. As my questions were stored in a ContextAPI, I was able to pull them out like so...
this may not be the best solution however it worked for me.
const setStateGood = () => {
formQuestions[name].map((question) => {
setFormAnswers((state) => ({
...state,
[name]: { ...state[name], [question]: "Good" },
}));
});
};
const setStateNA = () => {
formQuestions[name].map((question) => {
setFormAnswers((state) => ({
...state,
[name]: { ...state[name], [question]: "N/A" },
}));
});
};
I was able to map through each question since the name is being passed through props is a key inside the actual object, formQuestions[name]. Because i'm mapping through each one I can set that question as a key and return the new state for each question to whatever I would like.
However, if I was to create an onClick={setState('Good')}, React didn't like that and it created an infinite loop. I will look for more solutions and update this post if I find one.
I already built the form in React and it shows the input fields in red borders that'll change to regular borders once someone types it in. I used this example from this React form article link So everything is working except I wanted to add the error message under the input field that displays "Please fill in the blank field" that will disappear once someone starts typing in the field. How do I do this?
Here's my code in Form.js:
import React, { Component } from 'react';
import FormField from './FormFieldBox';
function validate(name, isin) {
// true means invalid, so our conditions got reversed
return {
name: name.length === 0,
isin: isin.length === 0
};
}
export default class PopupForm extends Component {
constructor(props) {
super(props)
this.state = {
name: '',
isin: '',
country: '',
errormessage: ''
}
}
updateInput = (e) =>{
this.setState({[e.target.name]: e.target.value})
}
closePopupSubmit = (e) => {
if (!this.canBeSubmitted()) {
e.preventDefault();
}
let security = { //1.gather security data from form submit
name: this.state.name,
isin: this.state.isin,
country: this.state.country
}
this.props.submitPopup(security); //2.closePopup function, add security data
}
canBeSubmitted() {
const errors = validate(this.state.name, this.state.isin);
const isDisabled = Object.keys(errors).some(x => errors[x]);
return !isDisabled;
}
cancelPopupSubmit = (e) => {
e.preventDefault()
this.props.cancelPopup();
}
render() {
const errors = validate(this.state.name, this.state.isin);
const isDisabled = Object.keys(errors).some(x => errors[x]);
return (
<div className='popup'>
<div className='popup-inner'>
<form onSubmit={this.closePopupSubmit}>
<FormField onChange={this.updateInput} className={errors.name ? "input error" : "input"} label="Name" type="text" name="name" value={this.state.name} />
<FormField onChange={this.updateInput} className={errors.isin ? "input error" : "input"} label="ISIN" type="text" name="isin" value={this.state.isin} />
<FormField onChange={this.updateInput} label="Country" type="text" name="country" value={this.state.country} />
<button type="button" onClick={this.cancelPopupSubmit} className="button">Cancel</button>
<button type="submit" className="button" disabled={isDisabled}>Submit</button>
</form>
</div>
</div>
)
}
}
And my component FormField.js
import React from "react";
const FormBox = props => {
return (
<div className="field">
<label className="label">{props.label}</label>
<div className="control">
<input onChange={props.onChange}
className={props.className}
type={props.type}
name={props.name}
value={props.value}
placeholder={props.placeholder} />
{/* {props.errormessage} */}
</div>
</div>
)
}
export default FormBox;
const FormBox = props => {
return (
<div className="field">
<label className="label">{props.label}</label>
<div className="control">
<input onChange={props.onChange}
className={props.className}
type={props.type}
name={props.name}
value={props.value}
placeholder={props.placeholder} />
</div>
{Boolean(props.value.length) || (
<div className="err-msg">
Please fill in the blank field
</div>
)}
</div>
)
}
There are two ways you can achieve this
First : oninvalid attribute in HTML5 and calling a custom function on that.
Second : along with each element name object in state have a length attribute. In validation function you can check for the length and throw a custom error that you want to display.
I'm creating an intake form where a piece of data can be input into the text field, or generated randomly with a button next to the field.
I want to do this for 3 fields on the form so I created a component
called <RandomDataButton />
I'm stuck with how to make sure the results of the calculation done by the button component update the value of the text box so that the form submission contains the generated data.
I don't fully understand the state propagation, but what I do understand is that the flow is one way, down the hierarchy of components.
So what I am attempting to do is have a choice of inputting some data in the text box, or generating some random data from a button (I'd like to reuse it in other ui creations)
Where I am stuck is how do I update the input field from the componenet that is lower in the hierarchy.
Do I pass the state to the randomizer button and then have it update a copy of state? Or am I totally off base with that approach?
App:
class App extends React.Component {
render(){
return (
<div>
<DataInputForm />
</div>
);
}
}
DataInputForm:
class DataInputForm extends React.Component{
state= {
projectname: '',
datasource: '',
data1: '',
data2: '',
data3: '',
};
handleSubmit = e => {
e.preventDefault();
console.log({
projectname: this.projectname.value,
datasource: this.datasource.value,
data1: this.data1.value,
data2: this.data2.value,
data3: this.data3.value,
});
}
handleChange = e => this.setState({[e.target.name]: e.target.value});
render(){
return(
<form className="ui form" onSubmit={this.handleSubmit}>
<div className="field">
<label htmlFor="projectname">Project Name: </label>
<input
type="text"
id="projectname"
name="projectname"
placeholder="Project Name"
ref={input => this.projectname = input}
/>
</div>
<div className="field">
<label htmlFor="datasource">Data Source: </label>
<input
type="text"
id="datrasource"
name="datasource"
placeholder="Data Source"
ref={input => this.datasource = input}
/>
</div>
<div className="field">
<label htmlFor="data1">Data 1: </label>
<input
type="number"
min="3"
max="18"
id="data1"
name="data1"
ref={input => this.data1 = input}
/>
<RandomDataButton buttonid={"data1button"} buttonname={"Data1"} />
</div>
<div className="field">
<label htmlFor="data2">Data 2: </label>
<input
type="number"
min="3"
max="18"
id="data2"
name="data2"
ref={input => this.data2 = input}
/>
<RandomDataButton buttonid={"data2button"} buttonname={"Data2"} />
</div>
<div className="field">
<label htmlFor="data3">Data 3: </label>
<input
type="number"
min="3"
max="18"
id="data3"
name="data3"
ref={input => this.data3 = input}
/>
<RandomDataButton buttonid={"data3button"} buttonname={"Data3"} />
</div>
<button className="ui button" type="submit">Create Data</button>
</form>
);
}
}
RandomDataButton:
const getRandom = max => Math.floor(Math.random() * Math.floor(max));
class RandomDataButton extends React.Component {
generateData(value){
var result, destination;
destination = value.toLowerCase();
result = getRandom(1000);
console.log("Generated " + result + " for range of " + value + "]: " + destination);
//this.state.{destination};
}
render(){
return(
<button id={this.props.buttonid} type="button" onClick={this.generateData.bind(null,this.props.buttonname)}>{this.props.buttonname}</button>
//<button id="strbutton" type="button">Generate</button>
);
}
}
Pass a function as prop to RandomDataButton. Define the function in DataInputForm and use it update the state in DataInputForm by calling the prop function from RandomDataButton whenever you need the update.
It seems you are working with multiple components, so while working with multiple components, it is highly recommended to use any central storage container, which would be useful to get your desired data in any components
Flux and redux both are tested architectures for data state management, you could use any of them, I would recommend using redux.
Here's a codesandbox for your reference: https://codesandbox.io/s/bold-frog-01ff2
This is effectively a continuation of Amala's suggestion.
You are correct, the hierarchy is one-way. Which means we should define a function in DataInputForm (lvl2) and pass it as a prop to RandomDataButton (lvl3). That function is bound to DataInputForm's execution context, and we want to update it's state so we can feed the new data back into each individual input.
For example:
createRandomText = (associatedField, value) => {
this.setState(
{
[associatedField]: value
},
() => console.log(this.state)
);
};
So to update the state correctly, we need to provide a field corresponding to the right input, and a value (the randomized value).
We pass in that function as a prop to RandomDataButton and use it for the onClick() handler.
class RandomDataButton extends React.Component {
generateData = () => {
let result = getRandom(1000);
this.props.createRandomText(this.props.matchingInput, result);
};
render() {
return (
<button
id={this.props.buttonid}
type="button"
onClick={this.generateData}
>
{this.props.buttonname}
</button>
//<button id="strbutton" type="button">Generate</button>
);
}
}
Additionally we need to provide another prop to the button component so we can call the above function correctly:
<RandomDataButton
buttonid={"data1button"}
buttonname={"Data1"}
createRandomText={this.createRandomText}
matchingInput={"data1"}
/>
See sandbox for full details :)
I´m trying to change one value of my parent. My parent container send the data to PersonalComponent and PersonalComponent send the data to my AvatarComponent what is the component where I have my input
I receive the props in my PersonalComponent that's I know, but when I send the data to AvatarComponent, I think I can change the data of my container that's the parent of PersonalComponent
This is the parent of all:
class HandlePersonal extends Component {
state = {
...this.props.user
};
handleUsername = e => {
e.preventDefault();
this.props.dispatch({
type: "CHANGE_USERNAME",
payload: this.state.displayName
});
};
handleChange = e => {
this.setState({ ...this.state, [e.target.name]: e.target.value });
alert(2);
};
render() {
return (
<PersonalComponent
handleUsername={this.handleUsername}
handleChange={this.handleChange}
data={this.state}
/>
);
}
}
This is my PersonalComponent
const PersonalComponent = props => {
return (
<div id="personal">
<h2>My profile</h2>
<Card>
<div id="personal-container">
<div id="cover">
<CoverComponent />
</div>
<div id="avatar">
<AvatarComponent
data={props.data.photoURL}
onChange={props.handleChange}
/>
</div>
<div id="user-name">
<form onSubmit={props.handleUsername}>
<InputText
name="displayName"
value={props.data.displayName}
onChange={props.handleChange}
/>
<Button id="save-username" label="Ok" />
</form>
</div>
</div>
</Card>
</div>
);
};
I know that it's working because I had alert(props.data) inside PersonalComponent
And then, this is my AvatarComponent
<h1>Value: {props.data}</h1>
<input name="photoURL" value={props.data} onChange={props.handleChange} />
And here, if I alert(props.data) I receive null that's the actual value of photoURL in the HandlePersonal but I can't change the value using my input. Why?
I want to make that when i write in my input inside AvatarComponent the value of HandleContainer change
In short:
HandlePersonal.handleChange() needs to call this.setState('photoUrl': 'input value') to update the state.
Lemme knwo if you need detailed answer..im on my phone so not so convenient.
EDIT
<input name="photoURL" value={props.data} onChange={props.onChange} />
Because u pass it as onChange