I have Here React JS Code. with 2 input areas. and 2 output areas. How to link 1 to 1 and 2nd to 2nd.
class MarkdownEditor extends React.Component {
constructor(props) {
super(props);
this.md = new Remarkable();
this.handleChange = this.handleChange.bind(this);
this.state = { value: 'Hello, **world**!' };
this.md2 = new Remarkable();
this.handleChange2 = this.handleChange2.bind(this);
this.state2 = { value: 'Hello, **Brad Pitt**!' };
}
handleChange(e) {
this.setState({ value: e.target.value });
}
handleChange2(e) {
this.setState({ value: e.target.value });
}
getRawMarkup() {
return { __html: this.md.render(this.state.value) };
}
getRawMarkup2() {
return { __html: this.md2.render(this.state2.value) };
}
render() {
return (
<div className="MarkdownEditor">
<h3>Input</h3>
<label htmlFor="markdown-content">
Enter some markdown
</label>
<textarea
id="markdown-content"
onChange={this.handleChange}
defaultValue={this.state.value}
/>
<textarea id="markdown-content2"
onChange={this.handleChange2}
defaultValue={this.state2.value2}
/>
<h3>Output</h3>
<div
className="content"
dangerouslySetInnerHTML={this.getRawMarkup()}
/>
<div
className="content"
dangerouslySetInnerHTML={this.getRawMarkup2()}
/>
</div>
);
}
}
ReactDOM.render(<MarkdownEditor />,
document.getElementById('markdown-example')
);
Is it "Value" that can be changed or "state"? Here only Text area input is used but I do want to use other inputs such as radio, numbers and checkboxes. Moreover Where do I perfrom Arithmatic calculations in code above
You can only use one this.state in your component. It's a javascript object so you can have your two keys inside it:
this.state = { input1: "", input2: "" }
Then, to update the values, you can call this.setState for each key:
handleChange(e) {
this.setState({ input1: e.target.value });
}
handleChange2(e) {
this.setState({ input2: e.target.value });
}
this.setState will update the state by merging the previous state with the object you send him ({input1: e.target.value}) that will cause a rerender of you component with the updated value.
Now in your <textearea>, you must use "value" instead of "defaultValue" and read the value directly from the state i.e. {this.state.input1} for the former and {this.state.input2} for the latter.
This is what we call a controlled component in React.
<input value = {this.state.formValue}>
And you can get or set input's value in other functions of component
Related
I am using localStorage to store the form details. When the component is mounted I am getting the data in the console. How can I show the data in the form and edit it? I've set the edited state but I am not getting how can I achieve this so that the values will be prefilled?
Here's the code :
class MileStoneForm extends Component {
constructor(props){
super(props)
this.state={
deliverable_name:"",
due_date:"",
deliverable_notes:"",
milestone_based_payment:false,
deliverable_name_error:"",
due_date_error:"",
deliverable_notes_error:"",
percent_rate:0,
percent_rate_error:"",
due_date_select:false,
edit:false,
milestonedata:null;
}
}
componentDidMount(){
let milestonedata=JSON.parse(localStorage.getItem('mileStoneData'))
console.log(milestonedata)
if(this.state.edit===true){
this.setState({
milestonedata:milestonedata
},()=>{this.setEditMileStoneData()})
}
}
setEditMileStoneData=()=>{
const {milestonedata}=this.state
let data={
deliverable_name:milestonedata.milestoneName,
deliverable_notes:milestonedata.description,
due_date:milestonedata.dueDate,
milestone_based_payment:milestonedata.isMilestoneBasedPayment,
percent_rate:milestonedata.percentageRate
}
this.setState({...data})
}
handleSubmit=()=>{
const {deliverable_name,deliverable_name_error,deliverable_notes,deliverable_notes_error,
due_date,due_date_error,milestone_based_payment,percent_rate}=this.state
let pass=true
if(pass){
let data={
description: deliverable_notes,
dueDate: due_date,
isDeleted: false,
isMilestoneBasedPayment: milestone_based_payment,
milestoneName: deliverable_name,
percentageRate: percent_rate,
}
console.log(data)
this.props.handleData(data)
localStorage.setItem('mileStoneData',JSON.stringify(data))
this.setState({
deliverable_name:'',
deliverable_name_error:'',
deliverable_notes:'',
deliverable_notes_error:'',
milestone_based_payment:false,
percent_rate:'',
due_date:'',
due_date_error:''
})
}
}
export default MileStoneForm
You should take a look at the "controlled components" section from React docs.
The thought behind it is:
You create a state object for your form.
You add the state for each form field as its value
You add a function to onChange that updates the satate.
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
Controlled component is the way to go here. I usually like to do it this way.
handleChange = (e) => {
// This way when we a change is made within the field this function is called and the name from the input will be changed with the new value provided
this.setState({ [e.target.name] : e.target.value })
}
render(){
<TextInput value = { this.state.NAME_THIS_CORRELATING_TO_THE_KEY_IN_STATE } name = "NAME_THIS_CORRELATING_TO_THE_KEY_IN_STATE" onChange = { this.handleChange }
}
We then do this for all of our components
I am new to react. I am trying to build dynamic form component of a set of input fields using react which can be reused in any single form as a set of inputs. How can I access those input data dynamically?
For example, the form for the set of reusable inputs:
export default class dynamicForm extends Component {
handleSubmit = event => {
// get dynamic input data. But how?
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<CustomInputs1/>
<CustomInputs2/>
<input type="submit" value="confirm"/>
</form>
)}
Input set no. 1 as custom input:
export default class CustomInputs1 extends Component {
constructor(props) {
super(props);
this.state = {
input1: "",
input2: ""
};
this.inputInput1Ref = React.createRef();
this.inputInput2Ref = React.createRef();
}
handleInputChange = event => {
event.preventDefault();
this.setState({
[event.target.name]: event.target.value
});
};
render() {
const { input1 } = this.state;
const { input2 } = this.state;
return (
<div>
<input type="text" name="input1" value={input1} onChange={this.handleInputChange}/>
<input type="text" name="input2" value={input2} onChange={this.handleInputChange}/>
</div>
)
}
}
Input set no. 2 as custom input:
export default class CustomInputs2 extends Component {
constructor(props) {
super(props);
this.state = {
input3: "",
input4: ""
};
this.inputInput3Ref = React.createRef();
this.inputInput4Ref = React.createRef();
}
handleInputChange = event => {
event.preventDefault();
this.setState({
[event.target.name]: event.target.value
});
};
render() {
const { input3 } = this.state;
const { input4 } = this.state;
return (
<div>
<input type="text" name="input3" value={input3} onChange={this.handleInputChange}/>
<input type="text" name="input4" value={input4} onChange={this.handleInputChange}/>
</div>
)
}
}
There can be more reusable input groups like this.I just added two group as example.
A few things: You are not using the refs you are creating in the CustomInputs, so those lines can go. Also, you can destructure the state in one line, like so: const {input1, input2} = this.state.
Now for your problem: You can either create refs to the CustomInputs in your dynamicForm and simply access the state of your custom inputs when the form is submitted or pass a function as props to each CustomInputs which writes the input to the state of dynamicForm. First option is probably easier ;)
I've created form in ReactJS. I am fetching value in common method in {key : value} paired. but I am getting previous value in method.
constructor(props) {
super(props);
{
this.state = { name: "", age: 0 };
}
}
inputChange = (key, value) => {
this.setState({ [key] : value });
console.log(this.state);
}
render() {
return (
<form>
<div>
Name : <input type="text" name="name" onKeyUp={(e) => this.inputChange('name', e.target.value)}></input>
</div>
<div>
Age : <input type="text" name="age" onKeyUp={(e) => this.inputChange('age', e.target.value)}></input>
</div>
</form>
)
}
I've attached the screenshot for better understanding.
setState enqueues a change to the state, but it doesn't happen immediately. If you need to do something after the state has changed, you can pass a second callback argument to setState:
inputChange = (key, value) => {
this.setState({ [key] : value }, () => {
console.log(this.state);
});
}
This will do exactly what you need.
class App extends Component {
constructor(props){
super(props)
this.state = {
name: '',
age: ''
}
}
handleInput(option, event){
if (option === 'name') {
this.setState({
name: event.target.value
}, () => {
console.log("Name: ", this.state.name)
});
}
else if (option === 'age'){
this.setState({
age: event.target.value
}, () => {
console.log("Age: ", this.state.age)
});
}
}
render() {
return (
<div>
<div>
<header>Name: </header>
<input type="text" onChange={this.handleInput.bind(this, 'name')}/>
<header>Age: </header>
<input type="text" onChange={this.handleInput.bind(this, 'age')}/>
</div>
</div>
);
}
}
update 2022
with functional components using useState hook this does not work longer, you have to use it in useEffect to update after rendering.
State updates from the useState() and useReducer() Hooks don't support the second callback argument. To execute a side effect after rendering, declare it in the component body with useEffect().
I have a little problem. In my function isLoginOk() it never enters into the if. It enters every time in the else even if my string is longer than 1 char.
When I console.log before the if I can see that my string is longer than 1 yet it doesn't go into the if!
Why is that? (I have verified that the string in my component state is correctly updated by handleChange())
export default class InputLogin extends React.Component {
constructor(props) {
super(props);
this.state = {
login: "",
}
};
handleChange = (e) => {
this.setState({
[e.target.name]: [e.target.value]
});
}
isLoginOk() {
if (this.state.login.length > 1) {
return (<div>OK</div>);
} else {
return (<div>ERROR</div>);
}
}
render() {
return (
<div>
<input type="text" name="login" value={this.state.login} onChange={this.handleChange} /> {this.isLoginOk()}
</div>
);
}
}
It looks like an inconsitency with the way you're setting the component state in handleChange() versus the way isLoginOk() expects the state to be defined.
In the handleChange() function, the "square bracket" syntax [e.target.value] means that you're setting the state value for key [e.target.name] as an array:
this.setState({
// Brackets around [e.target.value] creates array value
[e.target.name]: [e.target.value]
});
The isLoginOk() function however, expects the value to be a string rather than an array:
isLoginOk() {
// You're accessing the string directly and not as an array item
if (this.state.login.length > 1) {
return (<div>OK</div>);
} else {
return (<div>ERROR</div>);
}
}
Consider revising your handleChange() function like so to ensure that the state is updated in a way that is compatible with isLoginOK():
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value // Remove the [ and ] around value
});
}
There is a bug in your code, in your handleChange value.
[e.target.name]: [e.target.value]
You're setting the value of this.state.login to ['some input']. Your validation always fails because you can't get the length of the string inside the array using this.state.login.length. Your validation will return either 0 or 1 based on the array length, certainly not what you intended.
To fix this, remove the brackets around [e.target.value].
class InputLogin extends React.Component {
constructor(props) {
super(props)
this.state = {
login: ""
}
}
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value
});
}
render() {
const { login } = this.state;
const loginResult = login.length > 1 ? "OK" : "ERROR";
return (
<div>
<input type="text" name="login" value={login} onChange={this.handleChange} />
{loginResult}
</div>
)
}
}
ReactDOM.render(<InputLogin />, document.querySelector("#app"))
body {
background: #20262E;
padding: 20px;
font-family: Helvetica;
color: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<div id="app"></div>
http://jsfiddle.net/n5u2wwjg/261779/
Remove the brackets around e.target.value
Also simplify the render and get rid of the isLoginOk function. Seems unnecessary.
<input type="text" name="login" value={this.state.login} onChange={this.handleChange} />
<div>{ this.state.login.length > 1 ? 'OK' : 'ERROR' }</div>
I think I know what I need to do to make my searchLocationChange function work, but I'm not sure quite how to do it. Please forgive the indentation, was grappling a fair bit with StackOverflow's WYSIWYG!
Here's my Parent component setup:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
forecasts: [],
location: {
city: '',
country: '',
},
selectedDate: 0,
searchText: '',
};
this.handleForecastSelect = this.handleForecastSelect.bind(this);
this.searchLocationChange = this.searchLocationChange.bind(this);
}
}
With this specific function I want to make work:
searchLocationChange() {
console.log(this.state.searchText);
Axios.get('https://mcr-codes-weather.herokuapp.com/forecast', {
params: {
city: this.state.searchText,
},
})
.then((response) => {
this.setState({
forecasts: response.data.forecasts,
location: {
city: response.data.location.city,
country: response.data.location.country,
}
});
});
}
And in my Child component, the logic is:
class SearchForm extends React.Component {
constructor(props) {
super(props);
this.state = {
searchText: '',
};
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const enteredText = event.target.value;
this.setState({
searchText: enteredText
});
}
render() {
return (
<span className="search-form">
<div className="search-form__input"><input type="text" value={this.state.searchText} onChange={this.handleInputChange} /></div>
<div className="search-form__submit"><button onClick={this.props.searchLocationChange}>Search</button></div>
</span>
);
}
}
I realise I'm trying to update the Parent component with searchText from the state of the Child component, but I can't figure out what I need to change to make this work. I have a sneaking suspicion I'm 99% of the way there, and it's only a few more lines I need, but I could be way off?
You're already passing down searchLocationChange from your parent.
in parent component:
searchLocationChange(searchedText) {
console.log(searchText);
Axios.get('https://mcr-codes-weather.herokuapp.com/forecast', {
params: {
city: searchText,
},
})
.then((response) => {
this.setState({
forecasts: response.data.forecasts,
location: {
city: response.data.location.city,
country: response.data.location.country,
},
});
});
}
in child:
render() {
const { searchText } = this.state;
return (
<span className="search-form">
<div className="search-form__input"><input type="text" value={this.state.searchText} onChange={this.handleInputChange} /></div>
<div className="search-form__submit"><button onClick={()=>{this.props.searchLocationChange(searchText)}}>Search</button></div>
</span>
);
}
You should call the function like that this.props.searchLocationChange(this.state.searchText)
You can do something like below
<div className="search-form__submit"><button onClick={() => {this.props.searchLocationChange(this.state.searchText)}}>Search</button></div>
and function definition should be
searchLocationChange(searchText) {
You are mixing controlled and uncontrolled. Either do controlled or uncontrolled. So take the search Text from parent only. Above solutiion is one way of doing this . Another way is to pass searchTxt from parent to child.
<SearchForm
searchTxt={this.state.searchTxt}
handleInputChange={this.handleInputChange}
searchLocationChange={this. searchLocationChange}
/>
Move your handleInputChange in parent:
handleInputChange = (event) => {
const enteredText = event.target.value;
this.setState({ searchText: enteredText });
}
Then change your child component respective line to
<div className="search-form__input"><input type="text" value={this.props.searchText} onChange={this.props.handleInputChange} /></div>
Now when you try the above code it should work. Now you are keeping your searchTxt in the parent component. your SearchForm component is Completely controlled now.