TypeError: skills?.filter is not a function - node.js

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.

Related

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>
);
}

GSAP timeline needed on every page in Gatsby

My Gatsby site use the same GSAP timeline on every page, so I want to stay DRY and my idea is to include my timeline in my Layout component in that order.
But I don't know how to pass refs that I need between children and layout using forwardRef.
In short, I don't know how to handle the sectionsRef part between pages and layout.
sectionsRef is dependant of the page content (children) but is needed in the timeline living in layout.
How can I share sectionsRef between these two (I tried many things but always leading to errors)?
Here's a codesandbox without the refs in the Layout:
https://codesandbox.io/s/jolly-almeida-njt2e?file=/src/pages/index.js
And the sandbox with the refs in the layout:
https://codesandbox.io/s/pensive-varahamihira-tc45m?file=/src/pages/index.js
Here's a simplified version of my files :
Layout.js
export default function Layout({ children }) {
const containerRef = useRef(null);
const sectionsRef = useRef([]);
sectionsRef.current = [];
useEffect(() => {
gsap.registerPlugin(ScrollTrigger);
const scrollTimeline = gsap.timeline();
scrollTimeline.to(sectionsRef.current, {
x: () =>
`${-(
containerRef.current.scrollWidth -
document.documentElement.clientWidth
)}px`,
ease: 'none',
scrollTrigger: {
trigger: containerRef.current,
invalidateOnRefresh: true,
scrub: 0.5,
pin: true,
start: () => `top top`,
end: () =>
`+=${
containerRef.current.scrollWidth -
document.documentElement.clientWidth
}`,
},
});
}, [containerRef, sectionsRef]);
return (
<div className="slides-container" ref={containerRef}>
{children}
</div>
);
}
index.js (page)
import { graphql } from 'gatsby';
import React, { forwardRef } from 'react';
import SectionImage from '../components/sections/SectionImage';
import SectionIntro from '../components/sections/SectionIntro';
import SectionColumns from '../components/sections/SectionColumns';
const HomePage = ({ data: { home } }, sectionsRef) => {
const { sections } = home;
const addToRefs = (el) => {
if (el && !sectionsRef.current.includes(el)) {
sectionsRef.current.push(el);
}
};
return (
<>
{sections.map((section) => {
if (section.__typename === 'SanitySectionIntro') {
return (
<SectionIntro key={section.id} section={section} ref={addToRefs} />
);
}
if (section.__typename === 'SanitySectionImage') {
return (
<SectionImage key={section.id} section={section} ref={addToRefs} />
);
}
if (section.__typename === 'SanitySectionColumns') {
return (
<SectionColumns
key={section.id}
section={section}
ref={addToRefs}
/>
);
}
return '';
})}
</>
);
};
export default forwardRef(HomePage);
export const query = graphql`
query HomeQuery {
// ...
}
`;
Any clue greatly appreciated :)

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

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.

How to get the links in Draft js in read only mode?

