Push Is Not A Function, Redux - javascript

I have a component that's state I'm trying to use in other components, it has to be used in multiple components so i'm switching it to redux. Right now using the same function that works in my component, I am getting the error 'newUsers.push is not a function'
Below is my redux action:
import { FETCH_USERS_TO_ADD } from './types';
import axios from 'axios'
export const fetchUsersToAdd = () => dispatch => {
var userBox = [];
var newUserBox = [];
let newUserIds = {};
let newUsers = [];
axios.all([
axios.get('/updateUserList'),
axios.get('/userInformation')
])
.then(axios.spread((newUsers, currentUsers) => {
userBox.push(newUsers.data)
newUserBox.push(currentUsers.data)
for (let newUser of newUserBox){
newUserIds[newUser.id] = newUser.id
}
for (let user of userBox){
if(!newUserIds[user.staffID]){
console.log(user)
**newUsers.push(user)**
}
}
})).then(dispatch({
type: FETCH_USERS_TO_ADD,
payload: newUsers
}))
}
The Code Below in My class component doesnt give me this error:
let newUserIds = {};
let newUsers = [];
this.state = {
userBox : [],
newUserBox : [],
usersToBeAdded:[],
}
componentDidMount(){
axios.all([
axios.get('/updateUserList'),
axios.get('/userInformation')
])
.then(axios.spread((newUsers, currentUsers) => {
this.setState({
userBox : newUsers.data,
newUserBox : currentUsers.data,
})
}))
}
checkForNewUsers = (e) => {
for (let newUser of this.state.newUserBox){
newUserIds[newUser.id] = newUser.id
}
for (let user of this.state.userBox){
if(!newUserIds[user.staffID]){
newUsers.push(user)
}
}
this.setState({
usersToBeAdded:newUsers
})
console.log(newUsers)
}
<UpdateUsersButton onClick={this.checkForNewUsers}/>
When user is consoled it returns an array like this:
Array(10)
0: {staffID: 1, dept: "Junior Web Developer", email: "manasaN#tpionline.com", name: "manasa", password: "$2y$10$/zYS7OhzwdLOi6Slzc3xxxxxiY0y1J6MjnLCN24GmZ3rMHWUS"}
1: {staffID: 2, dept: "Web Developer", email: "juliof#tpionline.net", name: "Julio Fajardo", password: "$2y$10$MphAC8aRY2uzs1Zxxxxxnd7t0KukEkvGbx5Y4van.Da6"}

I think it's because newUsers gets shadowed in the arrow function and isn't what you think it is anymore. Don't reuse variable names.
Try this:
import { FETCH_USERS_TO_ADD } from './types';
import axios from 'axios'
export const fetchUsersToAdd = () => dispatch => {
var userBox = [];
var newUserBox = [];
let newUserIds = {};
let newUsersArr = []; // Make unique
axios.all([
axios.get('/updateUserList'),
axios.get('/userInformation')
])
.then(axios.spread((newUsers, currentUsers) => { // Was shadowed here
userBox.push(newUsers.data)
newUserBox.push(currentUsers.data)
for (let newUser of newUserBox){
newUserIds[newUser.id] = newUser.id
}
for (let user of userBox){
if(!newUserIds[user.staffID]){
console.log(user)
newUsersArr.push(user)
}
}
})).then(dispatch({
type: FETCH_USERS_TO_ADD,
payload: newUsersArr
}))
}

Related

I'm having problem figuring why login is working sometimes but sometimes not with Graph and Firebase

