Can't perform a React state update - javascript

I have this kind of error:
Warning: 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. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
I have tried to follow some advice that I found but I haven't solved it. How can I do??
class Starting extends Component {
_isMounted = false
constructor(props) {
super(props);
this.state = {
loading: true,
};
}
componentDidMount() {
this._isMounted = true;
User.getUserLoggato()
.then(dataUserLoggato => {
if (dataUserLoggato !== null) {
global.user = new User(JSON.parse(dataUserLoggato));
Actions.homepage({ utente: global.user });
} else {
Actions.login();
}
})
.catch(err => {
console.log(err);
})
.finally(() => {
this.setState({ loading: false });
});
}
componentWillUnmount() {
this._isMounted = false;
}
render() {
return (
<View style={style.container}>
<View style={style.page}>
<ActivityIndicator size="large" color="#56cbbe" />
</View>
</View>
);
}
}

Check if the component is mounted before you set the state:
.finally(() => {
this._isMounted && this.setState({ loading: false });
});

Related

Infinity fetching loop

Once I am opening my DocumentViewer fetching does infinity loop and I cannot see why this is happening, could somebody maybe see where is the problem, Its something to do with state and props but i cannot figure out why, it would be amazing to know what is the problem and how to approach it
class GeneralDocPresenter extends React.Component {
state = {
metaInfoDocs: [],
docs: [],
loading: false
};
updateDoc = () => {
this.props.selectedDocsStore.clear();
this.props.selectedDocsStore.setViewDocId(0);
this.setState({ loading: true });
this.props
.fetchMetaDocs()
.then((r) => this.setState({ metaInfoDocs: r.data, loading: false }))
.catch((err) => {
this.setState({ loading: false });
errorWithMessage("Could not load documents");
});
this.props.eventManager.on("viewDoc", (doc) => {
this.loadDocuments(doc.id);
});
};
componentDidUpdate(prevProps, prevState, snapshot) {
this.updateDoc()
}
componentDidMount() {
this.updateDoc()
}
render() {
return <Translation>
{(t) => {
if (this.state.loading) {
return (
<div style={{display: 'flex', justifyContent: 'center'}}>
<Spin size={"medium"}/>
</div>
)
}
if (this.state.metaInfoDocs.length === 0) {
return (
<div style={{display: 'flex', justifyContent: 'center'}}>
<NoDocumentsAlert><div dangerouslySetInnerHTML={{__html: t('noDocuments')}}/></NoDocumentsAlert>
</div>
)
}
return (
<DocViewWrapper docs={this.state.docs}
metaInfoDocs={this.state.metaInfoDocs.map(doc => {
return {...doc, type: this.props.type}
})}
eventManager={this.props.eventManager}
settings={this.props.settings}
childComponents={this.props.childComponents}
/>
)
}}
</Translation>
}
loadDocuments(id) {
this.props.loadDocument(id).then(r => {
this.setState({
docs: r.data
})
});
}
}
Try replacing ComponentDidUpdate from
componentDidUpdate(prevProps, prevState, snapshot) {
this.updateDoc()
}
To
componentDidUpdate(prevProps, prevState, snapshot) {
if(this.props !== prevProps){
this.updateDoc()
}
}
You can be more specific to didUpdate what to check instead of checking complete props change.

Why my functions is not returning a component? React

I have a function that render LoginPage if the user is not logged and render the IndexPage if is logged, but It is not rendering none, I tried alerting the user.displayName and It work. See my code.
renderPage = () => {
firebase.auth().onAuthStateChanged(user => {
if (user) {
return <IndexPage />;
} else {
return <LoginPage />;
}
});
};
render() {
return <div>{this.renderPage()}</div>;
}
Why is not working?
You miss a return in the renderPage function, but performing async requests in render is not a good approach in react.
What you should do, is to move the user into the state, then on componentDidMount fetch the user from your async code, and inside your render use the state prop user.
So your code should be something like:
constructor() {
this.state = { user: null };
}
componentDidMount() {
firebase.auth().onAuthStateChanged(user => {
user ? this.setState({ user }) : this.setState({ user: null });
});
}
render() {
const content = this.state.user ? <IndexPage /> : <LoginPage />;
return <div>{content}</div>;
}
Your function inside render method is async function, what you get is undefined.
You should store the user state. Do something like,
class YourComponent extends Component {
constructor(props) {
super(props);
this.state = {
user: null
};
}
componentDidMount() {
firebase.auth().onAuthStateChanged(user => {
if (user) {
this.setState({
user
});
}
});
}
render() {
return (
{this.state.user ? <IndexPage /> : <LoginPage />}
);
}
}

