Overwriting state when loaded from props - reactjs

I have this code, or the relevant parts at least....
class ClubDetails extends React.Component {
constructor(props) {
super(props)
this.state = {
clubBetsPending: this.props.pendingBets,
}
this.updatePbd = this.updatePbd.bind(this)
}
static async getInitialProps({ query }) {
const props = axios
.post('http://localhost:3000/api/club', { clubId: query.clubid })
.then((res) => {
//pass response to UI
if (res.data.response.boolean) {
return {
pendingBets: res.data.payload,
}
}
})
.catch((e) => {
//Not Found
})
return props
}
updatePbd(newBet) {
var newPbd = [newBet[0]].concat(this.state.clubBetsPending)
this.setState({ clubBetsPending: newPbd })
}
render() {
return ( <>
<NewBet updatePbd={this.updatePbd} />
<PendingBets data={this.state.clubBetsPending} />
</>
)
}
From what I have read my render isn't running when it hits the setState in updatePbd because the state is being recieved from the props this.props.pendingBets. So I understand the problem, i just don't understand what the solution is?
Can someone help, I can't find anything for this problem or I don't know what to search for.
Thanks

Related

How can I chain asynchronous Firebase updates in my React app?

React & Firebase newbie here. I have a React component that needs to look up some stuff in Firebase before rendering. My database design requires first getting the correct doohick ids and subsequently looking up the doohick details, but I'm not sure how to do that with the asynchronous nature of Firebase database access. This doesn't work:
class Widget extends React.Component {
componentDidMount() {
firebase.database().ref(`/users/${username}/doohick-ids`).on('value', snapshot => {
this.setState({doohick_ids: doohick_ids});
});
this.state.doohick_ids.forEach(id => {
// ids don't actually exist at this point outside the callback
firebase.database().ref(`/doohick-details/${id}`).on('value', snapshot => {
// update state
});
});
render() {
if (this.state.doohick-ids) {
return null;
} else {
// render the Doohick subcomponents
}
}
}
I can think of a few solutions here, but none that I like. What's the recommended way to chain together Firebase calls, or perhaps redesign this to eliminate the problem?
I think you should split one component Widget to two WidgetList and WidgetItem.
WidgetItem
subscribe and unsubscribe to firebase.database().ref(/doohick-details/${id})
class WidgetItem extends React.Component {
static propTypes = {
id: PropTypes.string.isRequired,
}
constructor(props) {
super(props);
this.state = {};
this.dbRef = null;
this.onValueChange = this.onValueChange.bind(this);
}
componentDidMount() {
const { id } = this.props;
this.dbRef = firebase.database().ref(`/doohick-details/${id}`);
this.dbRef.on('value', this.onValueChange);
}
componentWillUnmount() {
this.dbRef.off('value', this.onValueChange);
}
onValueChange(dataSnapshot) {
// update state
this.setState(dataSnapshot);
}
render() {
return (
<pre>{JSON.stringify(this.state, null, 2)}</pre>
);
}
}
WidgetList
subscribe and unsubscribe to firebase.database().ref(/users/${username}/doohick-ids)
class WidgetItem extends React.Component {
constructor(props) {
super(props);
this.state = { doohick_ids: [] };
this.dbRef = null;
this.onValueChange = this.onValueChange.bind(this);
}
componentDidMount() {
// Note: I've just copied your example. `username` is undefined.
this.dbRef = firebase.database().ref(`/users/${username}/doohick-ids`);
this.dbRef.on('value', this.onValueChange);
}
componentWillUnmount() {
this.dbRef.off('value', this.onValueChange);
}
onValueChange(dataSnapshot) {
this.setState({ doohick_ids: dataSnapshot });
}
render() {
const { doohick_ids } = this.state;
if (doohick_ids.length === 0) {
return 'Loading...';
}
return (
<React.Fragment>
{doohick_ids.map(id => <WidgetItem key={id} id={id} />)}
</React.Fragment>
);
}
}
And code that requires the data from the database needs to be inside the callback that is invoked when that data is available. Code outside of the callback is not going to have the right data.
So:
firebase.database().ref(`/users/${username}/doohick-ids`).on('value', snapshot => {
this.setState({doohick_ids: doohick_ids});
doohick_ids.forEach(id => {
// ids don't actually exist at this point outside the callback
firebase.database().ref(`/doohick-details/${id}`).on('value', snapshot => {
// update state
});
});
});
There's many optimizations possible here, but they all boil down to the code being inside the callback and updating the state when a value comes from the database.

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.

Rendering a new component inside componentDidMount - React

I will have to render a new component after all the expected components are loaded. I will need a timeout based on which the the new component has to be rendered. So this new component has to show up after 5 minutes after the page has loaded.
I need to render a component called new_component that extends React.component
public componentDidMount(): void {
if (visited) {
setTimeout(() => {
console.log('Reached the timeout')
//Render the new conponent here. (Not sure how to call the render function of another component here)
}, timeout);
}
Can someone help me call the render function of new_component inside componentDidMount please. i tried new_component.render(). But that does not seem to work.
You can use state to track this.
componentDidMount() {
setTimeout(() => {
this.setState({ showNewComponent: true })
})
}
and in render:
render() {
if (this.state.showNewComponent) {
return <NewComponent />
}
return null
}
You can go with this code, wait and then render new one:
cosnt FIVE_MIN = 5 * 60 * 1000
class Example {
this.state = { isShowComponent: false }
timer
componentDidMount() {
this.timer = setTimeout(() => {
this.setState({ isShowComponent: true })
}, FIVE_MIN)
}
componentWilllUnmount() {
clearTimeout(this.timer)
}
render() {
if (this.state.isShowComponent) return <NewComponent />
return <Component />
}
}
:)
you can render your component by your state.
class Foo extends React.Component {
constructor(props) {
super(props);
this.state = {
isTimeout: false,
};
}
componentDidUpdate(prevProps, prevState) {
this.checkTimeout = setTimeout(() => {
this.setState(() => ({isTimeout: true}))
}, 500);
}
componentWillUnmount() {
// clean it up when the component is unmounted.
clearTimeout(this.checkTimeout);
}
render () {
if (isTimeout) {
return (k<h1>time is running out</h1>)
}
return (<h1>hello world.</h1>)
}
}

ReactJS - Pass Updated Value To Sub-Component Method

I'm working on an environment that is basically set up with a Main Component like this:
class MainComponent extends Component {
constructor(props) {
super(props);
this.state = {
selectedValues: []
};
}
render() {
const { selectedValues } = this.state;
return (
// Other components
<SubComponent selectedValues = {selectedValues} />
// Other components
);
}
}
export default MainComponent;
And a Sub Component like this:
class SubComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
isExporting: false,
selectedValues: props.selectedValues
};
}
performTask = () => {
this.setState({ isWorking: true });
const { selectedValues } = this.state;
console.log(`Selected Values: ${selectedValues}`);
fetch('/api/work', {
method: 'GET'
})
.then(res => res.json())
.then((result) => {
// Handle the result
this.setState({ isWorking: false });
})
.catch((error) => {
console.log(error);
this.setState({ isWorking: false });
});
};
render() {
const { isWorking } = this.state;
return (
<Button
bsStyle="primary"
disabled={isWorking}
onClick={() => this.performTask()}
>
{isWorking ? 'Working...' : 'Work'}
</Button>
);
}
}
SubComponent.propTypes = {
selectedValues: PropTypes.arrayOf(PropTypes.string)
};
SubComponent.defaultProps = {
selectedValues: []
};
export default SubComponent;
In the Main Component, there are other components at work that can change the selectedValues. The functionality I'd like to see is that when the performTask method fires, it has the most recent and up to date list of selectedValues. With my current setup, selectedValues is always an empty list. No matter how many values actually get selected in the Main Component, the list never seems to change in the Sub Component.
Is there a simple way to do this?
I would suggest you 2 of the following methods to check this problem:
Maybe the state.selectedItems doesn't change at all. You only declare it in the contractor but the value remains, since you didn't setState with other value to it. Maybe it will work if you will refer to this.props.selectedItems instead.
Try to add the function component WillReceiveProps(newProps) to the sub component and check the value there.
If this method doesn't call, it means the selectedItems doesnt change.
Update if some of it works.
Good luck.
selectedValues in SubComponent state has not updated since it was set in SubComponent constructor. You may need to call setState again in componentWillReceivedProps in SubComponent