So, I have built this way of auth with firebase and graphql using mongo database, the problem is that everything is working, instead trying to login, its the same way of register but sometimes the method works and some times I get apollo client error, which I don't know why.
Here is my code to auth with Firebase and then check if the user exits and call the method and then the oposite.
import { FirebaseAuth } from "../config/Firebase";
import { GoogleAuthProvider, signInWithPopup } from "firebase/auth";
import { Notifier } from "../utils";
import { USER_AUTH_ERROR } from "../config/Responders";
const Google = async (Register, Login, dispatch) => {
var Provider = new GoogleAuthProvider();
const data = await signInWithPopup(FirebaseAuth, Provider);
try {
if (data) {
const user = data.user;
const creationTime = user.metadata.creationTime;
const lastSignInTime = user.metadata.lastSignInTime;
if (creationTime === lastSignInTime) {
const name = user.displayName.split(" ");
const firstName = name[0] || "";
const lastName = name[1] || "";
const config = {
variables: {
createUserInput: {
Name: firstName,
Surname: lastName,
Email: user.email,
Avatar: user.photoURL || null,
Uid: user.uid,
},
},
};
Register(config);
}
else {
const config = {
variables: {
uid: user.uid,
},
};
Login(config);
}
}
else Notifier(dispatch, USER_AUTH_ERROR, `error`);
} catch (error) {
Notifier(dispatch, USER_AUTH_ERROR, `error`);
}
};
export
default Google;
While here is the place where I manage the functions:
const [Register, { data: registerData }] = useMutation(REGISTER);
const [Login, { data: loginData }] = useLazyQuery(AUTH);
const Auther = () => Google(Register, Login, dispatch);
useEffect(() => {
if (!account.Auth) {
if (registerData?.hasOwnProperty("UserRegister")) {
dispatch(Authenticate(registerData.UserRegister));
}
}
}, [registerData]);
useEffect(() => {
if (!account.Auth) {
if (loginData?.hasOwnProperty("UserAuth")) {
dispatch(Authenticate(loginData.UserAuth));
}
}
}, [loginData]);
Here is the error I get:

How to store this array in to string

I want store this array in to string,so I used toString method() but its display objects in the console log
I create multiple input filed and i want get this multiple values into the string (separate by commars (,) and store to the database)
import { useState } from "react";
function AddRemoveInputField() {
const [inputFields, setInputFields] = useState([
{
fullName: "",
},
]);
const addInputField = () => {
setInputFields([
...inputFields,
{
fullName: "",
},
]);
};
const removeInputFields = (index) => {
const rows = [...inputFields];
rows.splice(index, 1);
setInputFields(rows);
};
const handleChange = (index, evnt) => {
const { name, value } = evnt.target;
const list = [...inputFields];
list[index][name] = value;
setInputFields(list);
};
console.log(inputFields)
}
Image is attached bellow

Already populated array in useContext giving me an empty array when accessing it in a function inside of the same context?

