javascript array contains values called empty - javascript

Not sure what is going on but when I update my store state by deleting items, it's saying that the items are empty.
see screenshot
and therefore the array is still of the same length?
these are my functions:
export const deleteUserFromFirebase = ({ id, name, username, email }) => {
return firebase
.database()
.ref("/users/" + id)
.set({
id: null
});
};
export const deleteUserFromStoreThenUpdateFirebase = user => {
return (dispatch, getState) => {
return dispatch(deleteUser(user)).then(() => {
return deleteUserFromFirebase(user);
});
};
};
export const deleteUser = user => {
return async dispatch => {
dispatch({ type: DELETE_USER, user: user });
};
};
and I call deleteUserFromStoreThenUpdateFirebase from a button click
why would it be doing this??
also my reducer does this:
case DELETE_USER: {
return {
...state,
users: state.users.filter(u => u.id !== action.user.id)
};
}
well confused right now
also firebase is showing the correct amount of nodes and when i refresh it's suppose to be pulling from firebase but it pulls the empty ones too

Related

remove the TMDB API

I am currently working with a clone of a streaming platform, it turns out that this clone has the TMDB API integrated and I want to remove it to store the objects returned by this api in a firebase database, but I am a little confused.
In my Firebase file, I have a promise that returns an array of objects and it looks like this:
export const getGamesDocument = () => {
return new Promise((resolve, reject) => {
const documents = [];
firestore
.collection("games")
.get()
.then((snapshot) => {
snapshot.forEach((doc) => {
const documentData = doc.data();
documentData.id = doc.id;
documents.push(documentData);
});
resolve(documents);
})
.catch((error) => {
reject(error);
});
});
};
So far everything is going well where I am getting confused is in this redux code since I have no knowledge of the subject:
export const fetchAdventureMoviesRequest = () => ({
type: moviesActionTypes.FETCH_ADVENTURE_MOVIES_REQUEST,
});
export const fetchAdventureMoviesSuccess = (adventureMovies, isPage) => ({
type: isPage
? moviesActionTypes.FETCH_ADVENTURE_MOVIES_SUCCESS
: moviesActionTypes.LOAD_MORE_ADVENTURE_MOVIES_SUCCESS,
payload: adventureMovies,
});
export const fetchAdventureMoviesFailure = error => ({
type: moviesActionTypes.FETCH_ADVENTURE_MOVIES_FAILURE,
payload: error,
});
export const fetchAdventureMoviesAsync = (fetchUrl, isPage) => {
return dispatch => {
dispatch(fetchAdventureMoviesRequest());
axios
.get(fetchUrl)
.then(res => {
const adventureMovies = res.data.results.map(el => ({
...el,
isFavourite: false,
}));
if (isPage) {
dispatch(fetchAdventureMoviesSuccess(adventureMovies, isPage));
} else dispatch(fetchAdventureMoviesSuccess(adventureMovies));
})
.catch(error => {
const errorMessage = error.message;
dispatch(fetchAdventureMoviesFailure(errorMessage));
});
};
};
I want to remove the array of objects that are obtained in the constant "adventureMovies" and replace it with the array of objects that I obtain in the aforementioned promise.

Firestore iterate over an object within a document's data REACT.JS

I'm trying to add some data inside the bookChapters path but it doesn't work, some suggestions?
export const createNewChapter = (bookId, inputText) => {
return async dispatch => {
dispatch(createNewChapterStart());
try {
const chaptersList = [];
firebase
.firestore()
.doc(`Users/${bookId}/bookChapters/${inputText}`)
.onSnapshot(querySnapshot => {
querySnapshot.forEach(doc => {
const chapters = doc.data().bookChapters;
Object.keys(chapters).forEach(k => {
chaptersList.push({ key: k, name: inputText });
});
});
});
dispatch(createNewChapterSuccess(inputText));
} catch (error) {
dispatch(createNewChapterFail(error));
console.log(error);
}
};
};
When you use collection(), it returns a CollectionReference. In this case, it's pointing towards a sub-collection 'bookChapters' but it's a map as in your screenshot. If you want to iterate over that map, you need to fetch that document first and then read the bookChapters field.
const chaptersList = [];
firebase
.firestore()
.doc(`Users/${bookId}`)
.onSnapshot(querySnapshot => {
querySnapshot.forEach(doc => {
const chapters = doc.data().bookChapters
Object.keys(chapters).forEach((k) => {
chaptersList.push({key: k, name: chapters[k]});
})
});
});
It might be better to store the list as an Array if you want to store all chapters in the same doc.
If you were trying to create a sub-collection, you can create one by clicking this button:

