├── 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 |
{ 18 | e.preventDefault(); 19 | addSong({ variables: { song: { 20 | name, 21 | artist, 22 | lyrics 23 | }} 24 | }); 25 | const songs = [{name, artist, lyrics}]; 26 | dispatch({type: "ADD_CONTENT", payload: songs}); 27 | setName(""); 28 | setLyrics(""); 29 | setArtist(""); 30 | }}> 31 | Add a new song 32 |
33 | setName(e.target.value)} /> 34 | 35 |
36 |
37 | setArtist(e.target.value)}/> 38 | 39 |
40 |
41 |