How do I get a series of documents from Firestore and return them all in an array? - reactjs

My Firestore data has the following structure:
topics collection containing documents with the following structure:
id: string
name: string
tag: string
infoID: string
these map to an ITopic interface:
interface ITopic {
id: string
name: string
tag: string
infoID: string
}
infos collection containing documents with the following structure:
id: string
content: string
topicID: string or null
which map to:
interface IInfo {
id: string
content: string
topicID: string | null
}
So each topic document has an associated info document, and vice-versa (ie a one-to-one mapping).
I've retrieved all my topic documents from the database and now I want to get the associated info for each one, all in an array. So each item in the array would be an object with topic and info fields:
interface ITopicInfo {
topic: ITopic
info: IInfo
}
I've tried this (topics is the existing array of all my topics):
async function getTopicsInfo(db: Firestore, topics:Array<ITopic>) {
try {
let topicsInfo:Array<ITopicInfo> = topics.map((topic) => {
const infoRef = doc(db, 'infos', topic.infoID);
const infoSnap = await getDoc(infoRef);
if (infoSnap.exists()) {
const result = infoSnap.data();
const newTopicInfo:ITopicInfo = {
topic: topic,
info: {
id: result.id
content: result.content
topicID: result.topicID
},
};
return newTopicInfo;
} else return null;
});
return topicsInfo;
} catch (error) {
throw error;
}
}
However, I'm getting a TypeScript error saying
Type '(Promise<ITopicTree | null> | null)[]' is not assignable to type '(ITopicTree | null)[]'.
it looks like it's returning the Promise rather than the result, basically. How do I fix this?