How to pass return value from component to another one

I have a const that assembles a get (in another component) and returns me a verification code, this is: response.data.verifyCode
This component is called through a submit on another component.
I need to get this value in my another const, which is below:
export const sendCode = (id, username) => (dispatch) => {
dispatch({ some code here });
return registerAccount
.sendCode(id, username)
.then((response) => {
dispatch({ payload: response.data.verifyCode });
return response.data;
})
.catch(() => {
return null;
});
};
export const getCodeAndVerify = (id, userCode) => (dispatch) => {
dispatch({ some code here });
const getVerifyCode = // I need to get response.data.verifyCode from sendCode above
// I try to use
// const getVerifyCode = { verifyCode: sendCode() };
// but this returns [object object]
return registerAccount
.getCodeAndVerify(id, userCode, getVerifyCode)
.then(() => {
// some code here
})
.catch(() => {
// some code here
});
};
That is, I need to get the verifyCode from the return from the superior const and use it in the other const, but I'm not sure how to do that. Can someone help me?
Asynchronous actions (I'm assuming thunks) also receive a getState second argument after dispatch. Assuming there's a reducer to handle the verifyCode send code success, you can access the store and retrieve the verifyCode value in getCodeAndVerify.
export const sendCode = (id, username) => (dispatch) => {
dispatch({ some code here });
return registerAccount
.sendCode(id, username)
.then((response) => {
dispatch({
type: 'VERIFY_CODE_SUCCESS', // <-- action object needs type
payload: response.data.verifyCode,
});
return response.data;
})
.catch(() => {
return null;
});
};
export const getCodeAndVerify = (id, userCode) => async (dispatch, getState) => {
dispatch({ type: TYPES.PASS_CREATION_REQUESTED });
const getVerifyCode = getState().path.to.verifycode; // <-- retrieve from state
return registerAccount
.getCodeAndVerify(id, userCode, getVerifyCode)
.then(() => {
// some code here
})
.catch(() => {
// some code here
});
};

setState in nested async function - React Hooks

How can I build a function which gets some data asynchronously then uses that data to get more asynchronous data?
I am using Dexie.js (indexedDB wrapper) to store data about a direct message. One thing I store in the object is the user id which I'm going to be sending messages to. To build a better UI I'm also getting some information about that user such as the profile picture, username, and display name which is stored on a remote rdbms. To build a complete link component in need data from both databases (local indexedDB and remote rdbms).
My solution returns an empty array. It is being computed when logging it in Google Chrome and I do see my data. However because this is not being computed at render time the array is always empty and therefor I can't iterate over it to build a component.
const [conversations, setConversations] = useState<IConversation[]>()
const [receivers, setReceivers] = useState<Profile[]>()
useEffect(() => {
messagesDatabase.conversations.toArray().then(result => {
setConversations(result)
})
}, [])
useEffect(() => {
if (conversations) {
const getReceivers = async () => {
let receivers: Profile[] = []
await conversations.forEach(async (element) => {
const receiver = await getProfileById(element.conversationWith, token)
// the above await is a javascript fetch call to my backend that returns json about the user values I mentioned
receivers.push(receiver)
})
return receivers
}
getReceivers().then(receivers => {
setReceivers(receivers)
})
}
}, [conversations])
/*
The below log logs an array with a length of 0; receivers.length -> 0
but when clicking the log in Chrome I see:
[
0: {
avatarURL: "https://lh3.googleusercontent.com/..."
displayName: "Cool guy"
userId: "1234"
username: "cool_guy"
}
1: ...
]
*/
console.log(receivers)
My plan is to then iterate over this array using map
{
receivers && conversations
? receivers.map((element, index) => {
return <ChatLink
path={conversations[index].path}
lastMessage={conversations[index].last_message}
displayName={element.displayName}
username={element.username}
avatarURL={element.avatarURL}
key={index}
/>
})
: null
}
How can I write this to not return a empty array?
Here's a SO question related to what I'm experiencing here
I believe your issue is related to you second useEffect hook when you attempt to do the following:
const getReceivers = async () => {
let receivers: Profile[] = []
await conversations.forEach(async (element) => {
const receiver = await getProfileById(element.conversationWith, token)
receivers.push(receiver)
})
return receivers
}
getReceivers().then(receivers => {
setReceivers(receivers)
})
}
Unfortunately, this won't work because async/await doesn't work with forEach. You either need to use for...of or Promise.all() to properly iterate through all conversations, call your API, and then set the state once it's all done.
Here's is a solution using Promise.all():
function App() {
const [conversations, setConversations] = useState<IConversation[]>([]);
const [receivers, setReceivers] = useState<Profile[]>([]);
useEffect(() => {
messagesDatabase.conversations.toArray().then(result => {
setConversations(result);
});
}, []);
useEffect(() => {
if (conversations.length === 0) {
return;
}
async function getReceivers() {
const receivers: Profile[] = await Promise.all(
conversations.map(conversation =>
getProfileById(element.conversationWith, token)
)
);
setReceivers(receivers);
}
getReceivers()
}, [conversations]);
// NOTE: You don't have to do the `receivers && conversations`
// check, and since both are arrays, you should check whether
// `receivers.length !== 0` and `conversations.length !== 0`
// if you want to render something conditionally, but since your
// initial `receivers` state is an empty array, you could just
// render that instead and you won't be seeing anything until
// that array is populated with some data after all fetching is
// done, however, for a better UX, you should probably indicate
// that things are loading and show something rather than returning
// an empty array or null
return receivers.map((receiver, idx) => <ChatLink />)
// or, alternatively
return receivers.length !== 0 ? (
receivers.map((receiver, idx) => <ChatLink />)
) : (
<p>Loading...</p>
);
}
Alternatively, using for...of, you could do the following:
function App() {
const [conversations, setConversations] = useState<IConversation[]>([]);
const [receivers, setReceivers] = useState<Profile[]>([]);
useEffect(() => {
messagesDatabase.conversations.toArray().then(result => {
setConversations(result);
});
}, []);
useEffect(() => {
if (conversations.length === 0) {
return;
}
async function getReceivers() {
let receivers: Profile[] = [];
const profiles = conversations.map(conversation =>
getProfileById(conversation.conversationWith, token)
);
for (const profile of profiles) {
const receiver = await profile;
receivers.push(receiver);
}
return receivers;
}
getReceivers().then(receivers => {
setReceivers(receivers);
});
}, [conversations]);
return receivers.map((receiver, idx) => <ChatLink />);
}
i think it is happening because for getReceivers() function is asynchronous. it waits for the response, in that meantime your state renders with empty array.
you can display spinner untill the response received.
like
const[isLoading,setLoading]= useState(true)
useEffect(()=>{
getReceivers().then(()=>{setLoading(false)}).catch(..)
} )
return {isLoading ? <spinner/> : <yourdata/>}
Please set receivers initial value as array
const [receivers, setReceivers] = useState<Profile[]>([])
Also foreach will not wait as you expect use for loop instead of foreach
I am not sure it is solution for your question
but it could help you to solve your error

