I'm trying to get a simple API call working, where the component calls the API as its mounting and sets the state to be rendered. But when I try to get the state to change an object in it, it says that the state is undefined.
TypeError: Cannot read property 'state' of undefined
class SpellGrid extends React.Component {
constructor(props) {
super(props);
this.state = {
value: '',
spacing: '16',
username: 'admin',
password: 'notpassword',
description: '',
remember: false,
spell: {
name: '',
school: '',
},
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.mapApiToState = this.mapApiToState.bind(this);
}
mapApiToState() {
// I've tried with all of the axios code in here.
}
componentDidMount() {
axios
.get("http://localhost:8000/api/spells/1")
.then(function(response) {
console.log('response', response);
let fields = response.data[0].fields;
// THIS IS THE LINE THAT IS ERRORING
let spell = Object.assign({}, this.state.spell);
spell.name = fields.Name;
spell.school = fields.School;
console.log('spell', spell);
this.setState({spell});
console.log('state.spell', this.state.spell);
//console.log('state', this.state);
})
.catch(function(error) {
console.log(error);
});
console.log('state', this.state);
}
handleChange = name => event => {
this.setState({
[name]: event.target.value,
});
};
onSubmit = (event) => {
event.preventDefault();
this.props.onSubmit(this.state.username, this.state.password)
};
handleSubmit(e) {
console.log('Form state: ', this.state);
e.preventDefault();
}
render() {
const {classes, theme} = this.props;
const { spacing } = this.state;
return (
<div>{this.state.spell.name}</div>
);
}
} export default withStyles(styles, { withTheme: true })(SpellGrid);
If you are using this, you will need to be carefull in which function scope you're in:
axios
.get("http://localhost:8000/api/spells/1")
.then(response => {
// Since the `response` is now an arrow function, we still
// get access to the original `this`
let fields = response.data[0].fields;
let spell = Object.assign({}, this.state.spell);
spell.name = fields.Name;
spell.school = fields.School;
this.setState({
spell
});
})
.catch(error => {
console.log(error);
});
Related
I have been working on react native project and I ran into an issue.
here's my parent class:
class Home extends Component {
constructor(props) {
super(props);
this.handler = this.handler.bind(this);
}
state = {
userDocExists: null
};
handler() {
console.log("in handler");
this.setState({
userDocExists: true
});
}
render() {
return <UserInfo handler={this.handler} />;
}
}
my child class:
class UserInfo extends Component {
constructor(props) {
super(props);
}
registerUser = () => {
const userId = firebase.auth().currentUser.uid;
firestore
.collection("Users")
.doc(userId)
.set({
firstName: this.state.firstName,
houseNumber: this.state.houseNumber,
CommunityId: this.state.CommunityId,
})
.then((docRef) => {
this.setState({ isLoading: false });
this.props.handler; //not being called
})
.catch((error) => {
console.error("Error adding document: ", error);
});
} else {
this.setState({ ErrorMes: true });
}
};
My this.props.handler is not being called for some reason. but when I use it in an OnPress of a button in the render , it works fine. Could someone tell me why this behaviour occurs?
this.props.handler will give you function definition but to call it You have to invoke it this.props.handler() this trigger the function handler of the parent
class UserInfo extends Component {
constructor(props) {
super(props);
}
registerUser = () => {
const userId = firebase.auth().currentUser.uid;
firestore
.collection("Users")
.doc(userId)
.set({
firstName: this.state.firstName,
houseNumber: this.state.houseNumber,
CommunityId: this.state.CommunityId,
})
.then((docRef) => {
this.setState({ isLoading: false });
//this.props.handler;
this.props.handler(); //Invoked the function
})
.catch((error) => {
console.error("Error adding document: ", error);
});
} else {
this.setState({ ErrorMes: true });
}
I'm trying to pass data in React from parent to child , I already managed to set right value from one file to another, but same that information that I passed I need to pass once more again. I will show you some code so you can understand actual problem.
From List.js file I'm taking the right information like
<Products categoryid={item.id}/>
so that same item.id I passed to Products, as you see I have this.props.categoryid which is giving me right information as value to add this item as you see, and it looks like
import React, { Component } from 'react'
import { getProducts, addItem, deleteItem, updateItem } from './ProductFunctions'
class Products extends Component {
constructor() {
super()
this.state = {
id: '',
title: '',
price: '',
off_price: '',
category_id: '',
arttitle: '',
artbody: '',
editDisabled: false,
items: []
}
this.onSubmit = this.onSubmit.bind(this)
this.onChange = this.onChange.bind(this)
}
componentDidMount() {
this.getAll()
}
onChange = e => {
this.setState({
[e.target.name]: e.target.value
})
}
getAll = () => {
getProducts().then(data => {
this.setState(
{
title: '',
price: '',
off_price: '',
category_id: this.props.categoryid,
items: [...data]
},
() => {
console.log(this.state.items)
}
)
})
}
So the real problem is how to pass this this.props.categoryid as a category_id to getProducts function in ProductFunctions.js so I can get list from ?
export const getProducts = category_id => {
return axios
.get('/api/products/${category_id}', {
headers: { 'Content-Type': 'application/json' }
})
.then(res => {
return res.data
})
}
It seems you forgot to use `` and instead used '' in the getProducts function in ProductFunctions.js, so let's correct that.
export const getProducts = category_id => {
return axios
.get(`/api/products/${category_id}`, {
headers: { "Content-Type": "application/json" }
})
.then(res => {
return res.data;
});
};
Now, just pass the categoryid you obtained from props to the getProducts in the getAll method, when its invoked. (As per what the exported function expects in ProductFunctions.js
getAll = () => {
const { categoryid } = this.props;
getProducts(categoryid).then(data => {
this.setState(
{
title: "",
price: "",
off_price: "",
category_id: categoryid,
items: [...data]
},
() => {
console.log(this.state.items);
}
);
});
};
Access the prop within getAll function
getAll = () => {
getProducts(this.props.categoryid).then(data => {
this.setState({
title: '',
price: '',
off_price: '',
category_id: this.props.categoryid,
items: [...data]
},
() => {
console.log(this.state.items)
}
)
})
}
So my console.log prints as if I am one step behind on my onSubmit calls, but when I check the React web tools on Chrome I see that my state is up to date. Can someone please explain to me what I am doing wrong here? I believe it is a misunderstanding up asynchronous and synchronous methods, but could really use a good explanation on this.
import React, { Component } from 'react';
import TodoInput from './todo-input';
class App extends Component {
constructor(props){
super(props);
this.state = {
todos: [],
inputValue: ''
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
};
handleChange = (e) => {
e.preventDefault();
this.setState({
inputValue: e.target.value
});
}
handleSubmit = (e) => {
e.preventDefault();
const newTodo = {
title: this.state.inputValue,
id: Date.now,
done: false
};
this.setState((prevState) => ({
todos: [...prevState.todos, newTodo]
}));
this.setState({inputValue: ''});
console.log(this.state.todos);
}
render() {
const mappedTodos = this.state.todos.map((todo, index) =>
<div key={index}>
{todo.title}
</div>
)
return (
<div>
<TodoInput
value={this.state.inputValue}
onChange={this.handleChange}
onSubmit={this.handleSubmit}
/>
{mappedTodos}
</div>
);
}
}
export default App;
This should console log after state update.
this.setState((prevState) => ({
todos: [...prevState.todos, newTodo]
}), () => {
console.log(this.state.todos)
});
Yes the setState() is an asynchronous call, so your console.log() is returning the state as if none of your setState() calls were made. However, this can be solved using the setState callback function argument to produce your expected output:
handleSubmit = (e) => {
e.preventDefault();
const newTodo = {
title: this.state.inputValue,
id: Date.now,
done: false
};
this.setState((prevState) => ({
todos: [...prevState.todos, newTodo],
inputValue: ''
}), () => { console.log(this.state.todos) });
}
I am new to React/Redux and I am stuck in a problem. My fetched data from API is not being passed to props. It's always an empty object.
I see that there might be some issues that I am not even aware of but I don't have a clue where to look for.
Please check my codes below:
RegisterPage.jsx
import { connect } from 'react-redux';
import { userActions } from '../_actions';
class RegisterPage extends React.Component {
constructor(props) {
super(props);
this.state = {
user: {
first_name: '',
last_name: '',
properties_id: '',
email: '',
phone_number: '',
password: ''
},
submitted: false,
checked: false,
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount() {
this.props.dispatch(userActions.getAll());
}
handleChange(event) {
const { name, value } = event.target;
const { user } = this.state;
this.setState({
user: {
...user,
[name]: value
},
checked: !this.state.checked
});
}
handleSubmit(event) {
event.preventDefault();
this.setState({ submitted: true });
const { user } = this.state;
const { dispatch } = this.props;
if(this.state.checked) {
if (user.first_name && user.last_name && user.properties_id &&
user.email && user.phone_number && user.password) {
dispatch(userActions.register(user));
}
} else {
alert("Please tick the checkbox to agree to Terms and Conditions");
}
}
render() {
const { registering, properties } = this.props;
const { user, submitted } = this.state;
return (......)
}
function mapStateToProps(state) {
const { registering } = state.registration;
const { properties } = state;
return {
properties,
registering
};
}
const connectedRegisterPage = connect(mapStateToProps)(RegisterPage);
export { connectedRegisterPage as RegisterPage };
users.reducers.js
export function users(state = {}, action) {
switch (action.type) {
case userConstants.GETALL_REQUEST:
return {
loading: true
};
case userConstants.GETALL_SUCCESS:
return {
items: action.properties
//action.users
};
case userConstants.GETALL_FAILURE:
return {
error: action.error
};
default:
return state
}
}
user.actions.js
export const userActions = {
login,
logout,
register,
getAll,
delete: _delete
};
function getAll() {
return dispatch => {
dispatch(request());
userService.getAll()
.then(
properties => dispatch(success(properties)),
error => dispatch(failure(error.toString()))
);
};
function request() { return { type: userConstants.GETALL_REQUEST } }
function success(properties) { return { type: userConstants.GETALL_SUCCESS, properties } }
function failure(error) { return { type: userConstants.GETALL_FAILURE, error } }
}
user.service.js
// Get All Properties
function getAll() {
const requestOptions = {
method: 'GET'
};
return fetch(`${config.apiUrl}/api/properties`, requestOptions).then(handleResponse).then(
properties => {
return properties;
}
);
}
Here's the screenshot of the console:
It is clear that properties array is not empty. But when I am going to use properties, it is empty. I don't know what's wrong. If anyone could help figure out what's wrong with my code or something that I missed, your help will be greatly appreciated. I just need to fix this so I could move forward. Thanks in advance!
I thinking that your state tree might not contain state.properties but instead state.items. Unless if you did something in combineReducers() that changes the shape of it again.
case userConstants.GETALL_SUCCESS:
return {
items: action.properties
//action.users
};
This part would probably cause action.properties to be stored in state.items instead of state.properties
I'd recommend using ReduxDevTools to make your life with state easier
This is the code. No idea as to why there is a problem.
class TeacherForm extends Component {
constructor({ data }) {
super();
this.isUpdatingForm = !! data;
this.state = Object.assign({ ... });
this.handleSubmit = this.handleSubmit.bind(this);
this.removeTeacher = this.removeTeacher.bind(this);
}
handleChange(value, field) {
this.setState({ shouldUpdate: true, [field]: value });
}
handleSubmit(e) {
e.preventDefault();
const { name, subjects, parttime, timing } = this.state;
if (this.isUpdatingForm) {
return update.call({
_id: this.props.data._id,
transaction: { name, subjects, parttime, timing },
}, () => this.setState({ shouldUpdate: false }));
}
return add.call();
}
removeTeacher() {
return remove.call(this.props.data._id);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
...
</form>
);
}
}
The error gets thrown at the handleSubmit method in the callback of update.call. This normally shows up when I call removeTeacher and a list updates and this component unmounts.
It sounds like the callback () => this.setState({ shouldUpdate: false }) is executed after that the component is unmounted. Is that possible? If so, one way to get around that is to replace this part by
return update.call({
_id: this.props.data._id,
transaction: { name, subjects, parttime, timing },
}, () => { !this.unmounted && this.setState({ shouldUpdate: false }); });
and to add
componentWillUnmount() {
this.unmounted = true;
}