React-Admin: Using "getList", I am getting "Error: cannot read property 'map' of undefined" - node.js

With react-admin, i'm trying to get the users list from API Restful Node server,and I have this error:
Error: cannot read property 'map' of undefined
Here's the getUsers in server user.controller.js:
const getUsers = catchAsync(async (req, res) => {
const users = await userService.getUsers(req.query);
const data = users.map(user => user.transform());
const total = users.length;
res.type('json');
res.set('Access-Control-Expose-Headers', 'Content-Range');
res.set('Content-Range', `users 0-2/${total}`);
res.set('X-Total-Count', total);
response = '{ data: ' + JSON.stringify(data) + ', total: ' + total + ' }';
res.send(response);
});
Here the data response received:
{
data: [
{"id":"5e6f5e3b4cf60a67701deeae","email":"admin#test.com","firstname":"Ad","lastname":"Min","role":"admin"},
{"id":"5e6f5e3b4cf60a67701deeaf","email":"test#test.com","firstname":"Jhon","lastname":"Doe","role":"user"}
],
total: 2
}
In react-admin, getList in dataProvider.js:
export default {
getList: (resource, params) => {
console.log(params);
const { field, order } = params.sort;
const query = {
...fetchUtils.flattenObject(params.filter),
sortBy: field
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
return httpClient(url).then(({ headers }, json) => ({
data: json,
total: parseInt(
headers
.get("Content-Range")
.split("/")
.pop(),
10
)
}));
},
Update:
UserList.tsx
import React from "react";
import {
TextField,
Datagrid,
DateInput,
Filter,
List,
EmailField,
SearchInput
} from "react-admin";
import { useMediaQuery, Theme } from "#material-ui/core";
import SegmentInput from "./SegmentInput";
import MobileGrid from "./MobileGrid";
const UserFilter = (props: any) => (
<Filter {...props}>
<SearchInput source="q" alwaysOn />
<DateInput source="createdAt" />
<SegmentInput />
</Filter>
);
const UserList = (props: any) => {
const isXsmall = useMediaQuery<Theme>(theme => theme.breakpoints.down("xs"));
return (
<List
{...props}
filters={<UserFilter />}
sort={{ field: "createdAt", order: "desc" }}
perPage={25}
>
{isXsmall ? (
<MobileGrid />
) : (
<Datagrid optimized rowClick="edit">
<TextField source="id" />
<EmailField source="email" />
<TextField source="firstname" />
<TextField source="lastname" />
<TextField source="role" />
<DateInput source="createdAt" />
<DateInput source="updatedAt" />
</Datagrid>
)}
</List>
);
};
export default UserList;
Here the documentation with somes examples for getList:
https://marmelab.com/react-admin/DataProviders.html#writing-your-own-data-provider
I don't understand, i need help please, what wrong?
Thanks & Regards
Ludo

Here's a detailed explanation of what's happening with the map() in reactjs.
And this source is on point for resolving this in nodejs
In your case, let's take a closer look here:
// user.controller.js
const getUsers = catchAsync(async (req, res) => {
// Actually, it might be wiser to first declare users
+ let users = [];
// You await for users (querying) to be resolved or rejected, cool
- const users = await userService.getUsers(req.query);
// And we then we assign "users" as before
+ users = await userService.getUsers(req.query);
// But here, map() requires users to be defined before iteration.
// So for this to work, we need the value of users to be resolved first, right?
- const data = users.map(user => user.transform());
// Could you change the above line to this, and see?
// This doesn't because we gave map(), a synchronous parameter (function)
- const data = Promise.all(users.map(user => user.transform()));
// Ok, break this up for easier understanding
// let's declare a userList, where we give map() an "async" parameter
// At this point, userList will be an "Array of Promises"
+ const userList = users.map(async user => user.transform());
// Now we resolve all the Promises, and we should have our clean data
+ const data = await Promise.all(userList);
});
With that change, we make sure that map() runs after our users is resolved.
Does that work for you? Let me know.

Related

TypeError: skills?.filter is not a function

Making A portfolio website using react and nextjs, currently i am using hygraph to pull a list of skills.
attempted to pull the skills list, succeeded but then when it gets filtered the error above occurs
Skills.tsx
`
import type { NextPage } from "next";
import { ISkills } from "../typings";
import { Skill } from "./Skill";
interface ISKillsProps {
skills: ISkills[];
}
export const Skills: NextPage<ISKillsProps> = ({ skills }) => {
const languages = skills?.filter(skill => skill?.fieldType?.toLowerCase() === "languages");
const frontend = skills?.filter(skill => skill?.fieldType?.toLowerCase() === "frontend");
const uilibraries = skills?.filter(skill => skill?.fieldType?.toLowerCase() === "uilibraries");
const headlessCms = skills?.filter(skill => skill?.fieldType?.toLowerCase() === "headless cms");
const testing_tools = skills?.filter(
skill =>
skill?.fieldType?.toLowerCase() === "testing" || skill?.fieldType?.toLowerCase() === "tools"
);
const familiar = skills?.filter(skill => skill?.proficient === false);
return (
<>
<h1 className="skills_heading">Skills</h1>
<div className="skills_box">
<Skill skills={languages} skill="Languages" />
<Skill skills={frontend} skill="Frontend" />
<Skill skills={uilibraries} skill="UI Libraries" />
<Skill skills={headlessCms} skill="Headless CMS" />
<Skill skills={testing_tools} skill="Testing & Tools" />
<Skill skills={familiar} skill="Familiar" />
</div>
</>
);
};
services.ts
import { GraphQLClient, gql } from "graphql-request";
export const graphcms = new GraphQLClient(
"https://api-ca-central-1.hygraph.com/v2/cl9p263ni15rr01us16va0ugq/master"
);
export const QUERY = gql`
{
skills(orderBy: uniqueId_ASC) {
uniqueId
skill
id
proficient
fieldType
image {
url
}
url
}
}
`;
skills.tsx takes the query result and filters it before adding it as a html element.
services.ts makes a query to hygraph for my skills list.

I am trying to display data from an external API, how can I fix the issue in my code?

This is the error message I am getting
TypeError: undefined is not an object (evaluating 'drinkList.drinks[0]')
How can I fix the error so that I can use the app to fetch data from the external api?
This is my drink.js code:
import React, { useEffect, useState } from "react";
import axios from "axios";
import Drinks from "./Drinks";
function Home() {
const [drinkName, setDrinkName]= useState([]);
const [drinkList, setDrinkList] = useState([]);
const drinksURL = `https://www.thecocktaildb.com/api/json/v1/1/search.php?s=${drinkName}`;
const handleChangeDrink= e => {
setDrinkName(e.target.value);
}
const getDrink = () => {
axios
.get(drinksURL)
.then(function (response) {
setDrinkList(response.data);
console.log(drinksURL);
})
.catch(function (error) {
console.warn(error);
});
};
return (
<main className="App">
<section className="drinks-section">
<input
type="text"
placeholder="Name of drink (e.g. margarita)"
onChange={handleChangeDrink}
/>
<button onClick={getDrink}>Get a Drink Recipe</button>
<Drinks drinkList={drinkList} />
</section>
</main>
);
}
export default Home;
This is my Drink.js code:
import React from "react";
function Drinks({ drinkList }) {
if (!drinkList) return <></>;
return (
<section className="drinkCard">
<h1>{drinkList.drinks[0].strDrink}</h1>
</section>
);
}
export default Drinks;
Couple of problems here...
drinkName is initialised as an array but it appears to be expecting a string
drinkList is initialised as an array but the data from the API is an object. You may want to assign the drinks array from the response instead
drinksUrl is never updated
An empty array is still truthy
Some easy fixes
const [drinkName, setDrinkName]= useState(null) // initialise as null
const [drinkList, setDrinkList] = useState([])
// don't include the "s" parameter here
const drinksURL = "https://www.thecocktaildb.com/api/json/v1/1/search.php"
const getDrink = () => {
// pass drinkName as a param
axios.get(drinksURL, {
params: { s: drinkName }
}).then(res => {
// note that we're extracting `drinks`
setDrinkList(res.data.drinks)
}).catch(console.warn)
}
and in Drink.js
function Drinks({ drinkList }) {
// check the array length
return drinkList.length && (
<section className="drinkCard">
<h1>{drinkList[0].strDrink}</h1>
</section>
)
}

Cannot import meta data from mdx file in getStaticProps nextjs

I have a problem while trying to require meta data from an mdx file in my Next.js project.
MDX file example:
export const meta = {
title: 'title',
date: new Date('May 09, 2019'),
};
Content
export const getStaticProps = async context => {
const postFilenames = await recRead(process.cwd() + '/pages', ['*.tsx']);
const postMetadata = await Promise.all(
postFilenames.map(async p => {
const { meta } = require(p);
return meta;
}),
);
return {
props: {
postMetadata: postMetadata,
},
};
};
It is a modified version of this: https://sarim.work/blog/dynamic-imports-mdx. While accessing a website I get an error:
Cannot find module '/home/oliwier/webDev/oliwierwpodrozy/pages/balkany/1.mdx'.
BTW recRead is this https://www.npmjs.com/package/recursive-readdir.
What is going on? Outside of getStaticProps I can import data.
I found something ridiculous when trying to solve the problem.
// 1)console.log(postFilenamesToImport[0]);
// 2) const meta = await import('../pages/wielka-brytania/1.mdx');
// 3) const meta = await import(postFilenamesToImport[0]);
// console.log(meta.meta);
shows: ../pages/wielka-brytania/1.mdx which is a string
This one works
But this one doesn't. Shows error: Error: Cannot find module '../pages/wielka-brytania/1.mdx'
It is not a const problem. It is written for tests and i know that using 2) and 3) together would cause problem. This error occurs when 1) is commented.
You can import metadata like follows.
First, we export the metadata from within the .mdx file
// in /pages/posts/example.mdx
import Layout from "../../components/layout";
export const meta = {
title: "example",
date: "2021-12-27",
slug: "example",
};
Lorem ipsum.
export default ({ children }) => (
<Layout subtitle={meta.title}>{children}</Layout>
);
Then we use getStaticProps to scan the file system at runtime, importing each .mdx file as a module, then mapping out their metadata exports. Since we are displaying the metadata on the index page, we will pop index.js from the array.
// in /pages/posts/index.js
export const getStaticProps = async (context) => {
const postDirectory = path.join(process.cwd(), "src/pages/posts");
let postFilenames = fs.readdirSync(postDirectory);
postFilenames = removeItemOnce(postFilenames, "index.js");
const postModules = await Promise.all(
postFilenames.map(async (p) => import(`./${p}`))
);
const postMetadata = postModules.map((m) => (m.meta ? m.meta : null));
return {
props: {
postMetadata: postMetadata,
},
};
// thanks https://sarim.work/blog/dynamic-imports-mdx
};
function removeItemOnce(arr, value) {
var index = arr.indexOf(value);
if (index > -1) {
arr.splice(index, 1);
}
return arr;
// thanks https://stackoverflow.com/a/5767357/13090245
}
Here is one way of using the prop to render a list of posts
// in /pages/posts/index.js
export default function PostsIndex({ postMetadata }) {
return (
<Layout subtitle="blog index">
<ul>
{postMetadata.map(({ slug, date, title }) => (
<li key={slug}>
<Link href={`/posts/${slug}`} a={title} />
<br />
{date}
</li>
))}
</ul>
</Layout>
);
}

