I've tried to add new fields here, each time button '+' is clicked it adds field input. It is working fine but the problem I've stuck upon is I want to add the value of those input fields. As I am new to react I am not finding a way to achieve it. Is it possible to achieve this.
class InputFields extends React.Component{
render(){
return (
<div>
<input name={`value[${this.props.index + 1}]`} onChange={this.onChangeValue} />
</div>
);
}
}
class Main extends React.Component{
constructor(props){
super(props);
this.values = props.value;
this.state={
inputs:[],
values:[]
};
this.addInputs = this.addInputs.bind(this);
}
addInputs() {
const inputs = this.state.inputs.concat(InputFields);
this.setState({ inputs });
}
onChangeValue(e){
var value = e.target.value;
this.value =value;
this.addValues();
}
addValues(){
...
}
render () {
const inputs = this.state.inputs.map((Element, index) => {
return <Element key={ index } index={ index } />
});
return <div>
<div>
<input name={`value[${this.props.index}]`} onChange={this.onChangeValue} />
</div>
<div>
<button className="btn btn-sm btn-primary" onClick={this.addInputs}>+</button>
</div>
</div>
}
}
ReactDOM.render(
<Main />,
document.getElementById('calculator')
);
It't not required to manage two different variables to manage it, you can loop it through the array and can change the value on onChange event
import React from "react";
export default class Example extends React.Component {
state = {
values: [null]
};
add() {
this.setState(prevState => prevState.values.push(null));
}
changeVal(val, index) {
this.setState(prevState => (prevState.values[index] = parseFloat(val)));
}
getSum() {
let sum = 0;
for (let i = 0; i < this.state.values.length; i++) {
if (this.state.values[i] !== null) {
sum += this.state.values[i];
}
}
return sum;
}
render() {
return (
<div>
{this.state.values.map((val, index) => (
<div key={index}>
<input
onChange={e => this.changeVal(e.target.value, index)}
type="number"
value={val}
placeholder="Enter a value"
/>
</div>
))}
<hr />
Sum is {this.getSum()}
<hr />
<button onClick={this.add.bind(this)}> +Add</button>
</div>
);
}
}
Edit
Added the method getSum() and converting the number to float in changeVal() method
Codesandbox link - https://codesandbox.io/s/235o6xoxyr
In react, the view is a function of data: UI = fn(data)
This means that you'll write a component tree that can transform data to view.
And only manipulate data, unlike what you might be familiar with in jQuery for example.
In practice, this would mean:
let a component Input handle the rendering of one input:
const Input = ({value, onChange}) =>
<input value={value} onChange={event => event.target.value} />
in your state, only save the list of input values as strings. React will take care of transforming each value to an Input component later on
this.state = {inputs: []}
write a function responsible of adding new inputs, to the state and not the view
addInput = () => {
this.setState({
inputs: this.state.inputs.concat('')
})
}
create a method that takes care of changing the value of one input in the list
changeValue = (i, newValue) => {
this.setState({
inputs: [
...this.state.inputs.slice(0, i),
newValue,
...this.state.inputs.slice(i+1)]
})
}
in the render method of Main, loop over your list of inputs and transform to a list of Input components
this.state.inputs.map(
(input, i) =>
<Input
key={i}
value={input}
onChange={newValue => this.changeValue(i, newValue)}
/>
)
Related
I am trying to build a simple ui so that I can learn react.
right now when I click an add button it will show I am here div and delete button
when I click add button multiple times it should show I am here div with delete button with multiple times.
so I research and found this example https://www.skptricks.com/2018/06/append-or-prepend-html-using-reactjs.html
using this example I implemented the appendData method but still its not adding the div multiple times.
in my console I am able to see how many times divs are added console.log("this.displayData---->", this.displayData);
can you tell me how to fix it.
so that in future I will fix it myself
https://stackblitz.com/edit/react-b2d3rb?file=demo.js
onClick = () => {
this.setState({ showResults: true });
this.setState({ showOrHideSearch: true });
this.displayData.push(
<div style={{ display: this.state.showOrHideSearch ? "" : "none" }}>
{" "}
I am here
<input
ref="rbc-toolbar-label"
type="submit"
value="Delete"
onClick={this.onDelete}
/>
</div>
);
console.log("this.displayData---->", this.displayData);
this.setState({ showdata: this.displayData });
};
First thing is you should not use this.setState multiple times, instead you should do them in one line. And instead of pushing data into class variables, you should set that data into your state variable and the same variable you should use in your render function. It will be good if you can share your complete code..
import React, { Component } from 'react';
import Calendar from 'rc-calendar';
import DatePicker from 'rc-calendar/lib/Picker';
import 'rc-calendar/assets/index.css';
import moment from 'moment';
class CalendarPage extends Component {
constructor(props) {
super(props);
console.log("AsyncValidationForm this.props---->", this.props);
this.state = {
displayData: []
};
}
onClick = () => {
let displayData = [...this.state.displayData];
displayData.push( { text: 'I am here' });
this.setState({ displayData: displayData });
};
onDelete = index => {
let displayData = [...this.state.displayData];
if(index > -1){
displayData.splice(index, 1);
}
this.setState({ displayData: displayData });
};
handleChange = name => event => {
const value = event.target.value;
this.setState(
{
[name]: value,
pristine: false
},
() => {
this.props.handleChange(name, value); //setState username, password of VerticalLinearStepper.js
}
);
};
onSubmit(event) {
event.preventDefault();
var newItemValue = this.refs.itemName.value;
if(newItemValue) {
this.props.addItem({newItemValue});
this.refs.form.reset();
}
}
render() {
const { handleSubmit, pristine, reset, submitting } = this.props;
let { displayData} = this.state;
return (
<form onSubmit={handleSubmit}>
<div>
<input type="submit" value="add" onClick={this.onClick} />
{displayData.length > 0 ? displayData.map(function(data, index) {
return (
<div key={index}>
{data.text} - For testing added index on screen {index}
<input
ref="rbc-toolbar-label"
type="submit"
value="Delete"
onClick={() => this.onDelete(index)}
/>
</div>
)}, this) : null}
</div>
</form>
);
}
}
export default CalendarPage;
Im making my first react project. Im new in JS, HTML, CSS and even web app programming.
What i want to do it is a Search input label. Now its look like this:
Like you can see i have some list of objects and text input.
I Have two components, my ProjectList.js with Search.js component...
class ProjectsList extends Component {
render() {
return (
<div>
<Search projects={this.props.projects} />
<ListGroup>
{this.props.projects.map(project => {
return <Project project={project} key={project.id} />;
})}
</ListGroup>
</div>
);
}
}
export default ProjectsList;
... and ProjectList.js displays Project.js:
How looks Search.js (its not ended component)
class Search extends Component {
state = {
query: ""
};
handleInputChange = () => {
this.setState({
query: this.search.value
});
};
render() {
return (
<form>
<input
ref={input => (this.search = input)}
onChange={this.handleInputChange}
/>
<p />
</form>
);
}
}
export default Search;
My project have name property. Could you tell me how to code Search.js component poperly, to change displaying projects dynamically based on input in text label? for example, return Project only, if text from input match (i want to search it dynamically, when i start typing m... it shows all projects started on m etc).
How to make that Search input properly? How to make it to be universal, for example to Search in another list of objects? And how to get input from Search back to Parent component?
For now, in react dev tools whatever i type there i get length: 0
Thanks for any advices!
EDIT:
If needed, my Project.js component:
class Project extends Component {
state = {
showDetails: false
};
constructor(props) {
super(props);
this.state = {
showDetails: false
};
}
toggleShowProjects = () => {
this.setState(prevState => ({
showDetails: !prevState.showDetails
}));
};
render() {
return (
<ButtonToolbar>
<ListGroupItem className="spread">
{this.props.project.name}
</ListGroupItem>
<Button onClick={this.toggleShowProjects} bsStyle="primary">
Details
</Button>
{this.state.showDetails && (
<ProjectDetails project={this.props.project} />
)}
</ButtonToolbar>
);
}
}
export default Project;
To create a "generic" search box, perhaps you could do something like the following:
class Search extends React.Component {
componentDidMount() {
const { projects, filterProject, onUpdateProjects } = this.props;
onUpdateProjects(projects);
}
handleInputChange = (event) => {
const query = event.currentTarget.value;
const { projects, filterProject, onUpdateProjects } = this.props;
const filteredProjects = projects.filter(project => !query || filterProject(query, project));
onUpdateProjects(filteredProjects);
};
render() {
return (
<form>
<input onChange={this.handleInputChange} />
</form>
);
}
}
This revised version of Search takes some additional props which allows it to be reused as required. In addition to the projects prop, you also pass filterProject and onUpdateProjects callbacks which are provided by calling code. The filterProject callback allows you to provide custom filtering logic for each <Search/> component rendered. The onUpdateProjects callback basically returns the "filtered list" of projects, suitable for rendering in the parent component (ie <ProjectList/>).
The only other significant change here is the addition of visibleProjects to the state of <ProjectList/> which tracks the visible (ie filtered) projects from the original list of projects passed to <ProjectList/>:
class Project extends React.Component {
render() {
return (
<div>{ this.props.project }</div>
);
}
}
class ProjectsList extends React.Component {
componentWillMount() {
this.setState({ visibleProjects : [] })
}
render() {
return (
<div>
<Search projects={this.props.projects} filterProject={ (query,project) => (project == query) } onUpdateProjects={ projects => this.setState({ visibleProjects : projects }) } />
<div>
{this.state.visibleProjects.map(project => {
return <Project project={project} key={project.id} />;
})}
</div>
</div>
);
}
}
class Search extends React.Component {
componentDidMount() {
const { projects, filterProject, onUpdateProjects } = this.props;
onUpdateProjects(projects);
}
handleInputChange = (event) => {
const query = event.currentTarget.value;
const { projects, filterProject, onUpdateProjects } = this.props;
const filteredProjects = projects.filter(project => !query || filterProject(query, project));
onUpdateProjects(filteredProjects);
};
render() {
return (
<form>
<input onChange={this.handleInputChange} />
</form>
);
}
}
ReactDOM.render(
<ProjectsList projects={[0,1,2,3]} />,
document.getElementById('react')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.0.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.0.0/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I will assumes both your Search and ProjectList component have a common parent that contains the list of your projects.
If so, you should pass a function into your Search component props, your Search component will then call this function when the user typed something in the search bar. This will help your parent element decide what your ProjectsLists needs to render :
handleInputChange = () => {
this.props.userSearchInput(this.search.value);
this.setState({
query: this.search.value
});
};
And now, here is what the parent element needs to include :
searchChanged = searchString => {
const filteredProjects = this.state.projects.filter(project => project.name.includes(searchString))
this.setState({ filteredProjects })
}
With this function, you will filter out the projects that includes the string the user typed in their names, you will then only need to put this array in your state and pass it to your ProjectsList component props
You can find the documentation of the String includes function here
You can now add this function to the props of your Search component when creating it :
<Search userSearchInput={searchChanged}/>
And pass the filtered array into your ProjectsList props :
<ProjectsList projects={this.state.filteredProjects}/>
Side note : Try to avoid using refs, the onCHnage function will send an "event" object to your function, containing everything about what the user typed :
handleInputChange = event => {
const { value } = event.target
this.props.userSearchInput(value);
this.setState({
query: value
});
};
You can now remove the ref from your code
I am learning reactJS and so I am trying my hands on an example. This example has a form textfield that can add an item to an existing array on click of a button. I am having errors here as when I enter a text and click on the button, the array list is not updated except I try to make changes to the text entered in the textfield. This is what I am doing:
import React, { Component } from 'react';
class App extends Component {
constructor(props){
super(props);
this.state = {
currentName : '',
arrays : ['john', 'james', 'timothy']
}
}
render() {
const showNames = this.state.arrays.map((thisName) => {
const values = <li>{thisName}</li>;
return values;
});
const getText = (e) => {
let value = e.target.value;
this.setState({
currentName : value
})
}
const addToUsers = () => {
this.state.arrays.push(this.state.currentName)
}
return (
<div>
<p>Add new name to List</p><br/>
<form>
<input type="text" onChange={getText}/>
<button type="button" onClick={addToUsers}>Add User</button>
</form>
<ul>
{showNames}
</ul>
</div>
);
}
}
export default App;
There are a host of things wrong with this, but your issue is likely that you need to use setState to modify state.
import React, { Component } from 'react';
class App extends Component {
constructor(){
super();
this.state = {
names: ['john', 'james', 'timothy']
}
}
addToUsers = () => {
this.setState(
prevState => ({
names: [...prevState.names, this.input.value]
})
)
}
render() {
const names = this.state.names.map(
(name, index) => <li key={index}>{name}</li>
)
return (
<div>
<p>Add new name to List</p><br/>
<form>
<input type="text" ref={e => this.input = e} />
<button type="button" onClick={this.addToUsers}>Add User</button>
</form>
<ul>
{names}
</ul>
</div>
)
}
}
export default App;
This quick edit changes a few things:
Uses setState for the addToUsers method
Eliminate onChange tracking and pull the name directly from the input when the button is clicked
Move the addToUsers method out to the component class rather than defining it on render
Rename this.state.arrays to this.state.names
Simplify conversion of this.state.names into list items
Set key on array elements (name list items)
Use prevState in setState to avoid race conditions
You need to make sure you update state using the setState method.
When you update arrays you are reaching into the state object and manipulating the data directly instead of using the method.
Instead try something like:
const addToUsers = () => {
const newArray = this.state.arrays.concat([this.state.currentName]);
this.setState({
arrays: newArray
});
}
You probably must add
onChange={getText}.bind(this)
to your functions.
Also change this
const addToUsers = () => {
this.state.arrays.push(this.state.currentName)
}
to this
const addToUsers = () => {
this.setState({here put your variable})
}
I'm trying to make a feature where a user can edit a submitted value. So to be completely clear:
You would enter some text
Click submit and that value will be pushed into an array
You will be able to see your value on the dom
If you made an error, you can click on that input and change the value, also updating the state of that value in the already pushed array.
On a button click, you will update the state and have a newly edited value.
I'm stuck on the part of changing the state of the value of the pushed items in the array.
For example:
If I were to click on the field of 'Bob', edit it and click submit, the value of whatever I changed it to would also change the state of what was originally in my array to the new value.
This is what I have so far:
import React, { Component } from 'react'
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
notes: ['hello', 'bob'],
val: ''
}
}
submit = () => {
const { notes, val } = this.state
notes.push(val)
this.setState({notes})
}
handleEdit = e => {
console.log(e)
}
render() {
return (
<div>
<input
type="text"
onChange={e => this.setState({val: e.target.value})}
/>
<button onClick={this.submit}>Submit</button>
{this.state.notes.map(item => {
return (
<form onSubmit={e => e.preventDefault()}>
<input
type="text"
defaultValue={item}
onChange={e => this.setState({val: e.target.value})}
/>
<button onClick={() => this.handleEdit(item)}>Submit
Change</button>
</form>
)
})}
</div>
)
}
}
Try this kind of thing :
handleEdit = (item) => {
const notes = this.state.notes.slice();
const index = notes.indexOf(item);
notes[index] = this.state.val;
this.setState({
notes
})
}
after spending a lot time I need help here.
I am creating a template. Where I can use different form fields. Everything is working. I am getting value form all fields as expected.
But problem is when I have 2 input fields together in one component and pass this component to parent component and get the value. then save this value in object.
I hope its clear enough. Thank you in advance
class Parent extends Component {
handlePriceMinMax(event) {
console.log(event.target.value);
/* I cant set the right values here.
* These values get overwritten with latest value.
* Because I get value on keypress/onChange value */
this.setState({
priceRange: {
min: event.target.value,
max: event.target.value
}
}
);
}
render() {
return (
<Child onChange={this.handlePriceMinMax}/>
)
}
}
class Child extends Component {
render() {
return (
<div className="form-group">
<label>Price</label>
<input type="number" onChange={this.props.onChange}/>
<input type="number" onChange={this.props.onChange}/>
</div>
);
}
}
export default Child;
https://codesandbox.io/s/zr30923nrm
I think this should work.
So basically you need to tell to the handlePriceMinMax what value do you want to update.
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { priceRange: { min: 0, max: 0 } };
}
handlePriceMinMax = (value, event) => {
const priceRange = this.state.priceRange;
priceRange[value] = event.target.value;
this.setState({ priceRange });
};
render() {
return <Child onChange={this.handlePriceMinMax} />;
}
}
class Child extends React.Component {
render() {
return (
<div className="form-group">
<label>Price</label>
<input type="number" onChange={this.minChange} />
<input type="number" onChange={this.maxChange} />
</div>
);
}
minChange = e => {
this.props.onChange("min", e);
};
maxChange = e => {
this.props.onChange("max", e);
};
}
I changed my code, you can create function in child first that get target value and pass to props function
class Child extends Component {
submitInput(e) {
this.props.onChange(e)
}
render() {
return (
<div className="form-group">
<label>Price</label>
<input type="number" onChange={(e) => this.submitInput(e)}/>
<input type="number" onChange={(e) => this.submitInput(e)}/>
</div>
);
}
}
export default Child;