React Native:由于内存泄漏无法执行更新
React Native: Can't perform update because of memory leak
我正在测试连接了 2 个 android 模拟器的 RN (0.59) 聊天应用程序(gifted chat 0.7.3)。一个客户端上有关于内存泄漏的警告消息:
'Chat message URL : ', 'http://192.168.1.3:3000/api/messages/new'
06-14 14:09:56.628 12503 12552 I ReactNativeJS: 'message appended : ', [ { text: 'hi',
06-14 14:09:56.628 12503 12552 I ReactNativeJS: user: { _id: 22, name: 'jc', avatar: undefined },
06-14 14:09:56.628 12503 12552 I ReactNativeJS: createdAt: Fri Jun 14 2019 14:09:55 GMT-0700 (PDT),
06-14 14:09:56.628 12503 12552 I ReactNativeJS: _id: '54e22cf0-0176-44da-9257-a904840a0d28' } ]
06-14 14:10:12.504 12503 12552 I ReactNativeJS: 'Received server message ', 'Hi did you hear me?'
06-14 14:10:12.510 12503 12552 E ReactNativeJS: '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 %s.%s', 'the componentWillUnmount method', '\n in Chat (at App.js:51)\n in ChatWithSocket (at SceneView.js:9)\n in SceneView (at StackViewLayout.js:786)\n in RCTView (at View.js:45)\n in View (at StackViewLayout.js:785)\n in RCTView (at View.js:45)\n in View (at StackViewLayout.js:784)\n in RCTView (at View.js:45)\n in View (at createAnimatedComponent.js:151)\n in AnimatedComponent (at StackViewCard.js:69)\n in RCTView (at View.js:45)\n in View (at createAnimatedComponent.js:151)\n in AnimatedComponent (at screens.native.js:59)\n in Screen (at StackViewCard.js:57)\n in Card (at createPointerEventsContainer.js:27)\n in Container (at StackViewLayout.js:862)'
应用在componentDidMount
提交消息检索请求后弹出警告。我不知道警告来自哪里。
这里是 Chat.js
:
import React, { Component} from 'react';
import {View, StyleSheet, AppRegistry } from 'react-native';
import { GiftedChat } from 'react-native-gifted-chat';
import DeviceInfo from 'react-native-device-info';
import GLOBAL from '../../lib/global';
import helper from "../../lib/helper";
export default class Chat extends React.Component {
static navigationOptions = {
title: 'Chat'
};
constructor(props) {
super(props);
console.log("Props socket id in chat : ", this.props.socket.id);
console.log("Props in chat : ", this.props);
this._onSend = this._onSend.bind(this);
this.state = {
messages: []
};
};
async _onSend(messages = []) {
console.log("socket.io in chat _onSend : ", this.props.socket.id);
this.props.socket.emit("status", {msg: messages[0].text});
//save message on backend
const result = await helper.getJwtToken();
console.log("secure store : ", result);
if (!result) this.props.navigation.navigate("Event");
let obj = JSON.stringify({
_device_id: DeviceInfo.getUniqueID(),
sender_id: this.props.navigation.state.params.user.id,
socket_id: this.props.socket.id,
data: {
msg_body:messages[0].text,
upload_content_info: ""
},
event_id: this.props.navigation.state.params.eventId,
});
try {
let url = `${GLOBAL.BASE_URL}/api/messages/new`;
console.log("Chat message URL : ", url);
let res = await fetch(url, {
method: "POST",
headers: {
'Accept': 'application/json, text/plain',
'Content-Type': 'application/json',
"x-auth-token": result.password,
"x-auth-secret" : result.username,
},
body: obj,
});
}
catch (err) {
console.log("Error in saving message : ", err.message);
};
this.setState(previousState => ({
messages: GiftedChat.append(previousState.messages, messages),
}))
console.log("message appended : ", messages);
}
formatMessages(messages) {
let obj = [], r = {};
for (i = 0; i < messages.length; i++) {
r = {
_id: '',
text: '',
createdAt : '',
user: {
_id: '',
name: '',
avatar: ''
}
};
r._id = messages[i].id;
r.text = messages[i].data.msg_body;
r.createdAt = messages[i].createdAt;
r.user._id = messages[i].sender_id;
r.user.avatar = messages[i].user.user_data.avatar;
r.user.name = messages[i].user.name;
console.log("r in chat : ", r);
obj.push(r);
}
console.log("formated messages : ", obj);
return obj;
};
async componentDidUpdate() {
};
async componentDidMount() {
console.log(" Chat this.props.socket.id in did mount : ", this.props.socket.id);
const result = await helper.getJwtToken();
console.log("secure store : ", result);
if (!result) this.props.navigation.navigate("Event");
//handle socket.io input
this.props.socket.on("event message", (msg) => {
console.log("Received server message ", msg.data.msg_body); //<<==output
let r = {
_id: msg.id,
text: msg.data.msg_body,
createdAt : msg.createdAt,
user: {
_id: msg.sender_id,
name: msg.user_name,
avatar: msg.user_avatar,
}
};
this.setState(previousState => ({
messages: GiftedChat.append(previousState.messages, r), //<<===problem here???
}))
});
try {
let url = `${GLOBAL.BASE_URL}/api/messages/e?_device_id=${encodeURIComponent(DeviceInfo.getUniqueID())}&event_id=${encodeURIComponent(this.props.navigation.state.params.eventId)}`;
console.log("Chat message URL : ", url);
let res = await fetch(url, {
method: "GET",
headers: {
'Accept': 'application/json, text/plain',
'Content-Type': 'application/json',
"x-auth-token": result.password,
"x-auth-secret" : result.username,
}
});
//
secret = res.headers.get("x-auth-secret");
token = res.headers.get("x-auth-token");
if (secret && token) await helper.updateJwtToken(secret, token);
res = await res.json();
console.log(res);
let formated_msgs = this.formatMessages(res);
this.setState({
messages: formated_msgs,
})
console.log("Messages : ", this.state.messages);
}
catch (e) {
console.log(e);
};
}
render() {
return (
<GiftedChat
messages={this.state.messages}
onSend={messages => this._onSend(messages)}
user={{_id: this.props.navigation.state.params.user.id,
name: this.props.navigation.state.params.user.name,
avatar: this.props.navigation.state.params.user.user_data.avatar}}
/>
);
}
}
AppRegistry.registerComponent('Chat', () => 'Chat');
该错误是Chat卸载后的设置状态导致的。修复的方法是在构造的时候加一个this._isMounted
,在componentWillUnmount
中设置false
。检查 _isMounted
before set 状态。这是修改后的 Chat.js
代码:
import React, { Component} from 'react';
import {View, StyleSheet, AppRegistry } from 'react-native';
import { GiftedChat } from 'react-native-gifted-chat';
//import io from 'socket.io-client';
import DeviceInfo from 'react-native-device-info';
import GLOBAL from '../../lib/global';
import helper from "../../lib/helper";
//import secureStore from "../../lib/securestore";
export default class Chat extends React.Component {
static navigationOptions = {
title: 'Chat'
};
constructor(props) {
super(props);
this._isMounted = true;
console.log("Props socket id in chat : ", this.props.socket.id);
console.log("myRef in chat constructor : ", this.myRef);
this._onSend = this._onSend.bind(this);
....
componentWillUnmount() {
this._isMounted = false;
this.props.socket.removeListener("event message", () => {
console.log("event message removed in Chat");
})
};
async componentDidMount() {
console.log(" Chat this.props.socket.id in did mount : ", this.props.socket.id);
const result = await helper.getJwtToken();
console.log("secure store : ", result);
if (!result) this.props.navigation.navigate("Event");
//handle socket.io input
this.props.socket.on("event message", (msg) => {
console.log("Received server message ", msg.data.msg_body);
let r = {
_id: msg.id,
text: msg.data.msg_body,
createdAt : msg.createdAt,
user: {
_id: msg.sender_id,
name: msg.user_name,
avatar: msg.user_avatar,
}
};
console.log("_isMounted : ", this._isMounted);
if(this._isMounted) this.setState(previousState => ({
messages: GiftedChat.append(previousState.messages, r),
}))
});
try {
let url = `${GLOBAL.BASE_URL}/api/messages/e?_device_id=${encodeURIComponent(DeviceInfo.getUniqueID())}&event_id=${encodeURIComponent(this.props.navigation.state.params.eventId)}&socket_id=${encodeURIComponent(this.props.socket.id)}`;
console.log("Chat message URL : ", url);
let res = await fetch(url, {
method: "GET",
headers: {
'Accept': 'application/json, text/plain',
'Content-Type': 'application/json',
"x-auth-token": result.password,
"x-auth-secret" : result.username,
}
});
//
secret = res.headers.get("x-auth-secret");
token = res.headers.get("x-auth-token");
if (secret && token) await helper.updateJwtToken(secret, token);
res = await res.json();
console.log(res);
let formated_msgs = this.formatMessages(res);
this.setState({
messages: formated_msgs,
})
console.log("Messages : ", this.state.messages);
}
catch (e) {
console.log(e);
};
}
render() {
return (
<GiftedChat
messages={this.state.messages}
onSend={messages => this._onSend(messages)}
user={{_id: this.props.navigation.state.params.user.id,
name: this.props.navigation.state.params.user.name,
avatar: this.props.navigation.state.params.user.user_data.avatar}}
/>
);
}
}
AppRegistry.registerComponent('Chat', () => 'Chat');
我正在测试连接了 2 个 android 模拟器的 RN (0.59) 聊天应用程序(gifted chat 0.7.3)。一个客户端上有关于内存泄漏的警告消息:
'Chat message URL : ', 'http://192.168.1.3:3000/api/messages/new'
06-14 14:09:56.628 12503 12552 I ReactNativeJS: 'message appended : ', [ { text: 'hi',
06-14 14:09:56.628 12503 12552 I ReactNativeJS: user: { _id: 22, name: 'jc', avatar: undefined },
06-14 14:09:56.628 12503 12552 I ReactNativeJS: createdAt: Fri Jun 14 2019 14:09:55 GMT-0700 (PDT),
06-14 14:09:56.628 12503 12552 I ReactNativeJS: _id: '54e22cf0-0176-44da-9257-a904840a0d28' } ]
06-14 14:10:12.504 12503 12552 I ReactNativeJS: 'Received server message ', 'Hi did you hear me?'
06-14 14:10:12.510 12503 12552 E ReactNativeJS: '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 %s.%s', 'the componentWillUnmount method', '\n in Chat (at App.js:51)\n in ChatWithSocket (at SceneView.js:9)\n in SceneView (at StackViewLayout.js:786)\n in RCTView (at View.js:45)\n in View (at StackViewLayout.js:785)\n in RCTView (at View.js:45)\n in View (at StackViewLayout.js:784)\n in RCTView (at View.js:45)\n in View (at createAnimatedComponent.js:151)\n in AnimatedComponent (at StackViewCard.js:69)\n in RCTView (at View.js:45)\n in View (at createAnimatedComponent.js:151)\n in AnimatedComponent (at screens.native.js:59)\n in Screen (at StackViewCard.js:57)\n in Card (at createPointerEventsContainer.js:27)\n in Container (at StackViewLayout.js:862)'
应用在componentDidMount
提交消息检索请求后弹出警告。我不知道警告来自哪里。
这里是 Chat.js
:
import React, { Component} from 'react';
import {View, StyleSheet, AppRegistry } from 'react-native';
import { GiftedChat } from 'react-native-gifted-chat';
import DeviceInfo from 'react-native-device-info';
import GLOBAL from '../../lib/global';
import helper from "../../lib/helper";
export default class Chat extends React.Component {
static navigationOptions = {
title: 'Chat'
};
constructor(props) {
super(props);
console.log("Props socket id in chat : ", this.props.socket.id);
console.log("Props in chat : ", this.props);
this._onSend = this._onSend.bind(this);
this.state = {
messages: []
};
};
async _onSend(messages = []) {
console.log("socket.io in chat _onSend : ", this.props.socket.id);
this.props.socket.emit("status", {msg: messages[0].text});
//save message on backend
const result = await helper.getJwtToken();
console.log("secure store : ", result);
if (!result) this.props.navigation.navigate("Event");
let obj = JSON.stringify({
_device_id: DeviceInfo.getUniqueID(),
sender_id: this.props.navigation.state.params.user.id,
socket_id: this.props.socket.id,
data: {
msg_body:messages[0].text,
upload_content_info: ""
},
event_id: this.props.navigation.state.params.eventId,
});
try {
let url = `${GLOBAL.BASE_URL}/api/messages/new`;
console.log("Chat message URL : ", url);
let res = await fetch(url, {
method: "POST",
headers: {
'Accept': 'application/json, text/plain',
'Content-Type': 'application/json',
"x-auth-token": result.password,
"x-auth-secret" : result.username,
},
body: obj,
});
}
catch (err) {
console.log("Error in saving message : ", err.message);
};
this.setState(previousState => ({
messages: GiftedChat.append(previousState.messages, messages),
}))
console.log("message appended : ", messages);
}
formatMessages(messages) {
let obj = [], r = {};
for (i = 0; i < messages.length; i++) {
r = {
_id: '',
text: '',
createdAt : '',
user: {
_id: '',
name: '',
avatar: ''
}
};
r._id = messages[i].id;
r.text = messages[i].data.msg_body;
r.createdAt = messages[i].createdAt;
r.user._id = messages[i].sender_id;
r.user.avatar = messages[i].user.user_data.avatar;
r.user.name = messages[i].user.name;
console.log("r in chat : ", r);
obj.push(r);
}
console.log("formated messages : ", obj);
return obj;
};
async componentDidUpdate() {
};
async componentDidMount() {
console.log(" Chat this.props.socket.id in did mount : ", this.props.socket.id);
const result = await helper.getJwtToken();
console.log("secure store : ", result);
if (!result) this.props.navigation.navigate("Event");
//handle socket.io input
this.props.socket.on("event message", (msg) => {
console.log("Received server message ", msg.data.msg_body); //<<==output
let r = {
_id: msg.id,
text: msg.data.msg_body,
createdAt : msg.createdAt,
user: {
_id: msg.sender_id,
name: msg.user_name,
avatar: msg.user_avatar,
}
};
this.setState(previousState => ({
messages: GiftedChat.append(previousState.messages, r), //<<===problem here???
}))
});
try {
let url = `${GLOBAL.BASE_URL}/api/messages/e?_device_id=${encodeURIComponent(DeviceInfo.getUniqueID())}&event_id=${encodeURIComponent(this.props.navigation.state.params.eventId)}`;
console.log("Chat message URL : ", url);
let res = await fetch(url, {
method: "GET",
headers: {
'Accept': 'application/json, text/plain',
'Content-Type': 'application/json',
"x-auth-token": result.password,
"x-auth-secret" : result.username,
}
});
//
secret = res.headers.get("x-auth-secret");
token = res.headers.get("x-auth-token");
if (secret && token) await helper.updateJwtToken(secret, token);
res = await res.json();
console.log(res);
let formated_msgs = this.formatMessages(res);
this.setState({
messages: formated_msgs,
})
console.log("Messages : ", this.state.messages);
}
catch (e) {
console.log(e);
};
}
render() {
return (
<GiftedChat
messages={this.state.messages}
onSend={messages => this._onSend(messages)}
user={{_id: this.props.navigation.state.params.user.id,
name: this.props.navigation.state.params.user.name,
avatar: this.props.navigation.state.params.user.user_data.avatar}}
/>
);
}
}
AppRegistry.registerComponent('Chat', () => 'Chat');
该错误是Chat卸载后的设置状态导致的。修复的方法是在构造的时候加一个this._isMounted
,在componentWillUnmount
中设置false
。检查 _isMounted
before set 状态。这是修改后的 Chat.js
代码:
import React, { Component} from 'react';
import {View, StyleSheet, AppRegistry } from 'react-native';
import { GiftedChat } from 'react-native-gifted-chat';
//import io from 'socket.io-client';
import DeviceInfo from 'react-native-device-info';
import GLOBAL from '../../lib/global';
import helper from "../../lib/helper";
//import secureStore from "../../lib/securestore";
export default class Chat extends React.Component {
static navigationOptions = {
title: 'Chat'
};
constructor(props) {
super(props);
this._isMounted = true;
console.log("Props socket id in chat : ", this.props.socket.id);
console.log("myRef in chat constructor : ", this.myRef);
this._onSend = this._onSend.bind(this);
....
componentWillUnmount() {
this._isMounted = false;
this.props.socket.removeListener("event message", () => {
console.log("event message removed in Chat");
})
};
async componentDidMount() {
console.log(" Chat this.props.socket.id in did mount : ", this.props.socket.id);
const result = await helper.getJwtToken();
console.log("secure store : ", result);
if (!result) this.props.navigation.navigate("Event");
//handle socket.io input
this.props.socket.on("event message", (msg) => {
console.log("Received server message ", msg.data.msg_body);
let r = {
_id: msg.id,
text: msg.data.msg_body,
createdAt : msg.createdAt,
user: {
_id: msg.sender_id,
name: msg.user_name,
avatar: msg.user_avatar,
}
};
console.log("_isMounted : ", this._isMounted);
if(this._isMounted) this.setState(previousState => ({
messages: GiftedChat.append(previousState.messages, r),
}))
});
try {
let url = `${GLOBAL.BASE_URL}/api/messages/e?_device_id=${encodeURIComponent(DeviceInfo.getUniqueID())}&event_id=${encodeURIComponent(this.props.navigation.state.params.eventId)}&socket_id=${encodeURIComponent(this.props.socket.id)}`;
console.log("Chat message URL : ", url);
let res = await fetch(url, {
method: "GET",
headers: {
'Accept': 'application/json, text/plain',
'Content-Type': 'application/json',
"x-auth-token": result.password,
"x-auth-secret" : result.username,
}
});
//
secret = res.headers.get("x-auth-secret");
token = res.headers.get("x-auth-token");
if (secret && token) await helper.updateJwtToken(secret, token);
res = await res.json();
console.log(res);
let formated_msgs = this.formatMessages(res);
this.setState({
messages: formated_msgs,
})
console.log("Messages : ", this.state.messages);
}
catch (e) {
console.log(e);
};
}
render() {
return (
<GiftedChat
messages={this.state.messages}
onSend={messages => this._onSend(messages)}
user={{_id: this.props.navigation.state.params.user.id,
name: this.props.navigation.state.params.user.name,
avatar: this.props.navigation.state.params.user.user_data.avatar}}
/>
);
}
}
AppRegistry.registerComponent('Chat', () => 'Chat');