The problem I'm having is, that I have a useContext in which I provide all logged users. On the initial run of the app or when the users' log in the array gets populated with all the users that are currently on the server... Which works as expected. But I have also the functionality, that whenever the server "user-connected" event runs, the front-end should just push the user to the end of this array. And there lays the problem. From the backend, the right user is sent, but when I access the connectedUsers array, the array is empty... but it should be already populated.
UsersProvider.tsx
export const inState = {
connectedUsers: [],
addUser: (user: any) => {},
sortUsers: (user: any, socketID: string) => {},
setLoggedUsers: () => {},
};
export interface initState {
connectedUsers: any[];
addUser(user: any): void;
sortUsers(users: any, socketID: string): void;
setLoggedUsers: React.Dispatch < React.SetStateAction < any[] >> ;
}
const UsersContext = createContext < initState > (inState);
export const useUsers = () => {
return useContext(UsersContext);
};
const initUserProps = (user: any) => {
user.messages = [];
user.hasNewMessages = false;
};
export const UsersProvider = ({
children
}: Props) => {
const [connectedUsers, setLoggedUsers] = useState < any[] > ([]);
const addUser = (user: any) => {
console.log('add', connectedUsers);
// This is empty, but it should be already populated when next user connected.
};
const sortUsers = (users: any, socketUserID: string) => {
const usersCopy = users;
usersCopy.forEach((u: any) => {
for (let i = 0; i < usersCopy.length; i++) {
const existingUser = usersCopy[i];
if (existingUser.userID === u.userID) {
existingUser.connected = u.connected;
break;
}
}
u.self = u.userID === socketUserID;
initUserProps(u);
});
// put the current user first, and sort by username
let sorted = usersCopy.sort((a: any, b: any) => {
if (a.self) return -1;
if (b.self) return 1;
if (a.username < b.username) return -1;
return a.username > b.username ? 1 : 0;
});
setLoggedUsers([...sorted]);
};
return ( <
UsersContext.Provider value = {
{
connectedUsers,
setLoggedUsers,
addUser,
sortUsers
}
} >
{
children
} <
/UsersContext.Provider>
);
};
And the part of ChatBoard.tsx, you can find addUser function initiated whenever user-connected happens. I really don't know why the would array be empty, if it is populated on the first run with users event.
const ChatBoard = (props: Props) => {
const socket = useSocket();
const {
connectedUsers,
setLoggedUsers,
addUser,
sortUsers
} = useUsers();
useEffect(() => {
if (socket == null) return;
socket.on('users', (users) => {
console.log(users);
if (socket.userID) {
const socketID: string = socket ? .userID;
sortUsers(users, socketID);
}
});
socket.on('user-connected', (user: any) => {
console.log(user, 'this user connected!');
const connectingUser = user;
addUser(connectingUser);
});
socket.on('user-disconnected', (userID) => {
console.log('disconnected user');
const users = [...connectedUsers];
users.forEach((u) => {
if (u.userID === userID) {
u.connected = false;
setLoggedUsers([...users]);
}
});
});
return () => {
socket.off('users');
socket.off('user-connected');
};
}, [socket]);
CodeSandbox
So I have found the problem... so with React hooks sometimes a problem occurs called "Stale Closures", which means that React was picking up the old state (empty one, the one that was not yet populated and always returning that one.).
The solution to this problem, in my case is that when you use setState you use it with a callback. Like so, so you always get the latest state.
const addUser = (user: any) => {
setLoggedUsers((oldUsers) => {
const newUsers: any[] = [...oldUsers];
console.log(newUsers);
for (let i = 0; i < newUsers.length; i++) {
const existingUser = newUsers[i];
if (existingUser.userID === user.userID) {
existingUser.connected = true;
return newUsers;
}
}
initReactiveProperties(user);
newUsers.push(user);
return newUsers;
});
};

React - Changing state in context not rerendering DOM

I have useContext which save all loggedUsers in an array. Each user has few properties, one of them is messages array, which stores objects with two properties. I am using this context through custom hook inside of component, like so: const { loggedUsers, setLoggedUsers } = useUsers();. The context looks like this:
export const UsersProvider = ({ children }: Props) => {
const [loggedUsers, setLoggedUsers] = useState<any[]>([]);
const socket = useSocket();
const addUser = (user: any) => {
console.log(typeof loggedUsers);
const users: any[] = loggedUsers;
users.push(user);
setLoggedUsers(users);
};
const sortUsers = (users: any) => {
users.forEach((user: any) => {
user.self = user.userID === socket?.id;
});
let sorted = users.sort((a: any, b: any) => {
if (a.self) return -1;
if (b.self) return 1;
if (a.username < b.username) return -1;
return a.username > b.username ? 1 : 0;
});
setLoggedUsers(sorted);
};
return (
<UsersContext.Provider
value={{ loggedUsers, setLoggedUsers, addUser, sortUsers }}
>
{children}
</UsersContext.Provider>
);
};
User in array looks like this:
{userID: 'Cm6vG0udcV6vl7MEAAAB', userName: 'test123', messages: [{message: 'dsada', fromSelf: true}], self: false, connected: true}
I am updating the context/state like so, but it doesn't force rerender, so my messages between users are not shown. What could be the problem? If I console.log loggedUsers the messages are there, but they do not show up on DOM. If you need more code, let me know.
socket.on('private message', listenerMessage);
const listenerMessage = async (msgInfo: any) => {
console.log(msgInfo);
console.log(msgInfo.from);
const usersList = loggedUsers;
for (let i = 0; i < usersList.length; i++) {
const user = usersList[i];
if (user.userID === msgInfo.from) {
user.messages.push({
message: msgInfo.message,
fromSelf: false,
});
if (user !== state.userID) {
user.hasNewMessages = true;
}
console.log(loggedUsers, 'onprivatemessage');
setLoggedUsers(usersList);
break;
}
}
};
const onMessage = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (state.userName) {
socket?.emit('private-message', {
message: mess,
to: state.userID,
});
const usersList = loggedUsers;
for (let i = 0; i < usersList.length; i++) {
const user = usersList[i];
if (user.userID === state.userID) {
user.messages.push({ message: mess, fromSelf: true });
console.log(loggedUsers, 'onmessage');
}
}
setLoggedUsers(usersList);
}
setMess('');
};
You shouldn't mutate useState's state directly.
Clone it before:
const usersList = [...loggedUsers]
const usersList = loggedUsers.slice()
Here :
setLoggedUsers(usersList);
You are passing the same state array to setLoggedUsers. Cloning the state will solve this.
What you can do is this :
setLoggedUsers(prev=>{
const prevUserList = prev.slice()
//your for loop or anything
return prevUserList;
});
Also here :
const usersList = loggedUsers;
for (let i = 0; i < usersList.length; i++) {
const user = usersList[i];
if (user.userID === state.userID) {
user.messages.push({ message: mess, fromSelf: true });
console.log(loggedUsers, 'onmessage');
}
}
You never update usersList , i don't know if that is wanted.
you using const while you updating the objects.
try using let and not const.
const objects should not be changed.