did you try to return map directly :
instead of doing this :
let topicsInfo:Array<ITopicInfo> = topics.map((topic) => { ...
Just do this :
return topics.map((topic) => { ...
UPDATE :
async function getTopicsInfo(db: Firestore, topics:Array<ITopic>): ITopicInfo[] { ...

Related

Insert list data over the iteration(map)

Here I am trying to modify my data over the iteration and send some result to API call.
The API Call receives a request with a structured data format which is
{ list: [{ id: "1", name: "Hello" }, ... ] }
Somehow I managed to call the API with single data ( const params in my current code, it only accepts single data).
But now it has to be done with multiple data something like this:
{ list: [{ id: "1", name: "Hello" }, { id: "22", name: "Ed" }, { id: "36", name: "Jason" } ... ] }
Here is my current code
const [table, setTalbe] = useState(..); // assume, we have some table data here
const processNow = () => {
let id = 0;
let name = '';
// if table length is greater than 1, we go for the loop.
if (table.length >= 1) {
table.map(data => {
id = data.userId;
name = data.userName;
});
//insert table data to params, here I want to add whole table data into "list"
//the final result of this list should be something like this
//ex ) list: [{ id: '123', name: 'Josh' }, { id: '125', name: 'Sue' }, { id: '2222', name: 'Paker' } ...],
// but how??
const params: any = {
list: [
{
id: id,
name: name
},
],
};
//send PUT reqeust with params
axios
.put(
'/api/v1/tosent',
params,
)
.then(res => {
console.log('The response', res);
})
.catch(err => {
console.log('The error: ', err);
});
}
};
but I'm stuck with it, please help me to finish this code to work properly.
need your kind advice.
Array.prototype.map returns a new array with the function you pass applied to every element. You should study the MDN documentation on map to understand its use.
Your current code does nothing with the map return value:
table.map(data => {
id = data.userId;
name = data.userName;
});
You probably assumed .map would mutate the data, as in change it in place. Instead, the whole operation returns a new array.
It looks like you want to do:
const list = table.map(data => {
return {
id: data.userId,
name: data.userName
}
});
This is applying a function to every element in the array that will map each element to a new object, matching your question, with an id and name key. Then it looks like you want to pass the returned value of map (which we named list above) to your call:
const params: any = {
list: list
};

Getting a type error from my map function

I am trying to create an array containing only id's from the state.words object (below)
MY REQUIRED OUTPUT
['word-1','word-2','word-3','word-4','word-5','word-6','word-7']
My starting data structure looks like this
words: {
'word-1': { id: 'word-1', content: 'Jimmy Yukka' ,url:'www.paulayling.me'},
'word-2': { id: 'word-2', content: 'INXS' ,url:'www.paulayling.me'},
'word-3': { id: 'word-3', content: 'Up North' ,url:'www.paulayling.me'},
'word-4': { id: 'word-4', content: 'Prince' ,url:'www.paulayling.me'},
'word-5': { id: 'word-5', content: 'Magic Moose' ,url:'www.paulayling.me'},
'word-6': { id: 'word-6', content: 'Salt n Pepper' ,url:'www.paulayling.me'},
'word-7': { id: 'word-7', content: 'Maddonna' ,url:'www.paulayling.me'}
}
The problem I have is that because it is an object array based methods will not give me the result I need eg below does not work - but I need an equivalent for an object of objects
My function is below
addNewWord() {
var currentOrder = this.state.words.map(function (book) {
return book.id
})
console.log('words stuff', currentOrder)
}
I get the error
TypeError: this.state.words.map is not a function
The function is called from the componentDidMount() function if it is relavant
map can only be used on arrays. If you want to list the keys in your words object, you can do it this way:
const result = Object.keys(this.state.words);
This will return your required output

Graphql mutation query : how to access the data elements

const MUTATION_QUERY = gql`
mutation MUTATION_QUERY(
$name: bigint!
) {
insert_name(
objects: {
name: $name
}
) {
returning {
id
name
}
}
}
`;
const [onClick, { error, data }] = useMutation<{}, {}>(MUTATION_QUERY, {
variables: {
name: 1234,
},
});
My mutation query is inserting name in my table and autogenerating the id. On console logging the data variable I can view the fields id and name in the data object. But I am not able to access them them individually. How can I console.log "id". Thank you.
the console.log(data) looks like : {insert_name: {...}}
which expands to :
insert_name:
returning: Array(1)
0: {id: 1, name: 1234}
length: 1
_proto_: Array(0)
_typename: "insert_name_mutation_response
You can access the fields of an object with .
For example, if your object looks like this -
data = {
id: 1,
name: 'Jane',
}
You can get just the id with data.id
This works no matter how many layers deep your object may go, so take this example -
data = {
person: {
id: 1,
name: 'Jane',
}
}
You could get the id of person with data.person.id.
console.log(data.insert_name.returning[0].id) will give you the id returned.
For it to work in typescript we need to change the query to add the return type of data
const [onClick, { error, data }] = useMutation<{ReturnInsertNameProps}, {}>(MUTATION_QUERY, {
variables: {
name: 1234,
},
});
interface ReturnInsertNameProps {
insert_name: ReturnQueryProps;
}
interface ReturnProps {
returning: MessageProps[];
}
interface NameProps {
id: number;
name: number;
}
We can also use onCompleted method provided in useMutation if we want to process the result of the query.

DynamoDb: UpdateExpression for updating arrays

Env: NodeJS service using aws-sdk for interacting with DynamoDb.
Problem: When I set an attribute of an item to an array, it is saved as a string. I expect x: ['1'] but I get x: '1'. I believe this is because I'm incorrectly writing my UpdateExpression/ExpressionAttributeValues.
Situation: I have a table with a field called users. Users is an array of uuids that can be updated. An example of an item in the table:
{ x_name: 'Hello',
owner: '123456',
x_uuid: '1357911',
users: []
}
I want to update the users array with a user uuid. To my update function I pass through:
{ users: ['13245395'] }
The update function (data is { users: ['13245395'] }):
updateX(data, { x_uuid }) {
if (!x_uuid) {
throw new Error('No x_uuid supplied')
}
// new doc client
const docClient = new AWS.DynamoDB.DocumentClient();
var params = {
TableName: this.table,
Key: {
'x_uuid': x_uuid
},
UpdateExpression: "set users = :users",
ExpressionAttributeValues:{
":users": `${data.users}`
},
ReturnValues:"ALL_NEW"
};
return new Promise((resolve, reject) =>
docClient.update(params, (error, x) => {
return error ? reject(error) : resolve(x)
})
)
}
}
The result I get is
{ x_name: 'Hello',
owner: '123456',
x_uuid: '1357911',
users: '13245395'
}
but what I expected was:
{ x_name: 'Hello',
owner: '123456',
x_uuid: '1357911',
users: ['13245395']
}
Previously tried:
wrapping data.users in an array when creating params (works for the first id but the second id added gets appended to the same string as the first so it looks like ['123,456'] instead ['123', '456'].
UpdateExpression: "set users = :users",
ExpressionAttributeValues:{
":users": [${data.users}]
},
Using the "L" and "S" data types to determine that it's an array of strings, i.e.
UpdateExpression: "set users = :users",
ExpressionAttributeValues:{
":users": { "L": { "S":${data.users} } }
},
You are converting your users array to a string
":users": `${data.users}`
Try
":users": data.users
This will set users to the array in data.users

Adding property to nested objects in Flow causes errors

I can add a property to a flat object in Flow without any errors.
View demo
But if I add a property to a nested object using the same pattern, Flow throws an error:
/* #flow */
type Content = {
fields: {
slug: string,
},
frontmatter: {
title: string,
}
};
type CustomContent = {
fields: {
slug: string,
},
frontmatter: {
title: string,
// This is the new property:
headerImage: string,
}
};
function doSomethingWithContent(content: Content) {
return [
content.fields.slug,
content.frontmatter.title,
];
}
function doSomethingWithCustomContent(content: CustomContent) {
return [
content.fields.slug,
content.frontmatter.title,
content.frontmatter.headerImage,
];
}
Error:
doSomethingWithContent(customContent);
^ Cannot call `doSomethingWithContent` with `customContent` bound to `content` because property `headerImage` is missing in object type [1] but exists in object type [2] in property `frontmatter`.
View demo
For this to work, the frontmatter property needs to be marked as covariant, e.g.
type Content = {
fields: {
slug: string,
},
frontmatter: {
title: string,
}
};
to
type Content = {
fields: {
slug: string,
},
+frontmatter: { // Note the "+" prefix here
title: string,
}
};
If Flow didn't error on this, it would be perfectly valid for your function to do
function doSomethingWithContent(content: Content) {
content.frontmatter = { title: "" };
which make the type of content still a valid Content, but an invalid CustomContent object. By marking the property covariant, it essentially makes the frontmatter property read-only.

Resources