Merging to two API calls into the same object with Redux

I am making a table of data that has users. One of the details in each row is their online availability. To do this I need to call two APIs (one for users and one for online availability). The online availability call needs to be called continuously at setintervals to make it update live. I am very confused about how and where to do this. I have merged the two API's by matching their Id's, but I do not know how to constantly call second API without calling the first continuously, as it would use alot of memory. Also I dont know where to do this within redux. Do i do it in the action? reducer? and why? I want this API state to be stored in the store so I can access this merged API across all components of the APP.
// action files
export const getUsers = ()=> async dispatch =>{
const usersResponse = await axios.get(users);
const userData = usersResponse.data.result;
dispatch({
type: GET_USERS,
payload: userData
})
}
export const getAvailability = () => async dispatch =>{
const availabilityResponse = await axios.get(availability);
const availabilityData = availabilityResponse.data.result;
dispatch({
type: GET_AVAILABILITY,
payload: availabilityData
})
}
// reducers
initial state = {
getUsers: [],
getAvailability: []
}
export default function(state = initialState, action) {
switch (action.type) {
case GET_USERS:
return {
...state,
getUsers: action.payload
};
case GET_AVAILABILITY:
return {
...state,
getAvailability: action.payload
};
default:
return state;
}
}
//component
async componentDidMount() {
await this.props.getUsers();
await this.props.getAvailability();
let availabilityObject = {};
this.props.availability.map(availability => {
availabilityObject[availability.uuid] = availability;
});
if (typeof this.props.users != "undefined") {
this.props.users.map(user => {
user.availability = availabilityObject[user.uuid];
});
}
console.log(this.props.users)
//this doesn't show a merged Object of users and availability
}
const mapStateToProps = (state) => ({
users: state.getUsers,
status: state.getAvailability
});

Categories