├── public
├── favicon.ico
├── manifest.json
├── style.css
└── index.html
├── src
├── context.js
├── graphql
│ ├── mutations.js
│ └── queries.js
├── reducer.js
├── components
│ ├── App.js
│ ├── Home.js
│ └── AddSong.js
├── index.js
└── createServiceWorker.js
├── .gitignore
├── README.md
└── package.json
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shrutikapoor08/hooks-graphql/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/src/context.js:
--------------------------------------------------------------------------------
1 | import { createContext } from 'react';
2 |
3 | const Context = createContext({
4 | songs: []
5 | });
6 |
7 | export default Context;
--------------------------------------------------------------------------------
/src/graphql/mutations.js:
--------------------------------------------------------------------------------
1 | import gql from "graphql-tag";
2 |
3 | export const ADD_SONG = gql`
4 | mutation AddSong($song: SongCreateInput!) {
5 | createSong(data: $song) {
6 | name
7 | artist
8 | lyrics
9 | }
10 | }
11 | `;
--------------------------------------------------------------------------------
/src/graphql/queries.js:
--------------------------------------------------------------------------------
1 | import gql from "graphql-tag";
2 |
3 | export const singleArtist = gql`
4 | query songs($artist: String) {
5 | songs(where: { artist: $artist }) {
6 | name
7 | id
8 | artist
9 | lyrics
10 | }
11 | }
12 | `;
13 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "hooks state management",
3 | "name": "State management using hooks and graphql",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "192x192",
8 | "type": "image/png"
9 | }
10 | ],
11 | "start_url": "./index.html",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | .idea/
4 | src/.idea/
5 | # dependencies
6 | /node_modules
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [Create React App](https://github.com/facebookincubator/create-react-app).
2 |
3 | ## Available Scripts
4 |
5 | In the project directory, you can run:
6 |
7 | ### `npm start`
8 |
9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
11 |
12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console.
14 |
15 |
16 | ## TODO
17 | - ~~update UI automatically~~ (done)
18 | - ~~beautify the UI~~ (done)
19 | - pull from an open source graphQL API
20 |
--------------------------------------------------------------------------------
/src/reducer.js:
--------------------------------------------------------------------------------
1 | export default function reducer(state, action) {
2 | switch(action.type) {
3 | case "ADD_CONTENT":
4 | return {
5 | ...state,
6 | songs: [...new Set([...state.songs ,...action.payload])]
7 | };
8 | case "DELETE_CONTENT":
9 | //payload is an id.
10 | //remove song which has song. id
11 | console.log('...action.payload', action.payload)
12 | const newsongs = state.songs.filter(id => id !== action.payload.id)
13 | return {
14 | songs: newsongs
15 | };
16 | default:
17 | return state
18 | }
19 | }
--------------------------------------------------------------------------------
/src/components/App.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useReducer } from "react";
2 | import {Route, BrowserRouter as Router, Switch} from "react-router-dom";
3 | import Context from '../context';
4 | import reducer from '../reducer'
5 |
6 | import Home from "./Home";
7 |
8 | const App = () => {
9 | const initialState = useContext(Context);
10 | const [state,dispatch] = useReducer(reducer, initialState);
11 | return (
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | );
20 | };
21 |
22 | export default App;
--------------------------------------------------------------------------------
/public/style.css:
--------------------------------------------------------------------------------
1 |
2 |
3 | h1{
4 | font-size: 1.75em;
5 | font-weight: bold;
6 | font-family: 'Lexend Deca', sans-serif;
7 | }
8 |
9 | h3{
10 | font-size: 1.2em;
11 | font-family: 'Open Sans Condensed', sans-serif;
12 | }
13 |
14 | p{
15 | font-size: 1.0em;
16 | font-weight: lighter;
17 |
18 | }
19 |
20 | body{
21 | background: #F0F0F0;
22 | }
23 |
24 | .mui-form{
25 | width: 96%;
26 | margin-top: 20px;
27 | }
28 | .song-container{
29 | display: flex;
30 | flex-flow: row wrap;
31 | }
32 | .song-list-item{
33 | position: relative;
34 | width: 48%;
35 | border-radius: 3px;
36 | border: 1px solid #e9e9e9;
37 | margin: 5px 2px;
38 | overflow: hidden;
39 | background-color: #fff;
40 | }
41 |
42 | .text-wrapper{
43 | padding-left: 20px;
44 | }
45 |
46 | .width-small {
47 | margin-right: 10px;
48 | display: inline-block;
49 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hooks-graphql",
3 | "version": "0.0.38",
4 | "description": "A sample integration of React hooks into graphql",
5 | "keywords": ["hooks", "react", "graphql"],
6 | "author": "Shruti Kapoor",
7 | "main": "index.js",
8 | "dependencies": {
9 | "@material-ui/core": "3.9.3",
10 | "apollo-cache-inmemory": "^1.6.0",
11 | "apollo-client": "^2.6.0",
12 | "apollo-client-preset": "1.0.5",
13 | "apollo-link-http": "^1.5.14",
14 | "graphiql": "^0.13.0",
15 | "graphql": "0.11.7",
16 | "graphql-tag": "^2.10.1",
17 | "material-ui": "0.20.2",
18 | "muicss": "0.9.41",
19 | "prettier": "^1.18.2",
20 | "react": "^16.8.0",
21 | "react-apollo": "^2.5.6",
22 | "react-dom": "^16.8.0",
23 | "react-router-dom": "4.2.2",
24 | "typeface-roboto": "0.0.54"
25 | },
26 | "devDependencies": {
27 | "react-scripts": "1.0.0"
28 | },
29 | "scripts": {
30 | "start": "react-scripts start",
31 | "build": "react-scripts build",
32 | "test": "react-scripts test --env=jsdom",
33 | "eject": "react-scripts eject"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import { BrowserRouter } from "react-router-dom";
4 | import { ApolloProvider } from "react-apollo";
5 | import {
6 | ApolloLink,
7 | ApolloClient,
8 | InMemoryCache,
9 | HttpLink
10 | } from "apollo-client-preset";
11 |
12 | import App from "./components/App";
13 | const GRAPHCMS_API =
14 | "https://api-uswest.graphcms.com/v1/cju0304j40hm801cj1530pd2e/master";
15 |
16 | const httpLink = new HttpLink({
17 | uri: GRAPHCMS_API
18 | });
19 |
20 | const middlewareAuthLink = new ApolloLink((operation, forward) => {
21 | const token =
22 | "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ2ZXJzaW9uIjoxLCJ0b2tlbklkIjoiODQ2YzgxOGMtM2IxYi00ZmM3LThkZDItNjQxYjU1YTdiYWY5In0.17xeTnclFL2y8hXxUkWfuWOdOwQ4QD_bVbqmOYFFQjI";
23 | const authorizationHeader = token ? `Bearer ${token}` : null;
24 | operation.setContext({
25 | headers: {
26 | authorization: authorizationHeader
27 | }
28 | });
29 | return forward(operation);
30 | });
31 |
32 | const client = new ApolloClient({
33 | link: middlewareAuthLink.concat(httpLink),
34 | cache: new InMemoryCache()
35 | });
36 |
37 | ReactDOM.render(
38 |
39 |
40 |
41 |
42 | ,
43 | document.getElementById("root")
44 | );
45 |
--------------------------------------------------------------------------------
/src/components/Home.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useContext} from "react";
2 | import {graphql} from "react-apollo";
3 | import Container from "muicss/lib/react/container";
4 | import { singleArtist } from '../graphql/queries'
5 | import AddSong from './AddSong'
6 | import Context from '../context'
7 |
8 | const Home = ({ data: { loading, error, songs } }) => {
9 | const {state, dispatch} = useContext(Context);
10 | useEffect(() => {
11 | if(songs) {
12 | dispatch({type: "ADD_CONTENT", payload: songs});
13 | }
14 | }, [songs]);
15 |
16 | if (error) return
Error fetching songs
;
17 | if (state ) {
18 | return (
19 |
20 |
21 |
22 | {state.songs && state.songs.map(song => (
23 |
24 |
25 |
{song.name}
26 |
{song.artist}
27 |
{song.lyrics}
28 |
29 |
30 | ))}
31 |
32 |
33 | );
34 | }
35 | return Loading posts...
;
36 | };
37 |
38 |
39 | export default graphql(singleArtist, {
40 | options: ({ match }) => ({
41 | variables: {
42 | artist: match.params.slug,
43 | name: match.params.slug,
44 | lyrics: match.params.slug
45 | }
46 | })
47 | })(Home);
48 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
22 | React App
23 |
24 |
25 |
26 |
27 |
32 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/src/components/AddSong.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useContext } from "react";
2 | import { Mutation } from "react-apollo";
3 | import { ADD_SONG } from '../graphql/mutations'
4 | import Container from "muicss/lib/react/container";
5 | import Context from '../context';
6 |
7 |
8 | const AddSong = () => {
9 | const [name, setName] = useState("");
10 | const [artist, setArtist] = useState("");
11 | const [lyrics, setLyrics] = useState("");
12 | const {dispatch} = useContext(Context);
13 |
14 | return (
15 |
16 | {(addSong) => (
17 |
46 | )}
47 |
48 | )
49 | }
50 |
51 | export default AddSong
--------------------------------------------------------------------------------
/src/createServiceWorker.js:
--------------------------------------------------------------------------------
1 | // In production, we register a service worker to serve assets from local cache.
2 |
3 | // This lets the app load faster on subsequent visits in production, and gives
4 | // it offline capabilities. However, it also means that developers (and users)
5 | // will only see deployed updates on the "N+1" visit to a page, since previously
6 | // cached resources are updated in the background.
7 |
8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
9 | // This link also includes instructions on opting out of this behavior.
10 |
11 | export default function register() {
12 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
13 | window.addEventListener('load', () => {
14 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
15 | navigator.serviceWorker
16 | .register(swUrl)
17 | .then(registration => {
18 | // eslint-disable-next-line no-param-reassign
19 | registration.onupdatefound = () => {
20 | const installingWorker = registration.installing;
21 | installingWorker.onstatechange = () => {
22 | if (installingWorker.state === 'installed') {
23 | if (navigator.serviceWorker.controller) {
24 | // At this point, the old content will have been purged and
25 | // the fresh content will have been added to the cache.
26 | // It's the perfect time to display a "New content is
27 | // available; please refresh." message in your web app.
28 | console.log('New content is available; please refresh.'); // eslint-disable-line no-console
29 | } else {
30 | // At this point, everything has been precached.
31 | // It's the perfect time to display a
32 | // "Content is cached for offline use." message.
33 | console.log('Content is cached for offline use.'); // eslint-disable-line no-console
34 | }
35 | }
36 | };
37 | };
38 | })
39 | .catch(error => {
40 | console.error('Error during service worker registration:', error);
41 | });
42 | });
43 | }
44 | }
45 |
46 | export function unregister() {
47 | if ('serviceWorker' in navigator) {
48 | navigator.serviceWorker.ready.then(registration => {
49 | registration.unregister();
50 | });
51 | }
52 | }
53 |
--------------------------------------------------------------------------------