Why can't I add a Todo using Apollo Link State? - javascript

Todo never gets added. Visibility Filter works & remote Coinbase fetch works.
Here's my repo 👇
https://github.com/deadcoder0904/apollo-coinbase
& here's the same code sandbox 👇
https://codesandbox.io/s/github/apollographql/apollo-link-state/tree/master/examples/todo
The example is similar to the codesandbox one with just Remote URI for a fetch from coinbase APIs for some coins
I’ve copied the same example
Just 2 changes
1st change is I added a HTTPLink for fetching Remote Data using Coinbase API
2nd change is I didn’t use Apollo-Boost & explicitly imported everything else
I don't understand the difference bcz the code is literally similar (welp they are using apollo-boost & i have used every single chunk differently & gobbled together)
The addTodo mutation is not working in TodoForm.js personally :)
TodoForm.js
import React from "react";
import gql from "graphql-tag";
import { Mutation } from "react-apollo";
const ADD_TODO = gql`
mutation addTodo($text: String!) {
addTodo(text: $text) #client {
id
}
}
`;
const TodoForm = () => (
<Mutation mutation={ADD_TODO}>
{addTodo => {
let input;
return (
<form
onSubmit={e => {
e.preventDefault();
if (!input.value.trim()) return;
addTodo({ variables: { text: input.value } });
input.value = "";
}}
>
<input
type="text"
ref={node => {
input = node;
}}
/>
<button type="submit">Add Todo</button>
</form>
);
}}
</Mutation>
);
export { TodoForm };
The error is -
[Network error]: TypeError:
Object(WEBPACK_IMPORTED_MODULE_0_graphql_tag["gql"]) is not a
function. (In
'Object(WEBPACK_IMPORTED_MODULE_0_graphql_tag["gql"])(_templateObject)',
'Object(WEBPACK_IMPORTED_MODULE_0_graphql_tag["gql"])' is an
instance of Object) on App.js:46

It thinks gql is something other than a function (probably undefined). Should be easy enough to print it out and see. I wouldn't be surprised if you have a bad version of graphql-tag. Set it to a fixed version in package.json

Found the solution. I guess I have to get used to reading the logs properly.
I made one small mistake in ./resolvers/todos file & everywhere else it was fine
I actually imported gql as a named export like -
import { gql } from 'graphql-tag';
when I was supposed to use default export like -
import gql from 'graphql-tag';

Related

Why when following the quick start for easy peasy is my state not changing in Nextjs?

I was trying to use easy peasy for global state management within a nextjs app and was running into problems where the state would only update when I changed pages. I thought maybe I didn't quite grasp what I was doing so I decided to try a quick app with the quick start guide: https://easy-peasy.vercel.app/docs/tutorials/quick-start.html
Setting all this up nothing is happening when I click the button. If I make a change within the code and save it then all the changes happen when it hot reloads.
Does anyone have any idea why this is and how I fix it? I thought it might be nextjs but I just tested it with react and that's not working either. I must be missing some understanding of how this is supposed to work. In a tutorial I was following it worked just fine and cloning it also works. I have no idea when I try and create my own project with the same code its why it's not updating right away.
edit: I don't know why I didn't just share the repo. Here it is: https://github.com/Kyle-Kroger/easy-peasy-broken
edit 2: I tried to get Traversy Media's easy-peasy tutorial to work updating things for v5 and that does the same thing. Nothing updates when clicked but if I edit the code it will update the state on reload. I'm going to try on another computer in the morning.
https://github.com/Kyle-Kroger/traversy-media-easy-peasy-broken
edit 3: I think I might have figured it out. I wonder if it has something to do with version 18 of react. That is the only thing that is different between the repo I cloned that works and mine. Going to see how to use create-react-app with an older version and see if that will work.
edit 4: Well after many hours I figured out the problem. Something in react version 18 broke something with how easy-peasy works. Going back to 17 makes things work.
Here is all my code:
//store.js
import { createStore, action } from "easy-peasy";
export const store = createStore({
todos: [],
addTodo: action((state, payload) => {
state.todos.push({ text: payload, done: false });
}),
});
//body.js
import { useStoreState } from "easy-peasy";
const Body = () => {
const todos = useStoreState((state) => state.todos);
return (
<>
<p onClick={() => console.log(todos)}>Some more text to click</p>
<ul>
{todos.map((todo) => (
<li key={todo.text}>{todo.text}</li>
))}
</ul>
</>
);
};
export default Body;
//title.js
import { useStoreActions } from "easy-peasy";
import { useState } from "react";
const Title = () => {
const addTodo = useStoreActions((actions) => actions.addTodo);
const [value, setValue] = useState("");
return (
<>
<input onChange={(e) => setValue(e.target.value)} value={value} />
<button onClick={() => addTodo(value)}>Add Todo</button>
</>
);
};
export default Title;
_app.js
import "../styles/globals.css";
import { StoreProvider } from "easy-peasy";
import { store } from "../lib/store";
function MyApp({ Component, pageProps }) {
return (
<StoreProvider store={store}>
<Component {...pageProps} />
</StoreProvider>
);
}
export default MyApp;
//index.js
import Body from "../components/body";
import Title from "../components/title";
export default function Home() {
return (
<div>
<Title></Title>
<Body></Body>
</div>
);
}
Removing
<React.StrictMode></React.StrictMode>
from index.js fixes the issue on React 18.
Not a real solution but a workaround till Someone fixes it.
I was also having trouble with the update. Interestingly, I changed the StoreProvider from index.js to app.js and it works.

