I have an input field that i would like to update that users first name when you click submit for that specific user. right now i have an alert in handleSubmit to just see if its working. and it is but i want it to update the actual users name.
Displays Users on separate cards. would like the edit button to work for each user.
class User extends Component {
constructor(props) {
super(props);
this.state = {
names: ''
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.input = React.createRef();
}
handleChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.input.current.value);
event.preventDefault();
}
render() {
return (
<div className='UserCard'>
<div className='UserCardTop'>
<form className='Submit' onSubmit={this.handleSubmit}>
<input
type="text"
name="names"
value={this.state.names}
ref={this.input}
onChange={this.handleChange} />
<input type="submit" value="Submit" />
</form>
<h3>{this.props.name} {this.props.last}</h3>
<div className='ImageContainer'>
<img alt="image" width="80" src={this.props.image} />
</div>
</div>
<div className='UserCardBottom'>
<h5>{this.props.email}</h5>
<h5>{this.props.cell}</h5>
<h5>{this.props.city}, {this.props.state}</h5>
</div>
</div>
)
}
}
export default User
App.js
import React, { Component } from "react";
import axios from "axios";
import User from './User'
import Filter from './Filter.js'
class App extends Component {
constructor(props) {
super(props);
this.state = {
users: [],
searchTerm: '',
alphabetical: 'az'
};
this.handleChange = this.handleChange.bind(this);
this.handleFilter = this.handleFilter.bind(this);
}
componentDidMount() {
axios.get("https://randomuser.me/api/?page=3&results=10&seed=abc")
.then(response => {
console.log(response.data.results);
this.setState({ users: response.data.results });
})
.catch(error => {
console.log(error);
});
}
handleFilter(filterInput) {
this.setState(filterInput)
}
handleChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
let sortedUsers;
if (this.state.alphabetical === "az") {
console.log("sorted");
sortedUsers = this.state.users.sort((a, b) =>
a.name.first > b.name.first ? 1 : -1
);
}
let filteredUsers = sortedUsers;
if (this.state.searchTerm)
filteredUsers = this.state.users.filter(u =>
u.name.first.startsWith(this.state.searchTerm) || u.name.last.startsWith(this.state.searchTerm)
);
const userNames = filteredUsers.map(u => {
return <User
key={u.email}
name={u.name.first}
last={u.name.last}
image={u.picture.large}
email={u.email}
city={u.location.city}
state={u.location.state}
cell={u.cell}
/>;
});
return (
<div>
<Filter
searchTerm={this.state.searchTerm}
onFilter={this.handleFilter}
></Filter>
<select
name="alphabetical"
value={this.state.alphabetical}
onChange={this.handleChange}>
<option value="az">
A to Z
</option>
<option value="za">Z to A</option>
</select>
{userNames}
</div>
);
}
}
export default App
A simple solution would be to add an onChangeFirst callback prop to your User component, which would be invoked with the new first name when handleSubmit() is called:
User.js
handleSubmit(event) {
event.preventDefault();
if(this.props.onChangeFirst) {
/* Pass this users state (with names data) to onChange callback */
this.props.onChangeFirst(this.state.names);
}
}
The onChangeFirst callback prop would be "wired up" to the App component so that when it is called (from inside User.js), the corresponding user object (in App.js) is updated with the newFirstName. You could add the onChangeUserFirstName() function as shown below, which updates the first field of the user object in the App components state:
App.js
/* Helper function that updates first name state for matching user */
const onChangeUserFirstName = (user, newFirstName) => {
/* Updates state with newly mapped users to new array, where first name
if updated to that supplied in changes.names for existing user match */
this.setState({ users : this.state.users.map(u => {
return (u === user) ? { ...u, first : newFirstName } : u;
});
});
}
const userNames = filteredUsers.map(u => {
/* Add (newFirstName) => onChangeUserFirstName(u, newFirstName) */
return <User
onChangeFirst={ (newFirstName) => onChangeUserFirstName(u, newFirstName) }
key={u.email}
name={u.name.first}
last={u.name.last}
image={u.picture.large}
email={u.email}
city={u.location.city}
state={u.location.state}
cell={u.cell}
/>;
});
Related
I am trying to update state in react only after form is submitted. I have one html form which has 1 text input and a submit button, but it takes 2 click of submit button to actually change the state in react. I am using 2 methods handleSubmit and handleChange.
handleChange look for changes in input field and update the state accordingly.
handleSubmit append the state updated by handleChange to array on form submission
and state contains { itemslist: [], currentitem: "" }
when 1st time submit button is clicked it gives previous value of item (or gives empty array) and at 2nd time it gives array with value present in input field.
below is my full code
import React from 'react';
class App extends React.Component{
constructor(){
super()
this.state = {
currentitem: '',
itemslist: []
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleSubmit(event){
event.preventDefault();
this.setState((prevState) => {
return{
itemslist: prevState.itemslist.concat([this.state.currentitem])
}
})
console.log(this.state.items)
}
handleChange(event){
const {name, value} = event.target
this.setState({ [name] : value })
console.log(this.state.currentitem)
}
render(){
return(
<div>
<form onSubmit={this.handleSubmit} >
<input type='text' placeholder='enter text' name='currentitem' onChange={this.handleChange} value={this.state.currentitem} />
<button type='submit'>Submit</button>
</form>
</div>
)
}
}
export default App;
This answer could be a bit different of your code but this way it will work. Set the button type to button and make the button handle the submit, not the form. Then change the handleSubmit function to what I've got. I've tried it and it does works!:
import React from 'react';
class App extends React.Component{
constructor(){
super()
this.state = {
currentitem: '',
itemslist: []
}
this.handleChange = this.handleChange.bind(this)
this.handleSubmit = this.handleSubmit.bind(this)
}
handleSubmit(e){
e.preventDefault();
const { currentitem, itemslist } = this.state;
const newArray = [
...itemslist,
currentitem
];
this.setState({ itemslist, newArray });
}
handleChange(event){
const {name, value} = event.target
this.setState({ [name] : value })
console.log(this.state.currentitem)
}
render(){
return(
<div>
<form>
<input type='text' placeholder='enter text' name='currentitem' onChange={this.handleChange} value={this.state.currentitem} />
<button type='button' onClick={this.handleSubmit}>Submit</button>
</form>
// In cas eyou want to see the values in the array+
{
this.state.itemsList.map((item) => <p>{item}</>)
}
</div>
)
}
}
export default App;
setState function is asynchronous in react, so you cannot get the updated value immediately. But if you need to get the recent updated value from state, you must use callback function of setState.
this.setState({items: newItems}, () => { console.log(); })
I have modified your example like below to fulfil your requirement.
import React from 'react';
class App extends React.Component {
constructor() {
super();
this.state = {
currentitem: '',
items: []
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
event.preventDefault();
this.setState((prevState) => {
return {
items: prevState.items.concat([this.state.currentitem])
}
}, () => {
console.log(this.state.items)
});
}
handleChange(event) {
const {name, value} = event.target;
this.setState({[name]: value});
console.log(this.state.currentitem);
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input type='text' placeholder='enter text' name='currentitem' onChange={this.handleChange}
value={this.state.currentitem}/>
<button type='submit'>Submit</button>
</form>
</div>
)
}
}
export default App;
I am trying to get the user to input data for my TODO App instead of picking up data from a predefined array.
Below is my Parent component
import React from "react"
import TodoItem from "./TodoItem"
import todosData from "./todosData"
class App extends React.Component {
constructor() {
super()
this.state = {
name: "",
todos: todosData
}
this.handleChange = this.handleChange.bind(this)
this.nameEnter = this.nameEnter.bind(this)
}
handleChange(id) {
this.setState(prevState => {
const updatedTodos = prevState.todos.map(item => {
if (item.id === id){
return {
...item,
completed: !item.completed
}
}
return item
})
return {
todos: updatedTodos
}
})
}
nameEnter(){
var name = this.name
console.log(name)
}
render() {
const todoItems = this.state.todos.map(item => <TodoItem key={item.id} item={item}
handleChange = {this.handleChange} nameEnter= {this.nameEnter}/>)
return (
<div className="todo-list">
{todoItems}
</div>
)
}
}
export default App
This one is the child component where I have added input fields
import React from "react"
function TodoItem(props) {
return (
<div className="todo-item">
<input
type = "checkbox"
checked = {props.item.completed}
onChange = { () => props.handleChange(props.item.id) }
/>
<input type = "text" name= "name" />
<p>{props.item.text}</p>
</div>
)
}
export default TodoItem
This is how my page looks. Instead of predefined text example:"GROCERRY SHOPPING", I want the user to enter text and it should be in place of that.
Ok so here it is
<input onChange={(e)=>handleChange(e)}/>
//react hooks
const [values,setValues] = useState({inputValue: 'predefined'});
handleChange = (e)=>{
setValues({...values,[e.target.name]: e.target.value})
}
//classes
handleChange = (e)=>{
this.setState({[e.target.name]:e.target.value});
}
this generic and can apply to a number of inputs, not just one
import React from "react"
class Form extends React.Component {
constructor() {
super()
this.state = {
name: ""
}
}
onChange = (e) => {
this.setState({
[e.target.name]: e.target.value
})
}
render() {
const {name} = this.state
return (
<input type="text" name="name" value={name} onChange={this.onChange}/>
)
}
}
export default Form
changeInputVal (ev, index) {
ev.persist();
const newArr = this.state.inputArrVal;
newArr[index]=ev.target.value;
this.setState({inputArrVal:newArr, currentPage:1});
}
you need to do something like that in TodoItem --
where --
1- inputArrVal:{} is object And
2- "this.changeInputVal(event, props.item.id)}/>"
I need to have an edit button to edit the users first name, last name from the api but only update the input in local state. I've done a lot of research but can't find exactly what I'm looking for. I'm trying not to bring in other libraries (other than lodash possibly?). I've found a lot of other examples but its bringing in other libraries. Any suggestions would help (even on the code I have currently to help clean it up a bit.)
import React, { Component } from "react";
import axios from "axios";
import User from './User'
class App extends Component {
constructor(props) {
super(props);
this.state = {
users: [],
searchTerm: '',
alphabetical: 'az'
};
this.handleChange = this.handleChange.bind(this);
}
componentDidMount() {
axios.get("https://randomuser.me/api/?results=20")
.then(response => {
console.log(response.data.results);
this.setState({ users: response.data.results });
})
.catch(error => {
console.log(error);
});
}
handleChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
let sortedUsers;
if (this.state.alphabetical === "az") {
console.log("sorted");
sortedUsers = this.state.users.sort((a, b) =>
a.name.first > b.name.first ? 1 : -1
);
}
let filteredUsers = sortedUsers;
if (this.state.searchTerm)
filteredUsers = this.state.users.filter(u =>
u.name.first.startsWith(this.state.searchTerm) || u.name.last.startsWith(this.state.searchTerm)
);
const userNames = filteredUsers.map(u => {
return <User
key={u.email}
name={u.name.first}
last={u.name.last}
image={u.picture.medium}
email={u.email}
city={u.location.city}
state={u.location.state}
cell={u.cell}
/>;
});
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>
Search for user:
<input
type="text"
name="searchTerm"
value={this.state.searchTerm}
onChange={this.handleChange}
/>
</label>
<input type="submit" value="Submit" />
</form>
<select
name="alphabetical"
value={this.state.alphabetical}
onChange={this.handleChange}>
<option selected value="az">
A to Z
</option>
<option value="za">Z to A</option>
</select>
{userNames}
</div>
);
}
}
export default App
I have a main react component called 'App' which contain user input data in its state. The state is updated with setState() every time user enter new data. Then the state is passed as props to another component called 'IncomeList' which render the data on screen. However the IncomeList component is not getting updated state after user input some data.
class App extends React.Component {
constructor(props) {
super(props);
this.addData = this.addData.bind(this);
this.state = {
expenses: [],
income: [],
}
}
addData(data) {
if (data.type == 'income') {
this.setState((prevState) => {
income: prevState.income.push(data);
}, console.log(this.state.income));
} else if (data.type == 'expense') {
this.setState((prevState) => {
expenses: prevState.expenses.push(data);
})
}
}
render() {
return (
<div>
<UserInput addData={this.addData} />
<IncomeList income={this.state.income} />
</div>
);
}
}
// UserInput component which contain a form
class UserInput extends React.Component {
constructor(props) {
super(props);
this.addDataLocal = this.addDataLocal.bind(this);
}
addDataLocal(e) {
e.preventDefault();
const data = {
type: e.target.elements.type.value,
description: e.target.elements.description.value,
amount: e.target.elements.amount.value
}
this.props.addData(data);
}
render() {
return (
<div>
<form onSubmit={this.addDataLocal}>
<select name="type" id="input-type" name="type">
<option value="income">Income</option>
<option value="expense">Expense</option>
</select>
<input type="text" placeholder="decription..." name="description"/>
<input type="number" placeholder="amount..." name="amount"/>
<input type="submit" value="Add"/>
</form>
</div>
)
}
}
class IncomeList extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{
this.props.income.map((item) => {
return (
<IncomeListItem key={item.amount} item={item}/>
)
})
}
</div>
)
}
}
You don't return anything from this.setState. You need return an object to be merged with your current state.
addData(data) {
const key = data.type === 'income' ? 'income' : 'expenses';
this.setState(prevState => ({
// with Computed Property Names we can make our
// code concise and avoid conditionals
[key]: [...prevState[key], data]
}), console.log(this.state[key]));
}
your addData should be like this
addData(data) {
if (data.type == 'income') {
let income=[...this.state.income];
income.push(data);
this.setState({
income:income
})
} else if (data.type == 'expense') {
let expenses=[...this.state.expenses];
expenses.push(data);
this.setState({
expenses:expenses
});
}
}
With #Asaf Aviv input I have created a working fiddle. Hope this will help.
JS Fiddle
I am trying to modify some state parameters inside a child component and then pass it for use in the parent.
I have a state object called driver. Inside of my EditDriver component, I am calling ProfileTab and passing it the driver object, with fields like firstName, and more. Inside of the ProfileTab is where I do the actual modification. Inside the parent component EditDriver is where I need to send this data to the server in the updateDriver function.
//...imports
class EditDriver extends Component {
constructor(props) {
super(props);
this.state = {
driver: {
firstName: '',
},
};
this.updateDriver = this.updateDriver.bind(this);
this.driverOnChange = this.driverOnChange.bind(this);
}
updateDriver() {
this.props.updateDriver(this.state.driver);
}
driverOnChange(data) {
this.setState({
driver: data
});
}
render() {
return (
<ViewContainer
fullWidth
title="Driver"
toolbarRight={
<Button
onClick={this.updateDriver}
>
Save
</Button>
}
>
<ProfileTab match={this.props.match} driver={this.state.driver} driverOnChange={this.driverOnChange} />}
</ViewContainer>
);
}
}
const mapDispatchToProps = dispatch => ({
updateDriver: driver => dispatch(updateDriver(driver))
});
export default connect(
null,
mapDispatchToProps,
)(withStyles(styles)(EditDriver));
and the ProfileTab code looks like this:
class ProfileTab extends Component {
constructor(props) {
super(props);
this.state = {
driver: {
firstName: '',
},
};
this.handleDriverInputChange = this.handleDriverInputChange.bind(this);
}
componentWillReceiveProps(nextProps) {
if (nextProps.driver && !nextProps.isFetching) {
this.setState({
driver: {
...this.state.driver,
...nextProps.driver
}
});
}
}
handleDriverInputChange(event) {
const target = event.target;
const value = target.type == 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
driver: {
...this.state.driver,
[name]: value
}
});
}
render() {
const {
driver,
} = this.state;
return (
<div>
<TextField
name="firstName"
label="firstName"
margin="normal"
type="text"
onChange={this.handleDriverInputChange}
value={driver.firstName}
/>
</div>
);
}
}
ProfileTab.propTypes = {
driver: PropTypes.object.isRequired,
driverOnChange: PropTypes.func.isRequired,
};
export default ProfileTab;
The driverOnChange function does not actually set the state of the driver parameter in the ProfileTab. What would be the most efficient way to render the ProfileTab in the EditDriver component and have the changed parameter firstName available?
Just call driverOnChange in this method inside ProfileTab component
handleDriverInputChange(event) {
const target = event.target;
const value = target.type == 'checkbox' ? target.checked : target.value;
const name = target.name;
this.props.driverOnChange(value);
this.setState({
driver: {
...this.state.driver,
[name]: value
}
});
}
You've binded driverOnChange to EditDriver component therefore I think this in driverOnChange would refer to it even when it is being called inside ProfileTab component