How to stop subscribing store updates? - javascript

I move user from component1 to component2 by this.props.history.push. When I am in component2 it looks like component1 is still mounted.
I tried everything as in similar problems with unsubscribe store.
import React, {Component} from 'react';
import axios from 'axios';
import {Animate} from 'react-animate-mount';
import {translateTexts} from "../App";
import store from "../../store";
class Component1 extends Component {
constructor(props) {
super(props);
this.state = {
texts: {
computing: null
},
};
}
componentWillUnmount() {
return store.subscribe(() => {
console.log('unmount');
})
}
componentWillMount() {
store.subscribe(() => {
translateTexts(this.state.texts),store.getState().translate.userLanguage).then((res) => {{
this.setState({texts: res.data});
});
});
axios.post(`/api/`)
.then((res) => {
this.setState({show: false});
setTimeout(() => {
window.location.href = '/storage/'+res.data.id
}, 600);
})
.catch((err) => {
this.props.history.push({
pathname: '/error/'+err.response.status,
state: { code: err.response.status }});
});
});
render() {
return (
<div className="box">
Something
</div>
);
}
}
export default Component1;
import React, { Component } from 'react';
import store from "../../store";
import {translateTexts} from "../App";
class Component2 extends Component {
constructor(props) {
super(props);
this.state = {
code : null,
texts: {
msg: null
}
};
}
componentWillMount() {
this.setState( {
code: this.props.location.state.code,
});
store.subscribe(() => {
translateTexts(this.state.texts),store.getState().translate.userLanguage).then((res) => {{
this.setState({texts: res.data});
});
});
}
render() {
return (
<div>
Code: {this.state.code}
<br></br>
Message: <strong>{this.state.texts.msg}</strong>
</div>
);
}
}
export default Component2;
After componentWillUnmount I would to stop subscribing store events in component, because now when my store updates I see log unmount in my console, so Component1 is somehow mounted.
Thanks for any suggestions.

Store subscribe should return the unsubscribe function which you can call at componentWillUnmount to successfully unsubscribe from the store.
you can define and store it in state directly in componentWillMount:
this.setState({
unsubscribe: store.subscribe(() => {
translateTexts(this.state.texts),store.getState().translate.userLanguage).then((res) => {{
this.setState({texts: res.data});
});
});
});
and then call unsubscribe on componentWillUnmount
componentWillUnmount() {
this.state.unsubscribe()
}
Redux docs: https://redux.js.org/api/store#subscribe

If you're using React and Redux together, you really shouldn't be interacting with the store directly in your component. Instead, you should be using the official React-Redux library, which handles all the subscription and store update process for you. See the docs page on Why Use React Redux? for more details.

Related

How do i put entities into props after fetching it from componetDidMount?

inside componentDidMount Im calling the dispatched fetchReviews. but once mounted how does the fetched reviews get set in props?
business show component:
import React from "react";
import { Link } from 'react-router-dom';
import Star from "../star/star";
class BusinessShowIndex extends React.Component {
constructor(props) {
super(props)
this.state = {
loading: true
}
}
componentDidMount() {
this.props.fetchReviews(this.props.business.id)
// .then (() => this.setState({reviews: this.props.reviews}))
this.setState({loading: false})
console.log(this.props)
}
render() {
const { business, reviews } = this.props;
if (!this.props.reviews) return null;
if (this.state.loading === true) {
return <p>Loading...</p>
}
return (
<div className="business-show">
<Link to={`/business/${business.id}`} className='link-business-index'>
<img className="business-index-photo" src={business.photo_urls[0]} alt=""/>
<p className="business-index-name">{business.name}</p>
<Star reviews={reviews}/>
<p className="business-index-city">{business.city}</p>
<p className="business-index-cost">Cost: {business.cost}</p>
<p className="business-index-hours">Hours: {business.open} - {business.close}</p>
</Link>
</div>
)
}
};
export default BusinessShowIndex;
container:
import { connect } from 'react-redux';
import {fetchReviews} from '../../actions/review_actions';
import { withRouter } from 'react-router-dom';
import BusinessShowIndex from './business_show_index';
const mapStateToProps = (state, ownProps) => ({
business: state.entities.businesses[ownProps.business.id],
currentUser: state.entities.users[state.session.id],
reviews: Object.values(state.entities.reviews)
})
const mapDispatchToProps = dispatch => ({
fetchReviews: (businessId) => dispatch(fetchReviews(businessId))
})
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(BusinessShowIndex));
let me know what else you need to see! thank you!
also any advice to clean code? taking any suggestions.
Inside componentDidMount Im calling the dispatched fetchReviews. but once mounted how does the fetched reviews get set in props?
You can get using this.props.review
Check your mapStateToProps function. You get the entire state there and it returns whatsever part of it you want to return.

My React function being passed down as props is not being invoked