Cannot add a new article in a simple blog built with react

I have built a simple blog with React. I am having an issue, whenever I try and add a new article. I get the below error.
TypeError: Cannot read property 'upvotes' of null
The article page code is below:
const ArticlePage = ({ match }) => {
const name = match.params.name;
const article = ArticleContent.find(article => article.name === name);
const [articleInfo, setArticleInfo] = useState({ upvotes: -1, comments: [] });
useEffect(() => {
const fetchData = async () => {
const result = await fetch(`/api/articles/${name}`);
const body = await result.json();
setArticleInfo(body);
}
fetchData();
}, [name]);
if (!article) return <notFoundPage />
const otherArticles = ArticleContent.filter(article => article.name !== name);
return (
<>
<h1>{article.title}</h1>
<UpvotesSection articleName={name} upvotes={articleInfo.upvotes} setArticleInfo={setArticleInfo} />
{article.content.map((paragraph, key) => (
<p key={key}>{paragraph}</p>
))}
<CommentsList comments={articleInfo.comments} />
<AddCommentForm articleName={name} setArticleInfo={setArticleInfo} />
<h3>Other Articles:</h3>
<ArticlesList articles={otherArticles} />
</>
);
}
That is where the error tells me to look. I have tried to comment the upvotes section out, it just changes the error to comments. I comment them both out and the article renders but it has no comments or upvotes.
You get the error because your articleInfo is null.
That's why it doesn't find upvotes or comments in your return method.
Your initial state contains an object { upvotes: -1, comments: [] }. So that's not the problem. The only place where this can go wrong is where you call setArticleInfo.
Have you tried logging your body variable?
My guess is that your body variable is null, and by calling setArticleInfo(body) you set your articleInfo to be null hence why it throws an error.

