Im novice to React js, i don't know whats wrong with below code, but i'm getting setState is not a function error.Please help me to fix this.
class AppBarLayout extends React.Component {
constructor(props) {
super(props);
this.state = {
visibleSideBar:true,
slide:""
}
}
showProfile(){
this.setState({
slide:'slide'
});
console.log(this.state.slide);
}
render(){
return(
<div>
<header>
<NavBar show={this.showProfile}/>
<Profile slide={this.state.slide} />
</header>
</div>
);
}
}
export default AppBarLayout;
You need to bind this.showProfile in the component constructor
this.showProfile = this.showProfile.bind(this)
More detail about this on the Handling Events page of the React doc : https://facebook.github.io/react/docs/handling-events.html
Expanding on Delapouite's answer if you don't like to bind every function in the constructor you can use arrow functions to automatically bind to the correct context.
For example:
class AppBarLayout extends React.Component {
constructor(props) {
super(props);
this.state = {
visibleSideBar:true,
slide:""
}
}
// Now showProfile is an arrow function
showProfile = () => {
this.setState({
slide:'slide'
});
console.log(this.state.slide);
}
render(){
return(
<div>
<header>
<NavBar show={this.showProfile}/>
<Profile slide={this.state.slide}/>
</header>
</div>
);
}
}
export default AppBarLayout;
In my case, I solved the problem without binding.
Declaring the method like this was generating the error:
async onSubmit(e) {
event.preventDefault();
this.setState({ shopEthereumAddress: e.target.id });
}
The CORRECT declaration which will not generate the error is this:
onSubmit = async event => {
event.preventDefault();
this.setState({ shopEthereumAddress: event.target.id });
}
This works.
toggleSwitch() {
this.setState({
name: 'Ram ji'
});
}
Using an arrow function keeps the context of this set to the parent scope. The main benifit of arrow functions apart from being more concise is
Main benefit: No binding of ‘this’
// use this arrow function instead of
toggleSwitch = () => {
this.setState({
name: 'Ram ji' //It's working
});
}
Related
I'm trying to pass my state as props from component Locatione.js to Map.js, so the props are available when I call the function SendLocation in Map.js.
Here is my component Locatione
export default class Locatione extends Component {
state = {
location: null
};
componentDidMount() {
this._getLocationAsync();
}
_getLocationAsync = async () => {
let location = await Location.getCurrentPositionAsync({ });
this.setState({ location });
console.log("log this pls", this.state); // the state here logs correctly
};
render() {
return (
<Map locatione={this.state} /> // when accesing this props in Map, I'm getting **null**
);
}
}
Here is my Map.js component
export default class Map extends React.Component {
sendLocation() {
console.log("sending location log", this.props); // the props here appear as null
}
render() {
return (
<Button
title="Send Sonar"
onPress={(this.sendLocation, () => console.log("hi", this.props))} //the props here log correctly
/>
);
}
}
I also tried passing my props in this fashion, to no avail.
export default class Map extends React.Component {
sendLocation(altitude, longitude) {
console.log("sending location log", this.props);
}
render() {
return (
<Button
title="Send Sonar"
onPress={(this.sendLocation, (this.props)))}
/>
);
}
}
Thanks for your help
There is a little problem here:
onPress={(this.sendLocation, () => console.log("hi", this.props))}
The console.log will trigger everytime the code renders or re-renders the button, not when you click it.
If you want to log after you call a function change the onPress to:
onPress={() => {
this.sendLocation()
console.log("hi", this.props)
}}
The other problem is that you are not giving your sendLocation function access to this.
You have two ways of doing it:
First way: Binding it inside your constructor. So inside your Map.js you add it like:
constructor(props){
super(props);
this.sendLocation.bind(this);
}
Second way: Declaring your sendLocation function as an arrow function:
sendLocation = () => {
console.log("sending location log", this.props);
}
Just as you can pass regular values as props, you can also grab data from a component’s state and pass it down as props for any of its child components. You just need to pass the exact value, also use constructor in case of class components.
`export default class Location extends Component {
constructor(props) {
super(props);
this.state = {
location: null
};
}
render() {
return (
<Map location={this.state.location} />
);
}
}`
You need to pass the function to onPress and use arrow function to be able to use this inside sendLocation.
class Map extends React.Component {
sendLocation = () => {
console.log('sending location log', this.props.locatione); // the props here appear as null
};
render() {
return (
<Button
title="Send Sonar"
onPress={this.sendLocation}
/>
);
}
}
You are passing the props through components correctly, but you should use arrow function and also anonymous func.
Try:
export default class Map extends React.Component {
sendLocation = (altitude, longitude) => {
console.log("sending location log", this.props);
}
render() {
return (
<Button
title="Send Sonar"
onPress={()=>this.sendLocation}
/>
);
}
}
I am currently working on a project where my react app connects to my own API. I've run into a problem that I can't seem to overcome. When I try to update my data I get an error saying map is not a function.
this is my vehicleOverview.js script this is supposed to show a list of all my available data which works fine until a I update a data element.
export class VehicleOverview extends React.Component{
constructor(props){
super(props);
this.state= {
vehicles: [],
}
getAll().then((response) => {
console.log(response.message);
this.setState({
vehicles: response.message,
});
}).catch((error) => {
console.error(error);
});
}
render(){
return(
<div >
<h2>Vehicle List</h2>
<ul >
{
this.state.vehicles.map((vehicle, i) => {
return(
<Vehicle key={`vehicle-${i}`} {...vehicle} />);
})
}
</ul>
</div>
);
}
}
export default VehicleOverview;
I am not sure what is going wrong because I am pretty sure vehicles is an array. Tell me if I need to provide more code to solve this problem
I noticed two things here:
Using this.setState in constructor is not recommended, because component is yet to be rendered.
Always do api calls from componentDidMount - For the reasons mentioned in this article
Also, just in case, add a condition before looping through vehicles.
import React from 'react';
export class VehicleOverview extends React.Component {
constructor(props) {
super(props);
this.state = {
vehicles: []
};
}
componentDidMount() {
getAll().then((response) => {
console.log(response.message);
this.setState({
vehicles: response.message
});
}).catch((error) => {
console.error(error);
});
}
render() {
return (
<div >
<h2>Vehicle List</h2>
<ul>
{this.state.vehicles && this.state.vehicles.map((vehicle, i) => (<Vehicle key={`vehicle-${i}`} {...vehicle} />))}
</ul>
</div>
);
}
}
export default VehicleOverview;
.map() function will work only for Arrays data.
check what typeof response you are getting.
import React, { Component } from "react";
import FormUpdate from "../components/formUpdate";
import { fetchClothingItem, updateClothingItem } from "../actions/crud";
export default class Update extends Component {
constructor(props) {
super(props);
this.state = {
updateClothingItem: {}
};
}
componentWillMount() {
fetchClothingItem(this.props.match.params.postId)
.then(data => {
this.setState(state => {
state.updateClothingItem = data;
return state;
});
console.log("data", data);
//HERE IT IS RETURNING EXPECTED DATA
console.log("this.state.updateClothingItem",this.state.updateClothingItem)
})
.catch(err => {
console.error("err", err);
});
}
handleSubmit(data) {
//HERE IT IS THROWING:
> "TypeError: Cannot read property 'state' of undefined"
console.log("this.state.updateClothingItem", this.state.updateClothingItem);
updateClothingItem(this.state.updateClothingItem.id, data); this.props.router.push("/update");
}
render() {
return (
<div>
<FormUpdate
//onSubmit={this.handleSubmit.bind(this)}
id={this.state.updateClothingItem.id}
name={this.state.updateClothingItem.name}
sleeveLength={this.state.updateClothingItem.sleeveLength}
fabricWeight={this.state.updateClothingItem.fabricWeight}
mood={this.state.updateClothingItem.body}
color={this.state.updateClothingItem.color}
/>
<button
type="submit"
onClick={this.handleSubmit}
className="addItemButton"
>
Button
</button>
</div>
);
}
}
There are a few things that are technically wrong in terms of React code implementation.
Firstly, With ES6 style of writing a class, any function that needs to access the Class properties need to be explicitly binded. In your case you need to bind the handleSubmit function using arrow function of or binding in constructor.
See this answer for more details: Why and when do we need to bind functions and eventHandlers in React?
Secondly: You have your async request set up in the componentWillMount function and in the success response of it, you are setting state. However using setState in componentWillMount is triggered after the component is rendered so you still need to have an undefined check. You should instead make use of componentDidMount lifecycle function for async requests.
Check this answer on whether to have AJAX request in componentDidMount or componentWillMount
Third: setState is asynchronous and hence logging the state values after the setState function won't result in the correct output being displayed. Use the setState callback instead.
See these answers for more details:
calling setState doesn't mutate state immediately
When to use React setState callback
Code:
export default class Update extends Component {
constructor(props) {
super(props);
this.state = {
updateClothingItem: {}
};
}
componentDidMount() {
fetchClothingItem(this.props.match.params.postId)
.then(data => {
this.setState(state => {
state.updateClothingItem = data;
return state;
});
console.log("data", data);
//HERE IT IS RETURNING EXPECTED DATA
console.log("this.state.updateClothingItem",this.state.updateClothingItem)
}) // this statement will not show you correct result since setState is async
.catch(err => {
console.error("err", err);
});
}
handleSubmit = (data) => { . // binding using arrow function here
console.log("this.state.updateClothingItem", this.state.updateClothingItem);
updateClothingItem(this.state.updateClothingItem.id, data); this.props.router.push("/update");
}
render() {
return (
<div>
<FormUpdate
//onSubmit={this.handleSubmit.bind(this)}
id={this.state.updateClothingItem.id}
name={this.state.updateClothingItem.name}
sleeveLength={this.state.updateClothingItem.sleeveLength}
fabricWeight={this.state.updateClothingItem.fabricWeight}
mood={this.state.updateClothingItem.body}
color={this.state.updateClothingItem.color}
/>
<button
type="submit"
onClick={this.handleSubmit}
className="addItemButton"
>
Button
</button>
</div>
);
}
}
You forgot to bind your handleSubmit function to the class. You can either use arrow function to define the function.
handleSubmit=(data) =>{
...
}
Or you can bind the function in your constructor.
constructor(props) {
super(props);
this.state = {
updateClothingItem: {}
};
this.handleSubmit= this.handleSubmit.bind(this,data);
}
there is no state in constructor yet
if you want to set state in constructor you can do it like this
class SomeComponent extends Component {
constructor(props){
super(props)
this.state = { someKey: someValue }
}
}
or even like this
class SomeComponent extends Component {
state = { someKey: someValue }
}
but in this case babel should be properly configured
My parent component is like this:
export default class MobileCompo extends React.Component {
constructor(props) {
super(props);
this.state = {
data: null,
datasets: {}
};
this.get_data = this.get_data.bind(this);
}
componentWillMount() {
this.get_data();
}
async get_data() {
const ret = post_api_and_return_data();
const content={};
ret.result.gsm.forEach((val, index) => {
content[val.city].push()
});
this.setState({data: ret.result.gsm, datasets: content});
}
render() {
console.log(this.state)
// I can see the value of `datasets` object
return (
<div>
<TableElement dict={d} content={this.state.data} />
<BubbleGraph maindata={this.state.datasets} labels="something"/>
</div>
)
}
}
child component:
export default class BubbleGraph extends React.Component {
constructor(props) {
super(props);
this.state = {
finalData: {datasets: []}
};
console.log(this.props);
// here I can't get this.props.maindata,it's always null,but I can get labels.It's confusing me!
}
componentWillMount() {
sortDict(this.props.maindata).forEach((val, index) => {
let tmpModel = {
label: '',
data: null
};
this.state.finalData.datasets.push(tmpModel)
});
}
render() {
return (
<div>
<h2>{this.props.labels}</h2>
<Bubble data={this.state.finalData}/>
</div>
);
}
}
I tried many times,but still don't work,I thought the reason is about await/async,but TableElement works well,also BubbleGraph can get labels.
I also tried to give a constant to datasets but the child component still can't get it.And I used this:
this.setState({ datasets: a});
BubbleGraph works.So I can't set two states at async method?
It is weird,am I missing something?
Any help would be great appreciate!
Add componentWillReceiveProps inside child componenet, and check do you get data.
componentWillReceiveProps(newProps)
{
console.log(newProps.maindata)
}
If yes, the reason is constructor methos is called only one time. On next setState on parent component,componentWillReceiveProps () method of child component receives new props. This method is not called on initial render.
Few Changes in Child component:
*As per DOC, Never mutate state variable directly by this.state.a='' or this.state.a.push(), always use setState to update the state values.
*use componentwillrecieveprops it will get called on whenever any change happen to props values, so you can avoid the asyn also, whenever you do the changes in state of parent component all the child component will get the updates values.
Use this child component:
export default class BubbleGraph extends React.Component {
constructor(props) {
super(props);
this.state = {
finalData: {datasets: []}
};
}
componentWillReceiveProps(newData) {
let data = sortDict(newData.maindata).map((val, index) => {
return {
label: '',
data: null
};
});
let finalData = JSON.parse(JSON.stringify(this.state.finalData));
finalData.datasets = finalData.datasets.concat(data);
this.setState({finalData});
}
render() {
return (
<div>
<h2>{this.props.labels}</h2>
<Bubble data={this.state.finalData}/>
</div>
);
}
}
I'd like to trigger the function handleDisplayProduct on click and pass to it the object clicked. So far it calls the function handleDisplayProduct when the list is generated for all the objects but the function is not triggered on the click event.
So how do i bind the event onclick with the Container and passing to it the element clicked?
Container
// Method to retrieve state from Stores
function getAllProductState(){
return {
products: ProductStore.getProducts(),
};
}
export default class ProductAllContainer extends Component {
constructor(props){
super(props);
this.state = getAllProductState();
}
handleDisplayProduct(data){
console.log(data);
// ProductActions.selectProduct(data)
}
render(){
const products = this.state.products;
return(
<div>
{ products.map(function(product,i){
return (
<ProductThumbnail
product = { product }
key = { i }
**onDisplayProduct = { this.handleDisplayProduct(product) }**
/>
)
},this)}
</div>
)
}
}
View
const ProductThumbnail = (props)=>{
return(
<div>
<LinksTo to="/" **onClick={props.onDisplayProduct}**>
<h1>{props.product.headline}</h1>
<img src={props.product.images[0].imagesUrls.entry[1].url} alt="Thumbnail small pic"/>
</LinksTo>
</div>
)
}
You need to bind the event listener to the react class. You can do it this way.
constructor(props){
super(props);
this.state = getAllProductState();
this.handleDisplayProduct = this.handleDisplayProduct.bind(this);
}
or alternatively using es6, you can use an arrow function instead.
handleDisplayProduct = (data) => {
console.log(data);
// ProductActions.selectProduct(data)
}
Note: Class properties are not yet part of current JavaScript standard. So the second example wouldn't work unless you add a babel-plugin-transform-class-properties babel plugin
Edit: Also #ryanjduffy pointed out a crucial mistake in your code. Refer his comment.