remove duplication in object array from firebase or filter it? look code for more info

Here I validate if my users status is true, and if they are, I put them in an array. The thing here is that next time it will validate, all those who already was true will be added to the same array. Can it be solved by filter instead of push, or should I take the validation in any other way?
import {
UPDATE_LIST_SUCCESS
} from './types'
var arr = []
export const fetchList = () => {
return (dispatch) => {
firebaseRef.database().ref().child('users')
.on('value', snapshot => {
snapshot.forEach(function (child) {
var data = child.val()
if (child.val().profile.status === true) {
arr.push(data)
}
})
dispatch({ type: UPDATE_LIST_SUCCESS, payload: arr })
})
}
}
You can do it like this:
import {
UPDATE_LIST_SUCCESS
} from './types'
export const fetchList = () => {
return (dispatch) => {
firebaseRef.database().ref().child('users')
.on('value', snapshot => {
var arr = snapshot.filter(function (child) {
return child.val().profile.status === true
}).map(function (child) {
return child.val();
});
dispatch({ type: UPDATE_LIST_SUCCESS, payload: arr })
})
}
}
So here is my not so pretty way of solving it, but it works.
import {firebaseRef} from '../firebase/firebase'
import {
UPDATE_LIST_SUCCESS
} from './types'
export const fetchList = () => {
return (dispatch) => {
const arrayToFilter = []
firebaseRef.database().ref().child('users')
.on('value', snapshot => {
let snap = snapshot.val()
// Get acces to the keys in the object i got from firebase
let keys = Object.keys(snap)
// iterate the keys and put them in an User object
for (var i = 0; i < keys.length; i++) {
let k = keys[i]
let name = snap[k].profile.name
let age = snap[k].profile.age
let status = snap[k].profile.status
let profile_picture = snap[k].profile.profile_picture
let users = {name: '', age: '', status: Boolean, profile_picture: ''}
users.name = name
users.age = age
users.status = status
users.profile_picture = profile_picture
// adding the user object to an array
arrayToFilter.push(users)
}
// filter and creates a new array with users depending if their status is true
let arr = arrayToFilter.filter(child => child.status === true)
dispatch({ type: UPDATE_LIST_SUCCESS, payload: arr })
})
}
}

Categories