This is my first post!
I'm filtering through a list of products I'm fetching from Contentful using graphQL to find the product that matches what the user clicked. Everything worked before I converted the component to Typescript. I am vaguely familiar with TS and just started using it a few months ago.
When I log the product, I get back the Object with the correct filtered data, however when I go to access a key name "imagesCollection" I get the following TS error message:
Property 'imagesCollection' does not exist on type '{}'.ts(2339)
If I // @ts-ignore the product image renders without issues. Is this a race condition of some kind?
Any help is much appreciated as I have spent a good amount of time trying to solve it myself. Thank you!
The Component:
const Product = (props: any) => {
// Custom HOOK for fetching Contentful Data
const slug = props.match.params.slug;
const { product } = useContentful(query, slug);
console.log(product);
const [jacketColor, setJacketColor] = useState(null);
const [color, setColor] = useState(null);
useEffect(() => {
setJacketColor(
product.imagesCollection && product.imagesCollection.items[1].url
);
setColor(
// @ts-ignore
product.imagesCollection && product.imagesCollection.items[1].description
);
}, [product]);
const changeColor = (jacketColor: string) => {
// @ts-ignore
const filterHero = product.imagesCollection.items
.filter((heroJacket: { title: string }) =>
heroJacket.title.includes("hero")
)
.filter(
(item: { description: string }) => item.description === jacketColor
);
setJacketColor(filterHero[0].url);
setColor(filterHero[0].description);
};
const thumbnailImages = () => {
return (
// @ts-ignore
product.imagesCollection &&
// @ts-ignore
product.imagesCollection.items
.filter((jacket: queryProps) => jacket.title.includes("thumbnail"))
.map((jacket: { url: string; description: string }, index: number) => {
return (
<img
className={styles.thumbnailImages}
key={`products-${index}`}
src={jacket.url}
// @ts-ignore
alt={product.title}
onClick={() => changeColor(jacket.description)}
/>
);
})
);
};
return (
<>
<Header />
{!product ? (
<Loading />
) : (
<div className={styles.productsContainer}>
{/* @ts-ignore */}
<p className={styles.title}>{product && product.title}</p>
{documentToReactComponents(
// @ts-ignore
product.description && product.description.json,
RICHTEXT_OPTIONS
)}
{/* @ts-ignore */}
<p className={styles.price}>{`$${product && product.price}`}</p>
{
<img
className={styles.productImage}
// @ts-ignore
src={jacketColor}
// @ts-ignore
alt={product && product.title}
/>
}
<p className={styles.selectColor}>{`Select a colour: ${color}`}</p>
<div className={styles.thumbnailImagesWrapper}>
{thumbnailImages()}
</div>
</div>
)}
</>
);
};
export default Product;
The custom hook to fetch data:
function useContentful(query: string, slug: string | null) {
const [products, setProducts] = useState([]);
const [product, setProduct] = useState({});
useEffect(() => {
fetch(graphqlURL, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${REACT_APP_CDA_TOKEN}`,
},
body: JSON.stringify({ query }),
})
.then((res) => res.json())
.then(({ data }) => {
if (slug) {
data.arcteryxCollection.items.filter((item: queryProps) => {
return item.slug === slug && setProduct(item);
});
} else {
setProducts(data.arcteryxCollection.items);
}
})
.catch((err) => console.error(err));
}, [query, slug]);
return { product, products };
}
The graphQL query structure:
export const query = `
query {
arcteryxCollection {
items {
title
slug
description {
json
}
price
imagesCollection {
items {
url
title
description
}
}
}
}
}
`;
The TS interface I'm importing:
export interface queryProps {
title: string;
slug: string;
price: number;
description: {
json: {
content: {
value: string;
}[];
};
};
imagesCollection: {
items: {
url: string;
title: string;
description: string;
}[];
};
}
Link to the Repo: https://github.com/danlubbers/arcteryx-graphql