Does a change in a component refresh the whole page or just that component which was changed?

so I am new to React. Loving it so far. However, I am having a basic question which doesn't have a clear answer right now.
So, I am learning how to lift the state of a component.
So here's a reproducible example.
index.js
import React from "react";
import ReactDOM from "react-dom"
import {Component} from "react";
// import AppFooter from "./AppFooter";
import AppContent from "./AppContent";
import AppHeader from "./AppHeader";
import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap/dist/js/bootstrap.bundle.min'
import './index.css'
class App extends Component{
constructor(props) {
super(props);
this.handlePostChange = this.handlePostChange.bind(this)
this.state = {
"posts": []
}
}
handlePostChange = (posts) => {
this.setState({
posts: posts
})
}
render() {
const headerProps = {
title: "Hi Keshav. This is REACT.",
subject: "My Subject is Krishna.",
favouriteColor: "blue"
}
return (
<div className="app">
<div>
<AppHeader {...headerProps} posts={this.state.posts} handlePostChange={this.handlePostChange}/>
<AppContent handlePostChange={this.handlePostChange}/>
</div>
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById("root"))
I am trying to lift the state of posts which is changed in AppContent to AppHeader.
Here's my AppContent.js and AppHeader.js
// AppContent.js
import React, {Component} from "react";
export default class AppContent extends Component{
state = {
posts: []
}
constructor(props) {
super(props); // constructor
this.handlePostChange = this.handlePostChange.bind(this)
}
handlePostChange = (posts) => {
this.props.handlePostChange(posts)
}
fetchList = () => {
fetch("https://jsonplaceholder.typicode.com/posts")
.then((response) =>
response.json()
)
.then(json => {
// let posts = document.getElementById("post-list")
this.setState({
posts: json
})
this.handlePostChange(json)
})
}
clickedAnchor = (id) => {
console.log(`Clicked ${id}`)
}
render() {
return (
<div>
<p>This is the app content.</p>
<button onClick={this.fetchList} className="btn btn-outline-primary">Click</button>
<br/>
<br/>
<hr/>
<ul>
{this.state.posts.map((item) => {
return (
<li id={item.id}>
<a href="#!" onClick={() => this.clickedAnchor(item.id)}>{item.title}</a>
</li>
)
})}
</ul>
<hr/>
<p>There are {this.state.posts.length} entries in the posts.</p>
</div>
)
}
}
// AppHeader.js
import React, {Component, Fragment} from "react";
export default class AppHeader extends Component{
constructor(props) {
super(props); // constructor
this.handlePostChange=this.handlePostChange.bind(this)
}
handlePostChange = (posts) => {
this.props.handlePostChange(posts)
}
render() {
return (
<Fragment>
<div>
<p>There are {this.props.posts.length} posts.</p>
<h1>{this.props.title}</h1>
</div>
</Fragment>
)
}
}
So here's the main question. As we see, that I am calling the dummy posts api and trying to show the titles of the json object list returned by it.
The posts state is actually updated in AppContent and is shared to AppHeader by lifting it to the common ancestor index.js
However, here's what I have observed.
When I keep this code running using npm start I see that anytime I make a change in any place, it refreshes. I was under the impression that it renders the whole page running on localhost:3000.
Say here's my current situation on the web page:
Now, say I make a change in just AppContent.js, then here's how it looks then:
In here, we see that it's still showing 100 posts in case of AppHeader. Is this expected that react only reloads the component and not the whole page. When I refresh the whole page, it shows 0 posts and 0 posts in both the places. Now have I made a mistake in writing the code ? If yes, how do I fix this ?
Thank you.
In case the question is not clear please let me know.
In here, we see that it's still showing 100 posts in case of AppHeader. Is this expected that react only reloads the component and not the whole page.
It's not React, per se, that's doing that. It's whatever you're using to do hot module reloading (probably a bundler of some kind, like Webpack or Vite or Rollup or Parcel or...). This is a very handy feature, but yes, it can cause this kind of confusion.
Now have I made a mistake in writing the code ?
One moderately-signficant one, a relatively minor but important one, and a couple of trivial ones:
posts should either be state in App or AppContent but not both of them. If it's state in both of them, they can get out of sync — as indeed you've seen with the hot module reloading thing. If you want posts to be held in App, fetch it there and provide it to AppContent as a property. (Alternatively you could remove it from App and just have it in AppContent, but then you couldn't show the total number of posts in App.)
When you're rendering the array of posts, you need to have a key on each of the li items so that React can manage the DOM nodes efficiently and correctly.
There's no need to wrap a Fragment around a single element as you are in AppHeader.
If you make handlePostChange an arrow function assigned to a property, there's no reason to bind it in the constructor. (I would make it a method instead, and keep the bind call, but others like to use an arrow function and not bind.)
There's no reason for the wrapper handlePostChange functions that just turn around and call this.props.handlePostChange; just use the function you're given.
Two issues with your fetch call:
You're not checking for HTTP success before calling json. This is a footgun in the fetch API I describe here on my very old anemic blog. Check response.ok before calling response.json.
You're ignoring errors, but should report them (via a .catch handler).

Data from Gatsby GraphQL always returns undefined?

Gatsby noob here so please bear with me. I have a component that accepts props from the index.js where it is supposed to receive data from an array of objects but will always receive the error TypeError: Cannot read property 'map' of undefined where it's referring to the Hero.js component index.js is calling for.
My assumption is that the data being queried in index.js is either not specific enough or that it is rendering the component before data is received. Here is the index.js file:
import { graphql } from 'gatsby';
import { Layout, SEO, Hero } from 'components';
const IndexPage = ({ data }) => {
const dataFetch = data.contentfulTemplateIndex.heroes;
let tester = () => {
for (let count = 0; count < dataFetch.length; count++) {
return <Hero {...props} />;
}
};
console.log(dataFetch);
let props = {
impactText: dataFetch.impactText,
labels: dataFetch.labels,
primaryLabel: dataFetch.primaryLabel,
location: dataFetch.location
// supportingText: dataFetch.supportingText.json
};
return (
<Layout>
{dataFetch && tester()}
</Layout>
);
};
export const query = graphql`
query {
contentfulTemplateIndex {
heroes {
image {
fluid {
src
}
}
impactText
labels
location
primaryLabel
supportingText {
json
}
}
}
}
`;
export default IndexPage;
Here is the Hero.js component which index.js is calling:
import { Link } from 'gatsby';
import { documentToReactComponents } from '#contentful/rich-text-react-renderer';
import cx from 'classnames';
import styles from './hero.module.scss';
const Hero = (props) => {
return (
<div>
<ul>
<Link className={styles.pills}>{props.primaryLabel}</Link>
{props.labels.map((label) => {
return <Link className={styles.pills}>{label}</Link>;
})}
</ul>
<div className={styles.grid}>
<h1>{props.impactText}</h1>
</div>
</div>
);
};
export default Hero;
It's impossible for an outsider to debug your code without a minimum reproducable example.
The best way to debug GraphQL is to use the GraphiQL interface of your browser.
Run gatsby develop. If it fails because of the TypeError remove the lines of code that cause the type error (but not the code of your GraphQL query!). You need to get your development server runnning.
Open your browser, use the URL: http://localhost:8000/___graphql
Copy your graphQL query from your code and paste it into the GraphiQL query window.
Can you access your data there? If not you made a mistake writing your query or the data is not where it's supposed to be.
This way you can make sure the data exists.
It also helps to console.log(props) so you can examine the data object:
const Hero = (props) => {
console.log(props);
return (

Passing component as a function argument

I have a function that take a component as argument, and return another, enhanced component:
import React from 'react';
import { compose } from 'recompose';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { Layout, DarkBar } from 'SharedComponents/Layouts';
const myCreationFunction = ({
component,
}) => {
const Route = (props) => {
// Some code here
return (
<Layout>
<div><Link to={props.path}>LinkTitleHere</Link></div>
{React.createElement(component, {
...props,
...someOtherPropsHere,
})}
</Layout>
);
}; // The error points here
const mapStateToProps = () => ({});
const enhance = compose(
connect(mapStateToProps, someActionsHere),
)(Route);
return enhance;
};
I invoke that function in this way:
import MyComponent from './MyComponent';
import myCreationFunction from './HOC/myCreationFunction';
const Route = myCreationFunction({
component: MyComponent,
});
When I run it in the development mode, it runs smoothly. But when trying to build the app using npm run build and going through webpack, I get:
Module parse failed: Unexpected token (35:47)
You may need an appropriate loader to handle this file type.
| function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
> var createListRoute = function myCreationFunction((_temp = _ref, _ref2 = <_Layouts.DarkBar>
| <_Breadcrumbs2.default />
| <_Layouts.RoundAddButton path={addPath} color="white" />
What am I doing wrong?
Edit #1
It seems that the <Link> is causing the problem. Removing it fixed the problem. Also, when trying to replace it with a tag, I get the same issue. Weird.
Edit #2
I have not resolved this issue because of lack of time. I was trying for 1 hour without any progress and decided to go with button tag and onClick method that uses history to push the new url.
It was and is really weird to me, that a Link or <a> tag can break something during the build process itself. I will definitely jump deeper into it in some free time.

Unrecognized arguments in graphql mutation

I'm curretly following this tutorial on Meteor/Apollo/GraphQL, and having huge troubles to make a mutation with arguments/variables. Here is my code and some notes at the bottom !
The code
Schema
type Resolution {
_id: String!
name: String!
}
type Mutation {
createResolution(name: String!): Resolution
}
Resolver
import Resolutions from './resolutions'
export default {
Query: {
resolutions() {
return Resolutions.find({}).fetch()
}
},
Mutation: {
createResolution(obj, args, context) {
console.log('hey i get here')
}
}
}
The component using the mutation
import React, { Component } from 'react'
import gql from 'graphql-tag'
import { graphql } from 'react-apollo'
const createResolutionQuery = gql`
mutation createResolution($name: String!) {
createResolution(name: $name) {
_id
}
}
`
class ResolutionForm extends Component {
submitForm = () => {
this.props
.createResolution({
variables: {
name: this.name.value
}
})
.then(d => console.log('data received'))
.catch(e => console.log(e))
}
render() {
return (
<div>
<input type="text" ref={input => (this.name = input)} />
<button onClick={this.submitForm}>Submit</button>
</div>
)
}
}
export default graphql(createResolutionQuery, {
name: 'createResolution'
})(ResolutionForm)
What i know
When i try to send my query to the server, i get an http 400 error, and i get the following graphql error : "Unknown argument "name" on field "createResolution" of type "Mutation"."
The createResolution is available in my graphiQL but does not show any arguments in the doc.
It's stipulated in the tutorial that changing the .graphql schema does not trigger meteor server reloading, to apply change i have to modify my "register-api" file which is responsible for making the executable schema and create the apollo server with it. I made fake change to trigger it but it did not changed anything.
I tried to relaunch the server after erasing my browser's cache with no result.
So I think my problem is with the mutation arguments (brilliant deduction I know), but I can't figure out where is the typo or where I'm missing something. Help from somebody with a fresh look is welcome, thanks :)
Edit
Reinstall npm packages solved the issue.
All looks good I made a small change and added it as a pull request to your github repo.
createResolution(obj, {name}, context) {
console.log('hey i get here')
const id = Resolutions.insert({
name,
})
return Resolutions.findOne(id)
}
Running on my machine I get no errors.

Categories