fetch not returning data in react

I'm new to react, i'm having difficulty getting data for a single book out of list, be passed through via axios' get method.
I think it has something to do with the url, but I have been unable to get fix it.
Here's my code:
export function loadBook(book){
return dispatch => {
return axios.get('http://localhost:3000/api/books/book/:id').then(book => {
dispatch(loadBookSuccess(book.data));
console.log('through!');
}).catch(error => {
console.log('error');
});
};
}
//also tried this
export function loadBook(id){
return dispatch => {
return axios.get('http://localhost:3000/api/books/book/' + {id}).then(book => {
dispatch(loadBookSuccess(book.data));
console.log('through!');
}).catch(error => {
console.log('error');
});
};
}
Html code that contains a variable link to each individual book
<div className="container">
<h3><Link to={'/book/' + book._id}> {book.title}</Link></h3>
<h5>Author: {book.author.first_name + ' ' + book.author.family_name}</h5>
<h4>Summary: {book.summary}</h4>
<BookGenre genre={genre} />
</div>
link in Route:
<Route path="/book/:id" component={BookPage} />
Edit: code for the book component
class BookPage extends React.Component{
render(){
const book = this.props;
const genre = book.genre;
console.log(book);
return(
<div>
<div>
<h3> {book.title}</h3>
<h5>Author: {book.author.first_name + ' ' + book.author.family_name}</h5>
<h4>Summary: {book.summary}</h4>
<BookGenre genre={genre} />
</div>
</div>
);
}
}
BookPage.propTypes = {
book: PropTypes.object.isRequired
};
//setting the book with mapStateToProps
function mapStateToProps (state, ownProps){
let book = {title: '', author: '', summary: '', isbn: '', genre: []};
const bookid = ownProps.params._id;
if(state.books.length > 0){
book = Object.assign({}, state.books.find(book => book.id));
}
return {
book: book
};
}
function mapDispatchToProps (dispatch) {
return {
actions: bindActionCreators(loadBook, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(BookPage);
Instead of doing this:-
axios.get('http://localhost:3000/api/books/book/' + {id})
You should do like this:-
axios.get(`http://localhost:3000/api/books/book/${id}`)
So your action.js might look like this:-
export function loadBook(id){
const request = axios.get(`http://localhost:3000/api/books/book/${id}`);
return dispatch => {
request.then(book => {
dispatch(loadBookSuccess(book.data));
}).catch(error => {
console.log('error');
})
};
}
Since the id, you have passed it seems to be a string so it can be concatenated using ES6 template strings and make sure you wrap your strings in backtick . or you can do it by + operator, also make sure you pass id as a parameter in your loadbook function so that you can join it to your URL.
Figured out the solution to this problem.
My mistake was that I failed to send the id of the item I along with the api call.
Using componentDidMount and sending the dynamic id from the url params solved this problem for me.
Thank you, #Vinit Raj, I guess I was too much of a rookie then.

Resources