Async issue with Link - javascript

I'm running into an async issue I hope I can get help in. The error I'm getting is
index.js:2178 Warning: Can't call setState (or forceUpdate) 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.
in CreateRoomPage (created by Route)
The location of this error is within this function. The two location are found where it says loginPromise.then and the setState which is at the bottom of the function. I'm currently calling this function when a person clicks on .
createUser(e) {
if (this.state.username === "") {
e.preventDefault();
this.setState({
errors: "Username can't be blank"
});
return;
}
const loginPromise = new Promise((resolve, reject) => {
firebase.auth().onAuthStateChanged(user => {
if (user) {
window.user = user;
resolve(user.uid);
} else {
firebase
.auth()
.signInAnonymously()
.then(user => {
resolve(user.uid);
})
.catch(err => {
console.log(err);
});
}
});
});
loginPromise.then(id => {
let db = firebase.database();
let playersRef = db.ref(`Room/${this.state.roomId}/players`);
playersRef.child(`${id}`).set(`${this.state.username}`);
let player = db.ref(`Room/${this.state.roomId}/players/${id}`);
player.onDisconnect().remove();
let allPlayers = db.ref(`Room/${this.state.roomId}/all-players`);
allPlayers.child(`${id}`).set(true);
let allPlayer = db.ref(`Room/${this.state.roomId}/all-players/${id}`);
allPlayer.onDisconnect().remove();
let scoreBoard = db.ref(`Room/${this.state.roomId}/scoreBoard`);
scoreBoard.child(`${this.state.username}`).set(0);
let playerScore = db.ref(
`Room/${this.state.roomId}/scoreBoard/${this.state.username}`
);
playerScore.onDisconnect().remove();
let creator = db.ref(`Room/${this.state.roomId}`);
creator.child("creator").set(`${id}`);
db.ref(`Room/${this.state.roomId}`)
.child("gameStarted")
.set(false);
this.setState({
username: "",
errors: ""
});
});
I've spend nearly 3 hours trying to figure this out. I'm hoping someone can teach me where I'm making this error. I've tried to use a local state where once the componentDidMount it'll change local state to true and back to false once it unmounted like so:
componentDidMount() {
this.setState({ isMounted: true }, () => {
if (this.state.isMounted) {
let db = firebase.database();
let roomRefKey = db.ref("Room").push().key;
this.setState({
roomId: roomRefKey
});
}
});
}
Below is another place where it throws such an error
createUser(e) {
e.preventDefault();
if (
this.state.username.length === 0 &&
this.state.accesscode.length === 0
) {
this.setState({
errors: {
username: "Username can't be blank",
accesscode: "Access Code can't be blank"
}
});
return;
}
if (this.state.username.length === 0) {
this.setState({
errors: { username: "Username can't be blank", accesscode: "" }
});
return;
}
if (this.state.accesscode.length === 0) {
this.setState({
errors: { username: "", accesscode: "Access Code can't be blank" }
});
return;
}
const loginPromise = new Promise((resolve, reject) => {
firebase.auth().onAuthStateChanged(user => {
if (user) {
window.user = user;
resolve(user.uid);
} else {
firebase
.auth()
.signInAnonymously()
.then(user => {
resolve(user.uid);
})
.catch(err => {
console.log(err);
});
}
});
});
loginPromise.then(id => {
let db = firebase.database();
let playersRef = db.ref(`Room/${this.state.accesscode}/players`);
playersRef.child(`${id}`).set(`${this.state.username}`);
let player = db.ref(`Room/${this.state.accesscode}/players/${id}`);
player.onDisconnect().remove();
let allPlayers = db.ref(`Room/${this.state.accesscode}/all-players`);
allPlayers.child(`${id}`).set(true);
let allPlayer = db.ref(`Room/${this.state.accesscode}/all-players/${id}`);
allPlayer.onDisconnect().remove();
let scoreBoard = db.ref(`Room/${this.state.accesscode}/scoreBoard`);
scoreBoard.child(`${this.state.username}`).set(0);
let playerScore = db.ref(
`Room/${this.state.accesscode}/scoreBoard/${this.state.username}`
);
playerScore.onDisconnect().remove();
this.props.history.push({
pathname: `/waiting-room/${this.state.accesscode}`
});
});
}

Related

Message Unable to show automatically in reciever's side with Socket.io

I'm trying to send real time message with socket.io.
But the problem is that The reciever won't receive the message until i refresh the browser.
i'm getting all the data on the console but not in recievers end
I want to make it a real time message
Below are my codes
Messenger FrontEnd
// Context State
const { friends, setFriends, message, setMessage, authInfo } = useAuth();
const [currentFriend, setCurrentFriend] = useState("");
const [activeUser, setActiveUser] = useState([]);
const [newMessage, setNewMessage] = useState("");
const [socketMessage, setSocketMessage] = useState("");
const { updateNotification } = useNotification();
useEffect(() => {
socket.current = io("ws://localhost:9000");
socket.current.on("getMessage", (data) => {
setSocketMessage(data);
});
}, []);
useEffect(() => {
if (socketMessage && currentFriend) {
if (
socketMessage.senderId === currentFriend._id &&
socketMessage.receiverId === authInfo.profile.id
) {
console.log([...message, socketMessage]); // I'm confused on what to do here
}
}
setSocketMessage("");
}, [socketMessage]);
Socket.io Backend
let users = [];
const addUser = (userId, socketId, userInfo) => {
const checkUser = users.some((u) => u.userId === userId);
if (!checkUser) {
users.push({ userId, socketId, userInfo });
}
};
const userRemove = (socketId) => {
users = users.filter((u) => u.socketId !== socketId);
};
const findFriend = (id) => {
return users.find((u) => u.userId === id);
};
io.on("connection", (socket) => {
console.log("Socket Is Connecting...");
socket.on("addUser", (userId, userInfo) => {
addUser(userId, socket.id, userInfo);
io.emit("getUser", users);
});
socket.on("sendMessage", (data) => {
const user = findFriend(data.receiverId);
if (user !== undefined) {
socket.to(user.socketId).emit("getMessage", {
senderId: data.senderId,
senderName: data.senderName,
receiverId: data.receiverId,
createAt: data.time,
message: {
text: data.message.text,
image: data.message.image,
},
});
}
});

How to await firebase to start, avoiding Error: "Client is offline" on React Native?

I need help retrieving firebase data in React Native, using expo.
This code works fine when I refresh the app, but when it first starts, it throws an error:
Error: Error: Client is offline.
Maybe I need to do it async and await, I have tried some ways, but no success.
componentDidMount = async () => {
var radioFireData = null;
const { names } = this.props;
const dbRef = ref(db, "records/");
get(child(dbRef, "flap/"))
.then((snapshot) => {
if (snapshot.exists()) {
radioFireData = snapshot.val();
this.setState({ checked: radioFireData[names] });
} else {
console.log("No data available");
}
})
.catch((error) => {
console.log(error);
});
};
Here it is... Maybe I can help someone.
componentDidMount(){
const { names } = this.props;
const reference = ref(db, "records/" + "/flap/");
onValue(
reference,
(snapshot) => {
const data = snapshot.val();
this.setState({ checked: data[names] });
},
{
onlyOnce: true,
}
);
};

firebase realtime database doesn't work as expected

I'm a junior dev, so please be easy with me.
My goal is to auth an user anonymously when he open the homepage, then if all went ok, he fetch the data from the real time database, but only if the uid match is ok!
He there are my rules:
{
"rules": {
"$uid": {
".read": "auth != null && auth.uid == $uid",
".write": "false",
}
}
}
My database is structured like this https://ibb.co/jkRBCsF
The anonymous sign in is on the context
export const AuthContextProvider = ({ children }: Props) => {
React.useEffect(() => {
signInAnonymously(auth)
.then(() => {
// Signed in..
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
// ...
});
}, []);
const values = {};
return <AuthContext.Provider value={values}>{children}</AuthContext.Provider>;
};
Then when I open a page for example '/crew' getStaticProps launches fetchPlanetInfo
export const getStaticProps: GetStaticProps = async () => {
const planetInfo = await fetchPlanetsInfo("destinations");
return {
props: {
data: planetInfo,
},
};
};
export function fetchPlanetsInfo(query: string) {
let dataArr: any = [];
try {
onAuthStateChanged(auth, (user) => {
if (user) {
const uid = user.uid;
const databaseCall = ref(db, uid + "/" + query);
onValue(databaseCall, (snapshot) => {
const data = snapshot.val();
dataArr.push(data);
});
} else {
console.log("user is signout");
// User is signed out
// ...
}
});
} catch (error) {
console.log(error);
}
return dataArr.flat();
}
I tried to figure out where is the error but I didn't find out the solution
My problem is that the data is not get from the database. fetchPlanetsInfo return null. I think my rules is written wrong.

Why can't I access data after fetching?

I'm trying to keep session stayed logged in after refreshing the browser. The user data that is being fetched is not rendering after being fetched. The console is saying "Cannot read properties of undefined (reading 'user'). This is my code for the login/sign up page.
The data I'm trying to access is in the picture below:
(Auth.js)
const Auth = () => {
const navigate = useNavigate();
const dispatch = useDispatch();
const [isSignup, setIsSignup] = useState(false);
const [inputs, setInputs] = useState({
name: "",
username: "",
email: "",
password: ""
})
const handleChange = (e) => {
setInputs(prevState => {
return {
...prevState,
[e.target.name]: e.target.value
}
})
}
const sendRequest = async (type = '') => {
const res = await axios.post(`/user/${type}`, {
name: inputs.name,
email: inputs.email,
username: inputs.username,
password: inputs.password,
}).catch(error => console.log(error))
const data = await res.data;
console.log(data)
return data;
}
const handleSubmit = (e) => {
e.preventDefault()
console.log(inputs)
if (isSignup) {
sendRequest("signup")
.then((data) => {
dispatch(authActions.login());
localStorage.setItem('userId', data.user._id);
navigate("/posts");
});
} else {
sendRequest("login")
.then((data) => {
dispatch(authActions.login());
localStorage.setItem('userId', data.user._id);
navigate("/posts");
});
}
}
Redux store file
const authSlice = createSlice({
name: "auth",
initialState: { isLoggedIn: false },
reducers: {
login(state) {
state.isLoggedIn = true
},
logout(state) {
state.isLoggedIn = false
}
}
})
export const authActions = authSlice.actions
export const store = configureStore({
reducer: authSlice.reducer
})
Chaining promises using .then() passes the resolved value from one to the next. With this code...
sendRequest("...")
.then(() => dispatch(authActions.login()))
.then(() => navigate("/posts"))
.then(data => localStorage.setItem('token', data.user))
You're passing the returned / resolved value from navigate("/posts") to the next .then() callback. The navigate() function returns void therefore data will be undefined.
Also, your redux action doesn't return the user so you can't chain from that either.
To access the user data, you need to return it from sendRequest()...
const sendRequest = async (type = "") => {
try {
const { data } = await axios.post(`/user/${type}`, { ...inputs });
console.log("sendRequest", type, data);
return data;
} catch (err) {
console.error("sendRequest", type, err.toJSON());
throw new Error(`sendRequest(${type}) failed`);
}
};
After that, all you really need is this...
sendRequest("...")
.then((data) => {
dispatch(authActions.login());
localStorage.setItem('userId', data.user._id);
navigate("/posts");
});
Since you're using redux, I would highly recommend moving the localStorage part out of your component and into your store as a side-effect.

Close Modal Only When Authenticated

In order to create a transaction the user must first be authenticated(password) in the confirm dialog modal. Once the user is authenticated the modal is closed and the transaction is shown. If a user is not authenticated correctly the modal still closes and a toast message is displayed with the error. I would like to change this logic so that the user must re-enter the password if their attempt was not authenticated.
setting the state inside createTransaction is done in an async way. this.closeModal in newTransactionModal is whats causing the modal to be closed and the state to be reset.
NewTransactionModal.js
this.state.showConfirmDialog ? (
<ConfirmDialog
type="New Transaction"
onPasswordChange={this.changePassword}
onConfirm={this.handleCreateClick}
onCancel={this.handleClose}
errMsg = {this.props.passwordErrMsg}
/>
) : null
NewTransactionModal.js
handleCreateClick = () => {
if (this.formIsValid()) {
let path = '/transaction',
transaction = {
type: this.state.transactionType.id,
amount: this.state.transactionAmount,
internalComment: this.state.comment,
userPassword: this.state.password
},
extraDataForError = {
typeName: this.state.transactionType.name,
advertiserName: this.state.advertiser.name,
fundingType: this.state.advertiser.fundingType,
financialDocumentId: this.state.documentId,
financialDocumentType: this.state.document && this.state.document.documentMetadata.documentType
};
if (this.state.transactionType.creditType) {
path += '/transfer';
transaction.debitAdvertiserId = this.state.advertiserId;
transaction.creditAdvertiserId = this.state.transferAdvertiserId;
transaction.debitFinancialDocumentId = this.state.documentId;
transaction.creditFinancialDocumentId = this.state.documentId;
} else {
transaction.advertiserId = this.state.advertiserId;
transaction.financialDocumentId = this.state.documentId;
}
this.props.createTransaction(path, transaction, extraDataForError);
this.closeModal();
}
};
ListTransaction.js
createTransaction = (path, data, extra) => {
const failureMsg = 'Failed to create transaction';
if (!path || !data) {
this.setState({
toastMessage: failureMsg,
toastType: 'error'
});
return;
}
const getErrTransactions = res => {
return [{ transaction: {...data, ...extra}, validations: res.validations, result: res.result }];
};
this.setState({ toastType: 'pending' }, async () => {
try {
const res = await ApiService.post(path, data, this.abortController.signal);
if (res && res.result !== 'FAILURE') {
this.setState({
toastMessage: 'Transaction created',
toastType: 'success',
selected: []
}, () => this.loadData(1));
} else if (res) {
this.handleError(getErrTransactions(res), failureMsg);
} else {
this.setState({toastType: null})
}
} catch (err) {
if (err) {
if (err.name === 'AbortError') {
return false;
} else if (err.json) {
this.setState({
passwordErrMsg: "Please enter a valid password"
})
const jsonErr = await err.json();
this.handleError(jsonErr.result ? getErrTransactions(jsonErr) : jsonErr, failureMsg);
} else {
this.handleError(err, failureMsg);
}
} else {
this.setState({ toastType: null });
}
}
});
};
ListTransactions.js
canCreate ? (
<NewTransactionModal
show={showNewTransactionModal}
types={allowedTransactionTypes}
createTransaction={this.createTransaction}
passwordErrMsg = {this.state.passwordErrMsg}
handlePasswordAttempt = {this.handlePasswordAttempt}
handleRecent={this.handleRecent}
handleClose={this.hideModal}
/>
) : null
I expect that that the user must still enter their password after an invalid attempt and that the modal does not go away. Also after the user has entered the correct password than the modal is closed.

Categories