How to declare array from API in place of hardcoded array? - javascript

I was given an example for some code that has an array hardcoded. Im looking to swap this out for my array that is pulled in from an API using graphql. Below is the code pen to the original example & another for what i've tried with no avail.
I'm pretty new to graphql & js so likely an amateur mistake, any pointers would be much appreciated!
Original code - https://codesandbox.io/s/nice-saha-gwbwv
My pen - https://codesandbox.io/s/quiet-wind-brq8s?file=/src/App.js

I would change your component structure to something like:
import React, { useState } from 'react'
import { graphql } from 'gatsby'
const YourPage = ({data}) => {
console.log('data is', data)
const [filters, setFilters] = useState({
type: "",
category: ""
});
//your calculations
return (
<div>
Your stuff
</div>
)
}
export const query = graphql`
query yourQueryName{
allStrapiHomes {
nodes {
type
category
}
}
}
`
export default YourPage
In your code, upon some critical imports, you are missing a few stuff from Gatsby. If you use a staticQuery, you will need to add a render mode to it. It may look a bit old-fashioned, it's better to use the useStaticQuery hook provided by Gatsby or adding a page query (my approach).
I've added a page query. Your data is under props.data.allStrapiHomes.nodes, destructuring props you omit the first step, so your data will be at data.allStrapiHomes.nodes. Both type and category will be an array if they are set like this in the Strapi back-end.

Related

Create class/component from string

Say i have a file like below on the server eg Github. I want to download the file as string with fetch and then be able to convert it to an object that i could use.
OBS: all import and the interface exist on locally so there wont be any thing missing.
import parser from '../interface/parsers';
import '../interface/extension';
export default class test implements parser {
public name: string;
constructor() {
}
}
Here's an example that at least works in a Snack (ignored the fetch part and just used a local string):
export default function App() {
const [evaledComponent, setEvaledComponent] = useState(null)
useEffect(() => {
this.React = React;
this.Text = Text;
setEvaledComponent(eval('this.React.createElement(this.Text, {}, "test component")'));
},[])
return <Text>{evaledComponent}</Text>
}
Note: I took a trick from from Why eval() is not working in React Native? assigning React to this.React in order to get it usable -- it's not a pretty hack and I'd recommend a more stable solution if you went ahead with this plan.
But:
This is an extremely uphill battle to take on and using eval is a terrible security risk since it executes arbitrary code. If there's another route you can take (like rendering things based on a JSON description), I would take that route instead.
Update:
Example of returning a custom object (non-React)
eval("var MyParser = { getString: () => {return 'hi there'} }");
return <Text>{MyParser.getString()}</Text>

Cannot Get Values from Prop in Twin.macro

You can see an example of what I am trying to do here: https://codesandbox.io/s/vibrant-leaf-qj8vz
Note: this particular example is using Twin.macro with Styled Components. On my local computer I tried the same thing with the same results using Twin.macro with emotion/next.js.
Here is a sample component illustrating what I am trying to do:
import React from 'react'
import tw from 'twin.macro'
const Acme = ({ children, type }) => <div css={[tw`${type}`]}>{children}</div>
export default Acme
Here is how I would use that component: <Acme type="text-2xl">Text Goes Here</Acme>
My expectation is that I will be able to style this instance of the <Acme /> component via the tailwind css classes that I pass into the type prop. Instead, I get the following error message:
/src/components/Acme.js: twin.macro: Property value expected type of string but got null Learn more: https://www.npmjs.com/package/twin.macro
When trying to figure this out, I noticed something interesting that may be relevant. Here is a variation of the code that does work:
const Acme = ({ children, type }) => {
const typeClass = 'text-2xl'
const typeObj = {
class: 'text-2xl',
}
return <div css={[tw`${typeClass}`]}>{children}</div>
}
export default Acme
Note that I have created a variable typeClass and set it to the same tailwind css class. Note, in particular, the following line of code:
css={[tw`${typeClass}`]}
I have replace the prop type with the variable typeClass. This works. But now, instead of using the variable typeClass let's use the object typeObj that I have created as follows:
const Acme = ({ children, type }) => {
const typeClass = 'text-2xl'
const typeObj = {
class: 'text-2xl',
}
return <div css={[tw`${typeObj.class}`]}>{children}</div>
}
export default Acme
This does not work and produces the same error:
/src/components/Acme.js: twin.macro: Property value expected type of string but got null Learn more: https://www.npmjs.com/package/twin.macro
This is so even though typeClass === typeObj.class evaluates to true.
I don't know if this is helpful, but perhaps it can help indicate a solution. If I can get the type prop to behave like the typeClass variable then hopefully this would work.
Either way, any idea why this is not working and how to fix it?
Thanks.
I found the answer (meaning that someone else answered it on a different site). Here is is. I have to rewrite both the Component and the usage of the component as follows:
// Acme.js
const Acme = ({ children, type }) => <div css={[type]}>{children}</div>
---
// App.js
import tw from "twin.macro"
<Acme type={tw`text-2xl`}>Text Goes Here</Acme>
I have tried this out and it works.

