react - handling promise without "ReduxPromise" - reactjs

I'm trying to handle a promise without using "ReduxPromise".
return (
<div>
<div>{this.props.weatherData.then(response => {return response.data.city.name})}</div>
</div>
);
But I get this error :
bundle.js:1212 Uncaught Error: Objects are not valid as a React child (found: [object Promise]).
If I console.log(response.data.city.name), I do get a resulted string.
With ReduxPromise, I just have to do this :
const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);
Then I only have to return this in the component :
<div>{this.props.weatherData.data.city.name}</div>
But I want to do it WITHOUT ReduxPromise
Without ReduxPromise, this.props.weatherData IS a promise. How do I handle this in the component ?
Doing this below does an infinite loop :
render(){
if (!this.props.weatherData) {
return <div></div>
};
this.props.weatherData.then(response => {
this.setState({ weatherData: response.data });
console.log(this.state.weatherData.city.name);
});
City name outputted infinitely.

You are attempting to render the return value of this.props.weatherData.then(...) in the jsx, which would be a promise, which is what the error message is telling you. Instead, fetch the data, put it in state, and render based off of that state.
Example:
class WeatherDataComp extends React.Component {
state = {
weatherData: null
}
componentDidMount() {
this.props.weatherData.then(response => {
this.setState({ weatherData: response.data })
})
}
render() {
if (!this.props.weatherData) {
return null
}
return (
<div>{this.state.weatherData.city.name}</div>
)
}
}

Thanks to #TLadd, I needed a lifecycle method, namely componentWillReceiveProps :
componentWillReceiveProps(nextProps) {
if (!nextProps.weatherData) {
return;
};
nextProps.weatherData.then(response => {
this.setState({ city: response.data.city.name });
});
}
Then :
render(){
if (!this.props.weatherData) {
return <div></div>
};
return (
<div>
<div>{this.state.city}</div>
</div>
);
}
And for the infinite loop, I'm pretty it's because of this :
setState causes the component to re-render, then the .then callback is called, so setState again, then re-render, etc.

Related

Error: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application

I'm getting the above error and I don't know how to handle it.
I got a component. And in the render() i'm looping through an array and placing another component and parsing a value to that component like this:
render() {
let allProducts = this.state.products.map((product, i) => {
return (
<div key={product.article}>
...
<PriceStock value={product.article} />
...
</div>
)
})
}
In the PriceStock component i'm fetching some data with axios like the code below:
export default class PriceStock extends React.Component {
constructor(props) {
super(props);
this.state = ({
buttoprice: ''
})
this.getPriceAndStock = this.getPriceAndStock.bind(this)
}
getPriceAndStock(articleNo) {
return axios.post('LINK_TO_URL', {
articleNo: articleNo
}).then(result => {
return result.data
})
}
async componentDidMount() {
let pricestock;
pricestock = await this.getPriceAndStock(this.props.value)
let bruttoPrice = PRICE_TO_PARSE_TO_THE_STATE;
this.setState({ buttoprice: bruttoPrice })
}
render() {
return (
<div >
{this.state.buttoprice}
</div>
);
}
}
The error seems to happen when I try to setState in the componentDidMount, any suggestions?
this is an error occurs because you are updating state before it gets initialized
perform your loading activities in the constructor it is the right way to do it
getPriceAndStock(orderNumber, articleNo) {
return axios.post('LINK_TO_URL', {
orderNr: orderNumber, vareNr: articleNo
}).then(result => {
return result.data
})
}
constructor() {
this.getPriceAndStock(this.props.value)
.then(pricestock=>{
let bruttoPrice = PRICE_TO_PARSE_TO_THE_STATE;
this.state({ buttoprice: bruttoPrice })
})
.catch(console.log)
}
Found the answear in this question: https://github.com/material-components/material-components-web-react/issues/434
It's remindend me a little bit about the comment with another stackoverflow question.

Update App state based on Child component

I need to update the state of a Parent component (App.js) when updating the state of a Child component where I do a GET request in componentDidMount().
I tried to pass the function setPoints as a prop but unfortunately this doesn't work.
Here's what I tried :
Child component :
class Child extends Component {
state = {
points: null
}
async componentDidMount() {
try {
const res = await axios.get(url);
const data = res.data;
this.setState({
points: data.points,
})
this.props.setPoints();
} catch (err) {
console.log('child', err);
}
}
render() {
return (
<div>
</div>
);
}
}
Parent component (App):
class App extends Component {
state = {
points: '',
}
setPoints() {
this.setState({
...this.state,
points: this.state.points
});
}
shouldComponentUpdate(nextState) {
if (this.state.points !== nextState.points) {
return true;
}
return false;
}
render() {
return (
<div className="App">
<Route exact path="/child" render={() => <Child setPoints={this.setPoints} />} />
</div>
);
}
}
Can anyone please help me with this? Help would be highly appreciated.
EDIT
I tried what Joe Clay wrote and this makes perfect sense, but I still catch an error. Here's my updated code :
async componentDidMount() {
try {
const res = await axios.get(url);
const data = res.data;
console.log(data.points);
this.props.setPoints(data.points);
} catch (err) {
console.log('child', err);
}
It does log the value of points, but for some reason I get : "Cannot read property 'points' of undefined".
Consider the value of data in Parent Component.I would be changing the value of data by passing a prop to a function which does a setState in the parent and thereby changing the value to the desired value.
ParentComponent
class Parent extends React.Component{
constructor(props){
super(props);
this.state({
name:''
})
}
handleChange(value){
this.setState({name:value});
}
render(){
return (<Child handleChange={this.handleChange} />)
}
}
Pass the handleChange function as a prop to the child component.
Child component
export default class Child extends React.Component{
sendData(false){
this.props.handleChange(hello) //make sure to pass the value in the
argument that you wish to do a setState on in the parent component
}
}
this would set the value of name to hello in the parent component.
on child:
this.props.setPoints(data.points)
on parent:
setPoints(points) {
this.setState({
...this.state,
points
});
}

React TypeError: this.setState is not a function despite having bound handler

Before flagging this as a duplicate of React this.setState is not a function, I have seen and read that.
My problem is that I'm getting this error message even though I've bound the handler.
class EditAccount extends Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
state = {
showForm: false
}
componentWillMount() {
this.setState = {
showForm: false
}
}
render() {
return (
<div>
<button onClick={this.onClick}>Edit</button>
{this.state.showForm ? ( <Stuff/> ): null }
</div>
)
}
//toggle form visibility
onClick(e) {
const showing = this.state.showForm;
if (showing) {
this.setState({ showForm: false });
} else {
this.setState({ showForm: true });
}
}
}
Any ideas?
componentWillMount() is invoked just before mounting occurs. It is called before render(), therefore calling setState() synchronously in this method will not trigger an extra rendering.
The reason to not use setState inside compomentWillMount,
React will use the initial state value from constructor or initialized default state for the first render instead of re-render. It not wait for componentWillMount to complete setState call asynchronously.
So,there is no point in making setState call inside componentWillMount. It's nothing more than that state handler processing which do nothing when setState is called.
The issue is in your componentWillMount. You are changing setState to no longer be a function but rather an object with the value showForm. You shouldn't be setting state in will mount as react advises against it. Drop that whole function and the code will work as you expect.
meaning this.setState = { showForm: false } is changing setState to be the object { showForm: false } instead of a function. So yes your error message is correct in that setState is not a function
Try dropping the whole componentWillMount function
class EditAccount extends Component {
constructor(props) {
super(props);
this.onClick = this.onClick.bind(this);
}
state = {
showForm: false
}
render() {
return (
<div>
<button onClick={this.onClick}>Edit</button>
{this.state.showForm ? ( <Stuff/> ): null }
</div>
)
}
//toggle form visibility
onClick(e) {
const showing = this.state.showForm;
if (showing) {
this.setState({ showForm: false });
} else {
this.setState({ showForm: true });
}
}
}
some optimizations.. you should be able to clean up the code a bit
class EditAccount extends Component {
state = {
showForm: false
}
render() {
return (
<div>
<button onClick={this.handleClick }>Edit</button>
{this.state.showForm ? <Stuff/> : null }
</div>
)
}
handleClick = (e) => {
this.setState((old) => ({ showForm: !old.showForm }) )
}
}

Cannot read property 'map' of undefined with REACTJS

I am new with reactjs.
This is what I am trying
class EventDemo extends Component {
constructor(){
super()
this.getStarWars()
this.state = {}
}
getStarWars = ()=> axios.get('https://swapi.co/api/people')
.then(res => {
console.log(res.data)
this.setState({
names: res.data.results
})
})
render() {
console.log(this.state.names);
return (
<div>
{this.state.names.map(function(e){
return <li>{e.name}</li>
})}
</div>
);
}
}
But This following error i am getting
What I am doing wrong here ? It supposed to work .
First of all,you shouldn't call your this.getStarWars() function inside the constructor, it is a very bad practice and could cause you troubles, http calls in React component should be generally called from the componentDidMount function.
However the issue in this case is another one,you haven't given an initial value to this.state.names, so when the component tries to do the initial render it fails because the names are undefined since the initial render appens before the http call is resolved
You code should be fixed like this:
class EventDemo extends Component {
constructor(){
super()
this.state = { names:[] }
}
componentDidMount(){
this.getStarWars()
}
getStarWars = ()=> axios.get('https://swapi.co/api/people')
.then(res => {
console.log(res.data)
this.setState({
names: res.data.results
})
})
render() {
console.log(this.state.names);
return (
<div>
{this.state.names.map(function(e){
return <li>{e.name}</li>
})}
</div>
);
}
}

React render nested component

In the return method of my react component I want to have check to async function and return only if satisfied. This is my code:
render() {
var data = new MyClass()
data.helper(function(response){
if(response.status === "authorised"){
return (
<div>
<List videos={videos}/>
</div>
)
}else{
return (
<div>
<p>Please wait</p>
</div>
)
}
})
}
But this way it is giving me error saying:
A valid react component must be returned. You are returning either array or list or undefined
I want to show the data only after my logic.
I suggest moving the AJAX call to the componentDidMount lifecycle method so the request fires when the DOM node is mounted, and then conditionally setting an authorised property on the state, contingent on a successful response. Then use this state property to conditionally render your different UI states in the render method:
class MyComponent extends React.Component {
constructor() {
super();
this.state = { authorised: false };
}
componentDidMount() {
var data = new MyClass();
data.helper((response) => {
if (response.status === "authorised") {
this.setState({ authorised: true })
}
});
}
render() {
if (this.props.authorised) {
return (
<div>
<List videos={videos}/>
</div>
);
}
return (
<div>
<p>Please wait</p>
</div>
);
}
}

Resources