I am trying to re-render the page based on a button click. I have the function updateCowList which calls setState() in my app component. The handleClick logic is in my newCow component which handles the button and the text input.
The console.logs() that I am seeing are 'fire', but I am not seeing the 'after' console.log(), nor am I seeing any of the logs within my updateCowList function in App.
How can I get my updateCowList function to run? I have tried calling it in all sorts of ways, destructuring props, etc.
Here is my App:
import React from 'react';
import CowList from './CowList.jsx';
import CowListEntry from './CowListEntry.jsx';
import axios from 'axios';
import SearchDB from './searchDB.js';
import NewCow from './NewCow.jsx';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
cows: []
}
// this.updateCowList = this.updateCowList.bind(this);
}
componentDidMount() {
SearchDB()
.then((res) => {
this.setState({cows: res.data})
}, (err) => {
console.log(err);
});
}
updateCowList(cow) {
console.log('update cow list is running')
oldCows = [...this.state.cows];
newCows = oldCows.push(cow);
console.log('new cows be4 set state', newCows);
this.setState({cows: newCows});
console.log('new cows after set state', newCows);
}
render() {
return (
<div>
<CowList cows={this.state.cows}/>
<NewCow props={this.updateCowList}/>
</div>
)
}
}
export default App;
here is my NewCow component:
import React from 'react';
import axios from 'axios';
class NewCow extends React.Component {
constructor(props) {
super(props);
this.state = {
entry: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleClick = this.handleClick.bind(this);
}
handleClick () {
let split = this.state.entry.split(', ')
console.log(split)
axios.post('http://localhost:3000/api/cows', {
name: split[0],
description: split[1]
})
.then(res => { console.log('fire', res.data);
this.props.updateCowList(res.data);
console.log('after')
})
.catch(err => 'error submitting cow :( mooooo');
}
handleChange (event) {
this.setState({entry: event.target.value})
}
render () {
return (
<div className='newCowForm'>
<input className='form-control' type='text' onChange={this.handleChange} value={this.state.entry} placeholder={'name, description'} />
<button onClick={this.handleClick} className='newCowButton'>Create new cow</button>
</div>
)
}
}
export default NewCow;
<NewCow props={this.updateCowList}/>
should be :
<NewCow updateCowList={this.updateCowList}/>

index.js:1 Warning: Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application

This was my code
import React, { Component } from "react";
import axios from "axios";
class App extends Component {
state = {
invites: [],
};
constructor() {
super();
axios.get(`http://localhost:8080/security/allUser`).then((res) => {
console.log(res.data);
this.setState({ invites: res.data });
});
}
render() {
return (
<div>
{this.state.invites.map((invite) => (
<h2 key={invite.id}>{invite.name}</h2>
))}
<h1>Welcome</h1>
</div>
);
}
}
export default App;
state and setState have worked for me alright for more complex codes before. This one keeps showing the same error
This is the error:
index.js:1 Warning: Can't call setState on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to this.state directly or define a state = {}; class property with the desired state in the App component.
Add a componentDidMount() and write your request call inside it. When the component first loads the componentDidMount function will run.
Actually you can make request in constructor (React allows it but you shouldnt) but you have to make the request only after the component has been mounted or just before it is about to be mounted.
So it is wise to make your requests in componentDidMount().
import React, { Component } from "react";
import axios from "axios";
class App extends Component {
constructor(props) {
super(props);
this.state = {
invites: [],
};
}
componentDidMount() {
axios.get(`http://localhost:8080/security/allUser`).then((res) => {
console.log(res.data);
this.setState({ invites: res.data });
});
}
render() {
return (
<div>
{this.state.invites.map((invite) => (
<h2 key={invite.id}>{invite.name}</h2>
))}
<h1>Welcome</h1>
</div>
);
}
}
export default App;

How to set state in componentDidMount based on the response from a callback

I am basically trying to set the state based on the response I got from an API.
api.js
const baseURL = "http://localhost:8000";
export const getStatus = (list) => {
fetch(`${baseURL}/api/status`).then(res => {
return res.json();
}).then(status => {
list.setState({status: status});
});
};
And this is how I call it from a component
import React, {PropTypes} from 'react';
import {getStatus} from '../../../api';
class List extends React.Component {
constructor(props) {
super(props);
this.state = {
status: [],
};
}
componentDidMount() {
getStatus(this);
}
I feel like it is not a good practice to pass this down and modify the state from the downstream api file. Is there a more "react" way to do this?
I also tried another way, which is to wait for the callback to send back the response and then modify the state based on the response, but the setState function never gets executed in componentDidMount. If someone can direct me, that would be great!
api.js
const baseURL = "http://localhost:8000";
export const getStatus = () => {
fetch(`${baseURL}/api/status`).then(res => {
return res.json();
}).then(status => {
return status;
});
};
import React, {PropTypes} from 'react';
import {getStatus} from '../../../api';
class List extends React.Component {
constructor(props) {
super(props);
this.state = {
status: [],
};
}
componentDidMount() {
getStatus((status) => {
this.setState({status: status});
})
}
Better way is to use .then() in componentDidMount
api.js
export const getStatus = () => {
return fetch(`${baseURL}/api/status`).then(res => {
return res.json();
});
};
yourComponent.jsx
import React, {PropTypes} from 'react';
import {getStatus} from '../../../api';
class List extends React.Component {
constructor(props) {
super(props);
this.state = {
status: [],
};
}
componentDidMount() {
getStatus()
.then(status => {
this.setState({status: status});
})
}
Making an API call and setting the state of a component based on what the call returns is normal practice in react. You don't need to pass a reference to this down to getStatus and for that matter you don't need to pass anything to getStatus. Instead, chain then off of what is returned from getStatus.
componentDidMount() {
getStatus()
.then(status => {
this.setState({status});
})
}
it is also unnecessary to call constructor or super in your component. Simply write:
class List extends React.Component {
state = {
status: []
}
}
If you are using ES6, try the async function syntax to increase readability.
api.js
export const getStatus = () => fetch(`${baseURL}/api/status`);
yourComponent.jsx
import React, {PropTypes} from 'react';
import {getStatus} from '../../../api';
class List extends React.Component {
constructor(props) {
super(props);
this.state = {
status: [],
};
}
async componentDidMount() {
const res = await getStatus()
this.setState({status: res.json()});
}
Also, you might not need to initialize the state, and you could remove the constructor if so.
I have a working code:
fetch(serviceUrl)
.then(result => result.json())
.then(newStatus => this.setState({status: newStatus}))
.catch(error => {
console.log(error);
this.setState({status: 'error'});
})

Using .map() to render Component UI with information from API

I'm currently having a problem trying to get UI to render with React. I'm using information I've received from ShopifyAPI and trying to render it to my component. I'm not sure what to do. Do I need to update the state with information returned from my API? Here's my code at the moment.
ShopifyCatalog.js
import React, { Component } from 'react';
import { Link } from 'react-router'
import styles from '../styles';
import ShopProducts from './ShopProducts'
import { getAllProducts } from '../utils/shopifyHelpers';
export default class ShopCatalog extends Component {
constructor(...args){
super(...args);
this.state = {
allProducts: []
}
}
render() {
let allProducts
getAllProducts()
.then((products) => {
return allProducts = products
})
.then((allProducts) => {
allProducts.map((product) => {
<div className='col-sm-offset-1 col-sm-2'>
<Link to={'shop/${product.id}'}>
<img src={product.images[0].src} />
<h5>{product.title}</h5>
</Link>
</div>
})
})
return (
<div style={styles.productInfo}>
{allProducts}
</div>
)
}
}
I thought it might have something to do with using promises more extensively, but I'm pretty sure it's because my state isn't updating with the information that I'm grabbing from the API. I appreciate your time, thank you.
EDIT:
I've updated my code now and it looks like this
ShopCatalog.js Updated
import React, { Component } from 'react';
import { Link } from 'react-router'
import styles from '../styles';
import ShopProducts from './ShopProducts'
import { getAllProducts } from '../utils/shopifyHelpers';
export default class ShopCatalog extends Component {
constructor(...args){
super(...args);
this.state = {
allProducts: [],
listAllProducts: []
}
}
componentDidMount() {
getAllProducts()
.then((products) => {
this.setState({
allProducts: products
})
})
}
render() {
return (
<div style={styles.productInfo}>
{this.state.allProducts.map((product) => {
<h1>{product.title}</h1>
})}
</div>
)
}
}
But it's still not rendering anything from the map of my state. Is it because map is called while there is nothing in the state? How do I work around this so map get's called and returns UI? Thank you.
Put your request in the componentDidMount lifecycle method, then update your state. Your render method is returning before your request has completed.
export default class ShopCatalog extends Component {
constructor(...args){
super(...args);
this.state = {
allProducts: []
}
}
componentDidMount() {
const _this = this;
getAllProducts()
.then((products) => {
_this.setState({ allProducts: products });
});
}
render() {
return (
<div style={styles.productInfo}>
{this.state.allProducts.map((product) => {
<div className='col-sm-offset-1 col-sm-2'>
<Link to={'shop/${product.id}'}>
<img src={product.images[0].src} />
<h5>{product.title}</h5>
</Link>
</div>
})}
</div>
)
}
}
I assume something like this, not sure specifics to your case, just giving idea how this should look like.
export default class ShopCatalog extends Component {
state = {
allProducts: []
}
getAllProducts = () => {
fetch(...API).then(response => response.json()).then(products =>
this.setState({allProducts: products}));
}
componentDidMount() {
this.getAllProducts()
}
render() {
const {allProducts} = this.state;
return (
<div>
{allProducts.map((product,key) => <div key={key}>
<span>{product.title}</span>
</div>
)}
</div>
)
}
}

Categories

Resources