I am creating a simple blog writing application. I am using Draft.js as an editor. I am able to create the link while writing the blog but when I go into read mode all the links are missing. Here are the React code for writing and reading the blogs. For simplicity I am storing the editorState/data in localStorage. Here is WriteBlog.js file
import React, { Component } from "react";
import Editor, { createEditorStateWithText } from "draft-js-plugins-editor";
import createInlineToolbarPlugin from "draft-js-inline-toolbar-plugin";
import createLinkPlugin from "draft-js-anchor-plugin";
import createToolbarPlugin, { Separator } from "draft-js-static-toolbar-plugin";
import {
convertFromRaw,
EditorState,
RichUtils,
AtomicBlockUtils,
convertToRaw
} from "draft-js";
import { ItalicButton, BoldButton, UnderlineButton } from "draft-js-buttons";
import editorStyles from "./editorStyles.css";
import buttonStyles from "./buttonStyles.css";
import toolbarStyles from "./toolbarStyles.css";
import linkStyles from "./linkStyles.css";
import "draft-js-alignment-plugin/lib/plugin.css";
const staticToolbarPlugin = createToolbarPlugin();
const linkPlugin = createLinkPlugin({
theme: linkStyles,
placeholder: "http://…"
});
const inlineToolbarPlugin = createInlineToolbarPlugin({
theme: { buttonStyles, toolbarStyles }
});
const { Toolbar } = staticToolbarPlugin;
const { InlineToolbar } = inlineToolbarPlugin;
const plugins = [staticToolbarPlugin, linkPlugin];
const text =
"Try selecting a part of this text and click on the link button in the toolbar that appears …";
export default class WriteBlog extends Component {
state = {
editorState: createEditorStateWithText(text)
};
onChange = editorState => {
let contentRaw = convertToRaw(this.state.editorState.getCurrentContent());
const stringyfyRawContent = JSON.stringify(contentRaw);
localStorage.setItem("blogcontent", JSON.stringify(contentRaw));
this.setState({
editorState
});
};
handleSave = async e => {
e.preventDefault();
// await this.setState({
// saveblog: 1,
// publish: 0
// });
// this.props.create_post(this.state);
// console.log("save state", this.state);
localStorage.setItem(
"blogsaveblog",
JSON.stringify(this.state.editorState)
);
};
focus = () => this.editor.focus();
render() {
return (
<div className={editorStyles.editor} onClick={this.focus}>
<form onSubmit={this.handleSave}>
<Editor
editorState={this.state.editorState}
onChange={this.onChange}
plugins={plugins}
ref={element => {
this.editor = element;
}}
/>
<Toolbar>
{// may be use React.Fragment instead of div to improve perfomance after React 16
externalProps => (
<div>
<BoldButton {...externalProps} />
<ItalicButton {...externalProps} />
<UnderlineButton {...externalProps} />
<linkPlugin.LinkButton {...externalProps} />
</div>
)}
</Toolbar>
<button
type="submit"
className="btn btn-primary"
style={{ margin: "10px" }}
>
Save
</button>
</form>
</div>
);
}
}
and here is ReadBlog.js file
import React, { Component } from "react";
import Editor, { createEditorStateWithText } from "draft-js-plugins-editor";
import createInlineToolbarPlugin from "draft-js-inline-toolbar-plugin";
import createLinkPlugin from "draft-js-anchor-plugin";
import createToolbarPlugin, { Separator } from "draft-js-static-toolbar-plugin";
import { convertFromRaw, EditorState, convertToRaw } from "draft-js";
import { ItalicButton, BoldButton, UnderlineButton } from "draft-js-buttons";
import editorStyles from "./editorStyles.css";
import buttonStyles from "./buttonStyles.css";
import toolbarStyles from "./toolbarStyles.css";
import linkStyles from "./linkStyles.css";
import "draft-js-alignment-plugin/lib/plugin.css";
const staticToolbarPlugin = createToolbarPlugin();
const linkPlugin = createLinkPlugin({
theme: linkStyles,
placeholder: "http://…"
});
const inlineToolbarPlugin = createInlineToolbarPlugin({
theme: { buttonStyles, toolbarStyles }
});
const { Toolbar } = staticToolbarPlugin;
const { InlineToolbar } = inlineToolbarPlugin;
const plugins = [staticToolbarPlugin, linkPlugin];
const text =
"Try selecting a part of this text and click on the link button in the toolbar that appears …";
export default class ReadBlog extends Component {
state = {
editorState: createEditorStateWithText(text)
};
componentDidMount = () => {
const rawContentFromdb = convertFromRaw(
JSON.parse(localStorage.getItem("blogcontent"))
);
const initialEditorStatedb = EditorState.createWithContent(
rawContentFromdb
);
this.setState({ editorState: initialEditorStatedb });
};
focus = () => this.editor.focus();
render() {
return (
<div className={editorStyles.editor} onClick={this.focus}>
<Editor
editorState={this.state.editorState}
plugins={plugins}
readOnly={true}
ref={element => {
this.editor = element;
}}
/>
</div>
);
}
}
I know this is super late, but you are not adding the decorator which is why this is not working. In this case, you'll want to use compositeDecorator to build your decorator object, and initialize the react state with it.
const decorator = new CompositeDecorator([
{
strategy: linkStrategy,
component: LinkDecorator,
},
]);
const [editorState, setEditorState] = useState(() =>
EditorState.createWithContent(initialContentState, decorator),
);

Resources