How to pass function as props in React?

I have functional component GetWeather which I want to pass result of GetLocation function as props based on which GetWetaher will do something i.e. another get request (in the example below it only renders its props). I think it has to happen inside ComponentDidMount, not sure how to do it
function GetLocation() {
axios.get('http://ipinfo.io')
.then((res) => {
return res.data.loc;
})
}
function GetWeather(props) {
//more code here, including another get request, based on props
return <h1>Location: {props.location}</h1>;
}
class LocalWeather extends Component {
componentDidMount() {
//???
}
render() {
return (
<div >
<GetWeather location={GetLocation}/> //???
</div>
);
}
}
Update: So based on suggestion from Damian below is working for me
function GetWeather(props) {
return <h3>Location: {props.location}</h3>;
}
class LocalWeather extends Component {
constructor(props) {
super(props);
this.state = {
location: []
};
}
getLocation() {
axios.get('http://ipinfo.io')
.then((res) => {
this.setState({location:res.data.loc});
})
}
componentDidMount() {
this.getLocation();
}
render() {
return (
<div >
<GetWeather location={this.state.location}/>
</div>
);
}
}
You can do it alternatively also
constructor() {
super();
this.state = {
Location:[]
}
}
function GetLocation() {
axios.get('http://ipinfo.io').then((res) => {
this.setState ({
Location:res.data.loc;
});
});
}
function GetWeather(props) {
return <h1>Location: {this.props.location}</h1>;
}
class LocalWeather extends Component {
componentDidMount() {
//code
}
render() {
return (
<div >
<GetWeather location={this.GetLocation.bind(this)}/>
</div>
);
}
}

Resources