I'm using jwt_decode but I got .map Error
My Code:
import jwt_decode, { JwtPayload } from "jwt-decode";
const token: string = "eyJhbGciOiJIUzI1NiIsInR.....";
const decoded = jwt_decode<JwtPayload>(token);
const jsonData = JSON.stringify(decoded);
// MY jsonData output
//{
// userId: '63ee8c85f146b856864eddb3',
// email: 'test#gmail.com',
// exp: 1676592335
// }
If I render I got "Property 'map' does not exist on type 'string'"
{
jsonData.map((data: any) => {
return <li key={data._id}>
{data.email}
</li>
})
Related
I was practicing with typescript and I have a problem, I am trying to pass information from the API to a variable (this is just to have the necessary information), but I have this error message:: "Property 'id' does not exist on type 'MyProps'" and "Property 'url' does not exist on type 'MyProps'", this is my code.
import { useEffect } from "react";
import { CardGif } from "./components/CardGif";
import { Content } from "./components/layout/Content";
import InterfaceImage from './Interfaces/InterfaceImage'
interface MyProps {
gifs: InterfaceImage[]
}
function App() {
const handleLoadInformation = async () => {
const key = 'url';
const url = `https://api.giphy.com/v1/gifs/search?q=goku&limit=10&api_key=${key}`;
const response = await fetch(url);
const { data } = await response.json();
const images = data.map((img: MyProps) => {
return {
id: img.id,
url: img.url
}
})
}
useEffect(() => {
handleLoadInformation();
}, [])
return (
<div>
<Content>
<div className="w-[90%] m-auto columns-3">
<CardGif />
</div>
</Content>
</div>
);
}
export default App;
and this is my Interface:
export default interface InterfaceImage {
id: number,
url: string,
}
The error stems from this piece of code:
const images = data.map((img: MyProps) => {
return {
id: img.id,
url: img.url
}
})
When you try to get img.id and img.url, the TypeScript compiler sees that img should be of type MyProps, which only has one property—gifs.
interface MyProps {
gifs: InterfaceImage[] // no `id` or `url` props here
}
To fix this error in general, you can add the props to the interface definition:
interface MyProps {
gifs: InterfaceImage[],
id: number,
url: string,
}
However, considering the InterfaceImage interface that you have defined, I assume that what you really need is to change the type annotation in the .map() callback:
const images = data.map((img: InterfaceImage) => {
return {
id: img.id,
url: img.url
}
})
I'm trying to use graphql-tools's addMocksToSchema function in a jest test with Apollo Client to allow me to test components that query Apollo by returning mock data that I can define for myself. However, the mock data isn't being properly returned when using useQuery.
I have a UserButton button that I'm trying to write tests for. The component is as follows:
// UserButton.jsx
import React from "react";
import PropTypes from "prop-types";
import { gql } from "#apollo/client";
import { useQuery } from "#apollo/client";
import Button from "./Button";
const GET_CURRENT_USER = gql`
query GetCurrentUser {
id
firstName
lastName
}
`;
function UserButton({ ...props }) {
const { data: userData } = useQuery(GET_CURRENT_USER);
return <Button {...props}>{userData.firstName} {userData.lastName}'s Button</Button>;
}
UserButton.propTypes = {
children: PropTypes.node,
};
export default UserButton;
Here is my test:
// UserButton.test.jsx
import React from "react";
import { render, screen } from "#testing-library/react";
import UserButton from "../UserButton";
import ApolloMockingProvider from "../../providers/ApolloMockingProvider";
import userEvent from "#testing-library/user-event";
import { debug } from "webpack";
describe("UserButton", () => {
let onClickMock;
beforeEach(() => {
jest.resetAllMocks();
onClickMock = jest.fn();
});
it("Renders with first and last name.", () => {
render(
<ApolloMockingProvider>
<UserButton />
</ApolloMockingProvider>
);
const button = screen.getByRole("button");
expect(button).toHaveTextContent("John Smith's Button")
});
});
The ApolloMockingProvider is based off the following article and the code for it is as follows:
// ApolloMoockingProvider.jsx
import {
ApolloClient,
ApolloProvider,
InMemoryCache,
} from "#apollo/client";
import { SchemaLink } from "#apollo/client/link/schema";
import { addMocksToSchema } from "#graphql-tools/mock";
import { makeExecutableSchema } from "#graphql-tools/schema";
import React from "react";
const schemaString = `
type AvatarObjectType {
id: Int,
avatarUrl: String
}
scalar DateTime
scalar GenericScalar
type UserObjectType {
id: Int,
username: String,
firstName: String,
lastName: String,
avatar: AvatarObjectType,
dateCreated: DateTime,
notificationPreferences: GenericScalar,
}
type Query {
GetCurrentUser: UserObjectType,
}
`;
const globalMocks = {
String: () => "abc",
Int: () => 56,
UserObjectType: () => ({
firstName: "John",
lastName: "Smith",
}),
};
const ApolloMockingProvider = ({ children }) => {
const executableSchema = makeExecutableSchema({ typeDefs: schemaString });
const schemaWithMocks = addMocksToSchema({
schema: executableSchema,
mocks: globalMocks,
});
const client = new ApolloClient({
link: new SchemaLink({ schemaWithMocks }),
cache: new InMemoryCache(),
});
return <ApolloProvider client={client}>{children}</ApolloProvider>;
};
export default ApolloMockingProvider;
The ApolloMockingProvider's globalMock's object that's passed into schemaWithMocks should return 'John Smith's Button' for the UserButton's Renders with first and last name. test. However, instead I get undefined for userData as shown by this error message:
UserButton › Renders with first and last name.
TypeError: Cannot read property 'firstName' of undefined
16 | function UserButton({ ...props }) {
17 | const { data: userData } = useQuery(GET_CURRENT_USER);
> 18 | return <Button {...props}>{userData.firstName} {userData.lastName}'s Button</Button>;
| ^
19 | }
20 |
21 | UserButton.propTypes = {
at UserButton (js/components/button/UserButton.jsx:18:39)
Anybody know what the error here is? Reproduction repo is here with the failing test being located here:
EDIT:
I added loading to the useQuery in UserButton but still get the same undefined error.
// UserButton.test.jsx
function UserButton({ ...props }) {
const { data: userData, loading } = useQuery(GET_CURRENT_USER);
let buttonText = "Loading...";
if (!loading) {
buttonText = `${userData.firstName} ${userData.lastName}'s Button`;
}
return <Button {...props}>{buttonText}</Button>;
}
And changed the UserButton.test.jsx test accordingly:
it("Renders with first and last name.", async () => {
render(
<ApolloMockingProvider>
<UserButton />
</ApolloMockingProvider>
);
const button = screen.getByRole("button");
expect(button).toHaveTextContent("Loading...")
await waitFor(() => {
expect(screen.queryByText("John Smith's Button")).toBeInTheDocument();
});
});
But the error I get is still with userData being undefined
UserButton › Renders with first and last name.
TypeError: Cannot read property 'firstName' of undefined
19 |
20 | if (!loading) {
> 21 | buttonText = `${userData.firstName} ${userData.lastName}'s Button`;
| ^
22 | }
23 | return <Button {...props}>{buttonText}</Button>;
24 | }
I have a component that renders a list of filters as removable chips which I am trying to test using react-testing-library. I am trying to do query by accessible name as explained here using getByRole.
component:
import Chip from '#material-ui/core/Chip';
import PersonIcon from '#material-ui/icons/Person';
import React from 'react';
import './FilterChips.less';
import { Filters } from './types';
export type FilterChipsProps = {
filters: Filters,
};
export const FilterChips = (props: FilterChipsProps) => {
const { filters } = props;
const people = filters.people
? filters.people.map((person: any) => (
<Chip
icon={<PersonIcon />}
label={`${person.Name} (${person.Id})`}
key={person.Id}
className='chips'
role='filter-chip'
/>
))
: [];
return people.length > 0
? (
<div className='filters'>
<span>Filters: </span>
{people}
</div>
)
:
null;
};
Test:
test('that filters are rendered properly', async () => {
const filters = {
people: [
{ Id: '1', Name: 'Hermione Granger' },
{ Id: '2', Name: 'Albus Dumbledore' },
],
};
const props = { filters };
const { getByRole } = render(<FilterChips {...props} />);
const PersonFilter = getByRole('filter-chip', { name: `${filters.people[0].Name} (${filters.people[0].Id})` });
expect(PersonFilter).toBeDefined();
});
But I am getting a typescript error:
Argument of type '{ name: string; }' is not assignable to parameter of type 'ByRoleOptions'.
Object literal may only specify known properties, and 'name' does not exist in type 'ByRoleOptions'
How do I fix this?
I tried a couple of things to fix this. I imported getByRole directly from #testing-library/dom and deconstructed container from rendered component
const { container } = render(<FilterChips {...props} />);
and then tried to do query by accessible name as following
const PersonFilter = getByRole(container, 'filter-chip', { name: '${filters.people[0].Name} (${filters.people[0].Id})' });
But this is also throwing the same error. Why am I getting this error and how do I fix it?
You can simply ignore ts preceding the problematic line with:
//#ts-ignore
const PersonFilter = getByRole('filter-chip', { name: `${filters.people[0].Name} (${filters.people[0].Id})` });
This will ignore all typescript alerts and treat that next line as if it were plain javascript
following this example in the docs (scroll to the end of the section linked and click on the 'React' tab):
import { render } from '#testing-library/react'
const { getByRole } = render(<MyComponent />)
const dialogContainer = getByRole('dialog')
your code should be:
const { getByRole } = render(<FilterChips {...props} />);
const PersonFilter = getByRole(`${filters.people[0].Name} (${filters.people[0].Id})`);
I'm trying to pass data from external api and convert it to a CSV file. This is what I have so far:
import React, { Component } from 'react';
import { CSVLink } from "react-csv";
import Header from './components/header'
import './App.scss'
class App extends Component {
constructor() {
super()
this.state = {
orders: []
}
this.getReports = this.getReports.bind(this)
}
getReports = async () => {
const response = await fetch('example.com')
const ordersData = await response.json()
this.setState({
orders: ordersData.data,
})
let order = this.state.orders.map(order => ({
ID: order.id,
Order_ID: order.order_id,
Date: order.created,
Amount: order.total_amount,
Payment_Provider: order.payments[0].provider_id,
Payment_ID: order.payments[0].id,
Refund_Reason: order.reason
}))
const objectToCsv = (order) => {
const csvRows = [];
const headers = Object.keys(order[0])
csvRows.push(headers.join(','));
// console.log(csvRows)
for (const row of order) {
const values = headers.map(header => {
const escaped = ('' + row[header]).replace(/"/g, '\\"')
return `"${escaped}"`
})
csvRows.push(values.join(','))
}
return csvRows.join('\n')
}
let csvData = objectToCsv(order)
console.log(csvData)
// console.log(order)
// console.log(this.state.orders)
}
render() {
return (
<div className="App">
<Header />
<div className="body">
{/* <button onClick={this.getReports}>CLICK</button> */}
<CSVLink data={csvData} onClick={this.getReports}>Click me</CSVLink>
</div>
</div>
);
}
}
export default App;
The problem I'm facing is that I can't pass the csvData variable to the data attribute in the CsvLink component since the variable is not global. I tried adding another csvData state where I passed the objectToCsv(order) and that stops the error, however when I download the CSV, the content is jiberish.
Any help is much appreciated!
I've added the following to my getReports function and removed the csvLink component and I was able to export the data to CSV file, but it's definitely not a nice UX. I still need to work on separating the inputs into columns.
const blob = new Blob([csvData], { type: 'text/csv' })
const url = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.setAttribute('hidden', '')
a.setAttribute('href', url)
a.setAttribute('download', 'download.csv')
document.body.appendChild(a)
a.click()
i am trying to adapt the react-apollo-hackernews tutorial to typescript.
tutorial: https://www.howtographql.com/react-apollo/1-getting-started/
code: https://github.com/howtographql/react-apollo
I doesnt really know what i am missing, because i am not that deep into typescript until now.
While trying the adaption, i get the following error messages for the code below:
src/components/CreateLink.tsx|40 col 22 error 2339| Property 'postMutation' does not exist on type 'Readonly<{ children?: ReactNode; }> & Readonly<{}>'.
src/components/CreateLink.tsx|45 col 16 error 7006| Parameter 'store' implicitly has an 'any' type.
src/components/CreateLink.tsx|45 col 33 error 7031| Binding element 'post' implicitly has an 'any' type.
src/components/CreateLink.tsx|62 col 16 error 2339| Property 'history' does not exist on type 'Readonly<{ children?: ReactNode; }> & Readonly<{}>'.
Can someone help me on that?
import * as React from 'react'
import { Component } from 'react'
import { graphql } from 'react-apollo'
import gql from 'graphql-tag'
import { FEED_QUERY } from './LinkList'
import { LINKS_PER_PAGE } from '../constants'
class CreateLink extends Component {
state = {
description: '',
url: '',
}
render() {
return (
<div>
<div className="flex flex-column mt3">
<input
className="mb2"
value={this.state.description}
onChange={e => this.setState({ description: e.target.value })}
type="text"
placeholder="A description for the link"
/>
<input
className="mb2"
value={this.state.url}
onChange={e => this.setState({ url: e.target.value })}
type="text"
placeholder="The URL for the link"
/>
</div>
<button onClick={() => this._createLink()}>Submit</button>
</div>
)
}
_createLink = async () => {
const { description, url } = this.state
await this.props.postMutation({
variables: {
description,
url,
},
update: (store, { data: { post } }) => {
const first = LINKS_PER_PAGE
const skip = 0
const orderBy = 'createdAt_DESC'
const data = store.readQuery({
query: FEED_QUERY,
variables: { first, skip, orderBy },
})
data.feed.links.splice(0, 0, post)
data.feed.links.pop()
store.writeQuery({
query: FEED_QUERY,
data,
variables: { first, skip, orderBy },
})
},
})
this.props.history.push(`/new/1`)
}
}
// 1
const POST_MUTATION = gql`
# 2
mutation PostMutation($description: String!, $url: String!) {
post(description: $description, url: $url) {
id
createdAt
url
description
}
}
`
// 3
export default graphql(POST_MUTATION, { name: 'postMutation' })(CreateLink)
You haven't created types for your props. The typescript compiler is throwing an error because it thinks the methods you defined don't exist. Here is an example of a typed react component
type AppProps = { // like this
message: string,
}
type AppState = { // and this
count: number,
}
class App extends React.Component<AppProps, AppState> {
state = {
count: 0
}
render() {
return (
<div>{this.props.message} {this.state.count}</div>
);
}
}