Using same variable name in component state and props with redux and I get error of undefined props

I have components that needs to know if the user is logged with Facebook or not, so I use the function mapStateToProps to map redux state to component props.
At the same time, some components can login themselves the users, so they need local state to know if the user is logged or not (I can not update component props inside component itself).
When user logs in, they dispatch a loggedIn event.
Is it a best practice or am I doing something wrong?
I have components like this:
import ...
const FBSDK = require('react-native-fbsdk');
const {
LoginButton,
AccessToken,
LoginManager
} = FBSDK;
class Settings extends Component {
constructor(props) {
super(props);
this.state = {
isLoggedIn: false,
};
}
componentDidMount () {
this._loadInitialState();
}
async _loadInitialState() {
AccessToken.getCurrentAccessToken().then(
(data) => {
if (data) {
this.setState({
isLoggedIn: true,
});
// Dispatch loggedIn action
this.props.loggedIn();
}
}
);
}
render() {
const {navigate} = this.props.navigation;
return (
<Content>
<List style={styles.list}>
{this.state.isLoggedIn &&
<ListItem>
<Body>
...
</Body>
</ListItem>
}
<ListItem style={styles.listItem}>
<LoginButton
readPermissions={["user_friends", "email"]}
onLoginFinished={
(error, result) => {
if (error) {
alert("login has error: " + result.error);
} else if (result.isCancelled) {
alert("login is cancelled.");
} else {
AccessToken.getCurrentAccessToken().then(
(data) => { this.setState({isLoggedIn: true});
// Dispatch loggedIn action
this.props.loggedIn();
}
).catch(function(e) {
log(e); // "oh, no!"
});
}
}
}
onLogoutFinished={() => {
this.setState({isLoggedIn: false});
// Dispatch loggedOut action
this.props.loggedOut();
}} />
</ListItem>
</List>
</Content>
)
}
}
Settings.propTypes = {
isLoggedIn: PropTypes.bool.isRequired,
}
const mapStateToProps = (state) => {
return {
isLoggedIn: state.isLoggedIn,
};
};
const mapDispatchToProps = (dispatch) => ({
startup: () => dispatch(StartupActions.startup()),
loggedIn: () => dispatch({
type: LOGGED_IN
}),
loggedOut: () => dispatch({
type: LOGGED_OUT
})
});
export default connect(mapStateToProps, mapDispatchToProps, null, {pure: false})(Settings);
As you can see the component does have isLoggedIn local state and isLoggedIn prop, and I need to update both at the same time.
I am asking cause other than the practice, when I login the user with Facebook I get this error (I did not looked for where but it seems that somewhere props are undefined):
TypeError: Cannot read property 'loggedIn' of undefined
and I do not know where and why it occurs and if it depends on this code.
You need to bind your method to access this in _loadInitialState method.
The simplest way would be to use an arrow function as
_loadInitialState = async() => {
Also dispatch actions once the setState has finished updating, in its callback method
this.setState({
isLoggedIn: true,
}, () => this.props.loggedIn());
onLogoutFinished={() => {
this.setState({isLoggedIn: false}, () => this.props.loggedOut());
}}

update state with AsyncStorage - React Native

I have a small problem with componentWillMount when I want to update the component's state I get this error.
the code is this :
export default class RouterComponent extends Component {
constructor(props) {
super(props);
this.state = {
isLogin: null
}
}
componentWillMount = async () => {
AsyncStorage.getItem('#MyUserFireBase:key').then(response => {
this.setState({ 'isLogin': true });
}).done();
}
render() {
return (
<Router>
<Stack key="root" hideNavBar>
<Stack key="main">
<Scene key="login" initial={!this.state.isLogin} component={LoginForm} title="Login" />
<Scene key="employeeList" initial={this.state.isLogin} component={EmployeeList} title="Employee List" />
</Stack>
</Stack>
</Router >
)
}
}
I have more than 3 days looking for the solution and I can not find it. What am I doing wrong?
Thanks for your time
Check out component lifecycle methods to help you with your problem: https://engineering.musefind.com/react-lifecycle-methods-how-and-when-to-use-them-2111a1b692b1
For my answer, try this:
componentDidMount = async () => {
AsyncStorage.getItem('#MyUserFireBase:key').then(response => {
this.setState({ 'isLogin': true });
}).done();
}
Also, if that does not work, try Update instead, since you are setting the state:
componentDidUpdate() = {
AsyncStorage.getItem('#MyUserFireBase:key').then(response => {
this.setState({ 'isLogin': true });
}).done();
}
Use componentDidMount() instead of componentWillMount() and remove async()
componentDidMount() {
AsyncStorage.getItem('#MyUserFireBase:key').then(response => {
this.setState({ 'isLogin': true });
}).done();
}
componentWillMount is also marked as deprecated in React 16.3
https://reactjs.org/blog/2018/03/29/react-v-16-3.html (Lifecycle section)

Wait for react-promise to resolve before render

So I have a large set of data that I'm retrieving from an API. I believe the problem is that my component is calling the renderMarkers function before the data is received from the promise.
So I am wondering how I can wait for the promise to resolve the data completely before calling my renderMarkers function?
class Map extends Component {
componentDidMount() {
console.log(this.props)
new google.maps.Map(this.refs.map, {
zoom: 12,
center: {
lat: this.props.route.lat,
lng: this.props.route.lng
}
})
}
componentWillMount() {
this.props.fetchWells()
}
renderMarkers() {
return this.props.wells.map((wells) => {
console.log(wells)
})
}
render() {
return (
<div id="map" ref="map">
{this.renderMarkers()}
</div>
)
}
}
function mapStateToProps(state) {
return { wells: state.wells.all };
}
export default connect(mapStateToProps, { fetchWells })(Map);
You could do something like this to show a Loader until all the info is fetched:
class Map extends Component {
constructor () {
super()
this.state = { wells: [] }
}
componentDidMount() {
this.props.fetchWells()
.then(res => this.setState({ wells: res.wells }) )
}
render () {
const { wells } = this.state
return wells.length ? this.renderWells() : (
<span>Loading wells...</span>
)
}
}
for functional components with hooks:
function App() {
const [nodes, setNodes] = useState({});
const [isLoading, setLoading] = useState(true);
useEffect(() => {
getAllNodes();
}, []);
const getAllNodes = () => {
axios.get("http://localhost:5001/").then((response) => {
setNodes(response.data);
setLoading(false);
});
};
if (isLoading) {
return <div className="App">Loading...</div>;
}
return (
<>
<Container allNodes={nodes} />
</>
);
}
Calling the render function before the API call is finished is fine. The wells is an empty array (initial state), you simply render nothing. And after receiving the data from API, your component will automatically re-render because the update of props (redux store). So I don't see the problem.
If you really want to prevent it from rendering before receiving API data, just check that in your render function, for example:
if (this.props.wells.length === 0) {
return null
}
return (
<div id="map" ref="map">
{this.renderMarkers()}
</div>
)
So I have the similar problem, with react and found out solution on my own. by using Async/Await calling react
Code snippet is below please try this.
import Loader from 'react-loader-spinner'
constructor(props){
super(props);
this.state = {loading : true}
}
getdata = async (data) => {
return await data;
}
getprops = async (data) =>{
if (await this.getdata(data)){
this.setState({loading: false})
}
}
render() {
var { userInfo , userData} = this.props;
if(this.state.loading == true){
this.getprops(this.props.userData);
}
else{
//perform action after getting value in props
}
return (
<div>
{
this.state.loading ?
<Loader
type="Puff"
color="#00BFFF"
height={100}
width={100}
/>
:
<MyCustomComponent/> // place your react component here
}
</div>
)
}

Categories