React component to output database info

I've making a recipe website and having trouble putting to words what I'm trying to do.
I have a component (home.js) which I'm trying to display different recipe posts on.
My thought was that I could make a component called recipe.js that would just have the structure of how a recipe post would be laid out. Then, all the recipe information would be displayed in that structure as it pulls all the recipe info from my database. This way I wouldn't have to have an individual component for each recipe.
Am I right in going about this? I'm a bit lost as to how I'm meant to do this or even what I would google to help accomplish this.
I'd be grateful for a push in the right direction.
I'm not a total React pro but I think a useFetch would work: it would pull info from the database using the props passed in.
import React from 'react';
import { useFetch } from 'react-async';
export const Recipe = (props) => {
const { data, error } = useFetch(`https://swapi.co/api/people/${id}/`, {
headers: { accept: "application/json" },
})
if (error) return error.message
if (data) return (<div className="recipe"></div>)
return null;
}

this.state.persons.map is not a function but persons is already an array

It's my first time working with React and I'm having some trouble with starting to use Axios. I watched a video, a very simple practical tutorial that showed how to use a get function, but I think something went wrong because even following the same exact steps I still get the error "this.state.persons.map is not a function". I want to stress the fact that the author of the video uses this exact same JavaScript code, and for him it works. Any explanation?
Here's the whole code for reference:
import React from "react";
import axios from "axios";
export default class personList extends React.Component{
state = {
persons: [],
};
componentDidMount(){
axios.get(`https://jsonplaceholder.typicode.com`)
.then(res =>{
console.log(res);
this.setState({persons: res.data});
});
}
render() {
return (
<ul>
{this.state.persons.map(person => <li key={person.id}>{person.name}</li>)}
</ul>
)
}
}
I looked around for an answer, but every other case that has been presented is either too different (using set arrays, json and whatnot) or it refers to a string used instead of an array, which causes the error, and obviously it's not my case.
You are making a GET request at https://jsonplaceholder.typicode.com which returns the whole webpage. If you want to fetch the users, use this URL instead: https://jsonplaceholder.typicode.com/users

Accessing Apollo Store like I used to go with Redux Store

I'm following this tutorial for Apollo here: https://github.com/howtographql/react-apollo/blob/master/src/components/Header.js.
In the Header.js file they have:
const userId = localStorage.getItem(GC_USER_ID)
which seems to me a bit ugly: everytime Header render() is called you read from localStorage?
With react-redux I used to go with something like this:
render() {
const { authentication } = this.props;
...
}
const mapStateToProps = state => ({
authentication: state.authentication
});
export default connect(mapStateToProps, { logoutAction })(Navbar);
And also if I read from localStorage on every render() and let's say I wanna store in localStorage all my user's data (eg. {username: "john", age: "15"}) when I pass this const to one of my children components everytime it re-render because the JSON.parse makes it a new object! Everytime!
How to have something like a Redux Store with Apollo?
Apollo's solution for managing state is through apollo-link-state. Here is some general info and the docs. After setting it up, you can then query state within a GraphQL query.
It has not reached 1.0 release yet, but it's usable and fairly easy to setup. Their examples aren't great yet. However, if you have some familiarity with GraphQL resolvers, it's fairly simple to setup. I'm using it here with success.

Categories