Page load errors due to missing this.setState - reactjs

I'm getting an error when I populate my form with stored data. My form contains an array so I'm using {this.state.careerHistoryPositions.map((careerHistoryPosition) to create a loop. The error comes from {careerHistoryPosition.errors['company']. This part of the form, is related to errors when the form is submited and I don't store this in the database so when the form is populated, errors isn't set. I assume it's to do with this.setState looking for something that doesn't exist.
Snippet: Constructor
constructor(props) {
super(props);
let uniqueId = moment().valueOf();
const profileCandidateCollection = props.profileCandidate;
const profileCandidateCollectionId = profileCandidateCollection._id;
const careerHistoryPositions = profileCandidateCollection && profileCandidateCollection.careerHistoryPositions;
this.state = {
careerHistoryPositions: careerHistoryPositions || [
{
company: '',
uniqueId: uniqueId,
title: '',
description: '',
startDateMonth: '',
startDateYear: '',
startDateMonth: '',
endDateYear: '',
isCurrent: false,
isDisabled: false,
errors: {}
}
],
profileCandidateCollectionId: profileCandidateCollectionId || null
};
}
Snippet: render
{this.state.careerHistoryPositions.map((careerHistoryPosition) => (
<div key={careerHistoryPosition.uniqueId} className="individual-position">
<SingleInput xs={9} inputType={'text'} controlFunc={this.handleCompanyNameChange(careerHistoryPosition.uniqueId)} content={careerHistoryPosition.company} placeholder={'Company'} bsSize={null}/>
{careerHistoryPosition.errors['company']
? <Col sm={12} className="has-error">
<span className="help-block custom-error">{careerHistoryPosition.errors['company']}</span>
</Col>
: ''}
</div>
))}

Related

Cannot read property 'files' of undefined for sending multiple images

Code
class Add_Give_Item_Form extends Component {
constructor(props) {
super(props);
this.state = {
// #インプット情報用
info: {
name: '',
owner: '',
keyword1: '',
keyword2: '',
keyword3: '',
bland: '',
state: '未使用、新品',
category: '',
images: [],
detail: '',
},
// Validation用
//  urlは必須項目ではないのでValidationには含めない
message: {
name: '',
keyword1: '',
keyword2: '',
keyword3: '',
state: '',
category: '',
detail: '',
},
allCategory: null,
allBland: null,
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleImageSelect = this.handleImageSelect(this);
}
////
...
////
handleChange = (e) => {
const name = e.target.name;
const value = e.target.value;
const { info, message } = this.state;
this.setState({
info: { ...info, [name]: value },
});
this.setState({
message: { ...message, [name]: this.validator(name, value) },
});
};
handleImageSelect = (e) => {
this.setState({
info: { ...this.state.info, images: [...this.state.info.images, e.target.files] },
});
};
render() {
const { info, message, allCategory, allBland } = this.state;
// setStateが完了するまではnullにする。
if (this.state.allCategory === null || this.state.allBland === null) {
return <CircularProgress />;
} else {
return (
<div>
///////
.....
///////
<label>Images</label>
<input type="file" multiple onChange={this.handleImageSelect} />
What I want to do
I would like to catch each file sent by a user and put into state as this.state.info.images which is an array.
I saw some questions on stackoverflow and then I found some solutions. When I wrote the same code as what I saw, I got an error like below.
cannot read property files of undefined
I should write the same code but I got the error for some reasons.
I may take another way to realize what I want to do, but I want to write readable codes and figure out why it is happening.
I would like you to teach me why this happens and solutions.
Thank you very much.
I just notice I didn't put bind with this.handleImageSelect = this.handleImageSelect(this).
Now it works well.
Thank you very much.

Want to populate the input values based on the click of an object inside map(): React+Typescript

I am maintaining an array of objects which is stored in a state object. Basically I am pushing each object to this array whenever I click on Add button .This stores this object in array.
Also I am iterating this array of objects to display down the page.
Right now I am trying to fill the input fields based on the object that I have clicked. I am unable to do it. Basically, the object that I have clicked should populate the input fields and then I should be able to edit it
Help would be appreciated
The structure of array of objects:
users= [
{"name":"xxx","email":"yyy","phone":"656"},
{"name":"yyy","email":"xxx","phone":"55"}
];
Component Code
import * as React from 'react';
interface IState{
users : Account[];
user: Account
}
interface Account{
name: string;
email: string;
phone: string
}
export default class App extends React.Component<{},IState> {
constructor(props:any){
super(props);
this.state= {
users: [],
user: {
name: '',
email: '',
phone: '',
}
}
}
removeAccount = (i:number) => {
let users = [...this.state.users];
users.splice(i,1);
this.setState({users},()=>{console.log('setting the data')});
}
handleChange = ( event: React.ChangeEvent<HTMLInputElement>) => {
this.setState({
user:{
...this.state.user,
[event.currentTarget.name]:event.currentTarget.value
}
})
}
onAdd = () => {
e.preventDefault();
this.setState({
users: [...this.state.users, this.state.user],
user: { name:'', email: '', phone: ''}
},()=>{console.log('adding')});
}
clearInputs = () => {
this.setState({user: { name:'', email: '', phone: ''}});
}
showDetails = (i:number) => { //I need to populate the input fields based on the index of the object clicked.
console.log(i);
}
render(){
const { name, email, phone } = this.state.user;
<React.Fragment>
<form onSubmit={this.onAdd}>
<input type="text" value={name} onChange={(e:any) => this.handleChange(e)} name={"name"} />
<input type="text" value={email} onChange={(e:any) => this.handleChange(e)} name={"email"} />
<input type="text" value={phone} onChange={(e:any) => this.handleChange(e)} name={"phone"} />
<button type="submit">Add</button>
</form>
<ul>
{this.state.users.map((row:any ,index: number) =>
<li key={index}>
<a onClick={()=> this.showDetails(index)}><span>{row.name}</span></a> // on click of this,i need to display the values corresponding to this object in the above input fields
<i className="close far fa-times" onClick={() =>this.removeAccount(index)}/>
</li>
)}
</ul>
</React.Fragment>
}
}
Based on logic of the code showDetails should look like
showDetails = (i:number) => {
this.setState ({user: this.state.users.splice(i,1)});
console.log(i);
}
Just set user to the selected element of users array. React will do update and calls render() with updated data.
Also utilizing splice will remove currently editing user from array. THis follow logic of the code. After edit Add should be clicked to add modified user back to array. This may be not convenient, so you may consider adding editingIndex to state and specify which user object currently editing. In such case you'll have to save index of selected object in editingIndex. In handleChange you should check if some user object editing now and modify data not only in user property of state but in corresponding users array element
interface IState{
users : Account[];
user: Account;
editingIndex: number | null;
}
// In constructor
constructor(props:any){
super(props);
this.state= {
users: [],
user: {
name: '',
email: '',
phone: '',
},
editingIndex: null
}
}
showDetails = (i:number) => {
this.setState ({user: this.state.users[i], editingIndex: i});
console.log(i);
}
handleChange = ( event: React.ChangeEvent<HTMLInputElement>) => {
let user = {...this.state.user,
[event.currentTarget.name]:event.currentTarget.value};
this.setState({user});
// If we currently editing existing item, update it in array
if (this.state.editingIndex !== null) {
let users = [...this.state.users];
users[this.state.editingIndex] = user;
this.setState({users});
}
}
removeAccount = (i:number) => {
let users = [...this.state.users];
// If we're going to delete existing item which we've been editing, set editingIndex to null, to specify that editing ends
if (this.state.editingIndex === i)
this.setState({user: {name: '', email: '', phone: ''}, editingIndex: null});
users.splice(i,1);
this.setState({users},()=>{console.log('setting the data')});
}
onAdd = () => {
e.preventDefault();
// If we NOT editing, but adding new editingIndex will be null so add user to users array. If we editing existing element it's no need to add it once again.
if (this.state.editingIndex === null)
this.setState({ users: [...this.state.users, this.state.user] });
this.setState ({ editingIndex: null,
user: { name:'', email: '', phone: ''}
},()=>{console.log('adding')});
}
// render will have no change

How to set initial state dynamically?

My initial state varies depending on how many items I get from the API call.
So basically sometimes it looks like this:
class MyComponent extends Component {
state = {
activeItem0: '',
activeItem1: '',
activeItem2: '',
activeItem3: '',
}
...
and some times it might look like this depending on the data returned by the database.
class MyComponent extends Component {
state = {
activeItem0: '',
activeItem1: '',
activeItem2: '',
activeItem3: '',
activeItem4: '',
activeItem5: '',
}
...
Is there a way to set the initial state keys dynamically?
here's what I have so far:
class MyComponent extends Component {
state = {
activeItem0: '',
activeItem1: '',
activeItem2: '',
activeItem3: ''
}
// Set the value for each product on the store
handleItemClick = (e, { name, children }) => this.setState({[e.target.name]: children })
let buttonGroup = _.times(products.length, i => (
<Button.Group>
<Button
name={`activeItem${i}`}
active={this.state[`activeItem${i}`] === 'Val1'}
onClick={this.handleItemClick}
>
Val1
</Button>
<Button
name={`activeItem${i}`}
active={this.state[`activeItem${i}`] === 'Val2'}
onClick={this.handleItemClick}>
Val2
</Button>
<Button
name={`activeItem${i}`}
active={this.state[`activeItem${i}`] === 'Val3'}
onClick={this.handleItemClick}>
Val3
</Button>
</Button.Group>
)
render() {
return(
<div>
<Grid container>{selectSizeInSideBar}</Grid>
</div>
)
}
}
So the issue is that if products.length returned by the DatabBase is 4
I should have in my initial state like this
state = {
activeItem0: '',
activeItem1: '',
activeItem2: '',
activeItem3: ''
}
but if my products.length returned by the DatabBase is 6 then
my initial state should look like this:
state = {
activeItem0: '',
activeItem1: '',
activeItem2: '',
activeItem3: ''
activeItem4: ''
activeItem5: ''
}
You can make a method that initialize your state after your component is ready ( let's say the data of your API has been received ), so its preferrable to make this in the componentDidMount.
const initializeStateForKeys = ( products ) => {
const fakeState = {};
_.each( products, product => {
fakeState[ product.id ] = { id: product.id ,isActive: product.isActive }
} );
return fakeState
}
and you should use it like this:
componentDidMount(){
API.fetchMyProducts()
.then( response => {
this.setState( { products: response.data.products }
, () => {
this.initializeStateForKeys( this.state.products);
});
});
}

invalid Invalid left-hand side in arrow function parameters (43:13)

hello im a noob in react and am trying to pass the car.id using props to my editcar component so i can update it via firebase , however im getting a an error Invalid left-hand side in arrow function parameters (43:13) any idea how i can pass the car.id to edit function? thanks for the help!
admin_cars.js
<ul className="TaskList">
{
Cars.map(car => (
<tr>
<th scope="row">{car.id}</th>
<td>{car.year}</td>
<td>{car.make}</td>
<td>{car.model}</td>
<td>{car.body_type}</td>
<td>{car.int_color}</td>
<td><img src={car.link} height="92" /> </td>
<td>{car.price}</td>
<td>{car.status ? "Available" : "Sold"}</td>
<td>
<Link to={`/admin/editcar/${this.props.car.id}`}>
<Icon icon={pencil} />
</Link>
</td>
</tr>
))
}
</ul>
edit_car.js
import { CarsRef, timeRef } from './reference';
class EditCar extends Component {
state = {
year: '',
make: '',
model: '',
trim: '',
engine: '',
drive_type: '',
body_type: '',
ext_color: '',
int_color: '',
transmission: '',
price: 0,
sale: 0,
status: true,
vin: '',
link: '',
elect_stab: '',
wireless: '',
seat: '',
keyless: '',
trip_comp: '',
tire_pressure: '',
wiper: '',
id:'',
headlight: '',
alertMsg: false
}
editcar = (e, car.id) => {
alert(this.car.id)
e.preventDefault();
const NewCar= {
body_type: this.state.body_type.trim(),
wiper: this.state.wiper,
headlight: this.state.headlight,
make: this.state.make,
link: this.state.link,
engine: this.state.engine,
transmission:this.state.transmission,
vin:this.state.vin,
seat: this.state.seat,
price: this.state.price,
ext_color: this.state.ext_color,
checked: false,
starred: false,
timestamp: timeRef
};
CarsRef.child().update(NewCar);
this.setState({ body_type: '' });
this.setState({ wiper: '' });
this.setState({ make: '' });
this.setState({link:''});
this.setState({ headlight: '' });
this.setState({price: ''});
this.setState({transmission: ''});
this.setState({engine: ''});
this.setState({vin: ''});
this.setState({ext_color: ''});
this.setState({id: ''})
}
I think this might be related to your editcar method. The second parameter is not valid and you probably meant for it to be car and not car.id, so:
change
editcar = (e, car.id) => {}
to
editcar = (e, car) => {}

Form renders with array of objects but errors when I type

I render a form with an array of objects to populate the form when it first loads using componentWillReceiveProps. The form renders correctly with no errors. this.state.data renders an array of objects that looks like:
this.state.data: (3) [Object, Object, Object]
[{
company: Company A,
title: Title A,
uniqueId: uniqueId A
},
{
company: Company A,
title: Title A,
uniqueId: uniqueId A
}]
When I type in the form handleInputChange appears to be causing the error that fires with each keyboard entry Uncaught TypeError: positions.map is not a function at PositionList StatelessComponent.ReactCompositeComponent.js.StatelessComponent.render and when I submit the form this.state.data appears to not have changed as it returns and array of objects that looks like:
this.state.data: (3) [Object, Object, Object]
[{
company: Company A,
title: Title A,
uniqueId: uniqueId A
},
{
company: Company A,
title: Title A,
uniqueId: uniqueId A
},
{
"": "Whatever text I've typed in to the input field"
}]
Please see the full form render below. Although it's long I think I need to add a fair amount of detail to show the problem.
function PositionItem(props) {
// Correct! There is no need to specify the key here:
return <li>
<input type="text" defaultValue={props.company} onChange={props.onChange} />
</li>;
}
function PositionList(props) {
const positions = props.positions;
const listPositions = positions.map((position) =>
// Correct! Key should be specified inside the array.
<PositionItem key={position.uniqueId.toString()}
company={position.company}
uniqueId={position.uniqueId}
onChange={props.onChange}
/>
);
return (
<ul>
{listPositions}
</ul>
);
}
export default class CareerHistoryFormPage extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
};
this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
}
componentWillReceiveProps(nextProps) {
const profileCandidateCollection = nextProps.profileCandidate;
const careerHistoryPositions = profileCandidateCollection && profileCandidateCollection.careerHistoryPositions;
const positions = careerHistoryPositions.map((position) =>
({
uniqueId: position.uniqueId || '',
company: position.company || '',
title: position.title || ''
}));
this.setState({
data: positions
})
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
const data = {...this.state.data, ...{[name]: value}};
this.setState({
data: data
});
}
handleFormSubmit(event) {
event.preventDefault();
console.log("click", this.state.data);
}
render() {
console.log('this.state.data: ', this.state.data);
return (
<div>
<form className="careerHistoryForm" onSubmit={this.handleFormSubmit}>
<PositionList positions={this.state.data} onChange={this.handleInputChange} />
<input type="submit" className="btn btn-primary" value="Save" />
</form>
</div>
);
}
}
You're setting data to an object then trying to call .map on it.
.map only works on arrays.
It looks like you want to replace this line:
const data = {...this.state.data, ...{[name]: value}};
with this line:
const data = [...this.state.data, {[name]: value}];

Resources