├── .eslintignore
├── .eslintrc
├── .gitignore
├── .lintstagedrc.js
├── .npmrc
├── .prettierignore
├── README.md
├── package.json
├── prettier.config.js
├── relay.config.js
├── schema.graphql
├── src
├── App.tsx
├── ConferencesQuery.tsx
├── Home.tsx
├── __generated__
│ └── ConferencesQuery.graphql.ts
├── index.html
├── index.tsx
└── relay
│ └── Environment.ts
├── tsconfig.json
├── webpack.config.common.js
├── webpack.config.development.js
├── webpack.config.production.js
└── yarn.lock
/.eslintignore:
--------------------------------------------------------------------------------
1 | /src/__generated__/*
2 | /webpack*.js
3 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "settings": {
3 | "react": {
4 | "version": "detect"
5 | }
6 | },
7 | "parser": "@typescript-eslint/parser",
8 | "plugins": ["@typescript-eslint"],
9 | "extends": [
10 | "canonical",
11 | "canonical/react",
12 | "plugin:@typescript-eslint/recommended",
13 | "prettier",
14 | "prettier/@typescript-eslint",
15 | "prettier/babel",
16 | "prettier/react",
17 | "prettier/unicorn"
18 | ],
19 | "root": true,
20 | "rules": {
21 | "class-methods-use-this": 0,
22 | "filenames/match-regex": 0,
23 | "no-unused-vars": 0,
24 | "arrow-body-style": 0,
25 | "react/no-multi-comp": 0,
26 | "no-extra-parens": 0,
27 | "import/max-dependencies": 0,
28 | "id-length": 0,
29 | "filenames/match-exported": 0,
30 | "react/jsx-no-bind": 0,
31 | "import/no-namespace": 0,
32 | "import/no-unresolved": 0,
33 | "object-curly-newline": 0,
34 | "no-nested-ternary": 0,
35 | "react/require-default-props": 0,
36 | // Allows us to use .graphql extensions for importing relay artifacts
37 | "import/extensions": 0,
38 | // There are types to be explicit and times to use inference.
39 | // React components are good example of place I like inference for return types.
40 | "@typescript-eslint/explicit-module-boundary-types": 0,
41 | // Disabled because in TS files we usually declare types at the top of the file, and in cases we need to export them.
42 | "import/exports-last": 0,
43 | "react/jsx-boolean-value": 0
44 | },
45 | "overrides": [
46 | {
47 | "files": ["**/*.d.ts"],
48 | "rules": {
49 | "@typescript-eslint/ban-types": 0,
50 | "@typescript-eslint/no-explicit-any": 0,
51 | "import/unambiguous": 0
52 | }
53 | },
54 | {
55 | "files": ["**/*.ts", "**/*.tsx"],
56 | "rules": {
57 | "@typescript-eslint/no-explicit-any": 2,
58 | "@typescript-eslint/no-empty-function": [
59 | 2,
60 | { "allow": ["arrowFunctions"] }
61 | ],
62 | "import/no-unassigned-import": 0
63 | }
64 | }
65 | ]
66 | }
67 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | coverage
2 | dist
3 | node_modules
4 | *.log
5 | package-lock.json
6 | .*
7 | !*/*.babelrc.js
8 | !.dockerignore
9 | !.editorconfig
10 | !.eslintignore
11 | !.eslintrc
12 | !.gitignore
13 | !.gitlab-ci.yml
14 | !.npmignore
15 | !.storybook
16 | !.npmrc
17 | !.svgrrc.js
18 | !.prettierignore
19 | !.lintstagedrc.js
20 |
--------------------------------------------------------------------------------
/.lintstagedrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | '*.{js,jsx,ts,tsx}': ['eslint --fix', 'prettier --write'],
3 | // jsonlint can process only one file at a time.
4 | '*.json': (filenames) =>
5 | filenames.map(
6 | (filename) => `jsonlint --in-place --sort-keys '${filename}'`
7 | ),
8 | };
9 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | package-lock=false
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | package.json
2 | /src/__generated__/*
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Router V6 + Relay Hooks POC
2 |
3 | **Please note that this repo is merely a work in progress and is currently not yet ready for use. It does not work and should not be taken as a successful example of React Router v6 and Relay hooks working together.**
4 |
5 | ```bash
6 | yarn
7 |
8 | export API_URL='https://api.react-finland.fi/graphql'
9 |
10 | yarn start
11 |
12 | ```
13 |
14 | - This repo uses the react-finland graphql test project found at https://api.react-finland.fi/graphql to provide a live graphql interface to work with.
15 |
16 | - This repo also defines routes as objects, using the `useRouter` hook.
17 |
18 | - There is one query -- `ConferencesQuery` -- which will serve as the proof of concept query for preloading in this repo.
19 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "browserslist": {
3 | "development": [
4 | "last 1 chrome version",
5 | "last 1 firefox version",
6 | "last 1 safari version"
7 | ],
8 | "production": [
9 | ">0.2%",
10 | "not dead",
11 | "not op_mini all"
12 | ]
13 | },
14 | "dependencies": {
15 | "@babel/core": "^7.10.3",
16 | "@babel/node": "^7.10.5",
17 | "@babel/plugin-proposal-numeric-separator": "^7.10.1",
18 | "@babel/plugin-proposal-object-rest-spread": "^7.10.3",
19 | "@babel/plugin-proposal-optional-chaining": "^7.10.3",
20 | "@babel/polyfill": "^7.11.5",
21 | "@babel/preset-env": "^7.10.3",
22 | "@babel/preset-react": "^7.10.1",
23 | "@babel/preset-typescript": "^7.10.4",
24 | "@babel/register": "^7.10.4",
25 | "@types/react": "^16.9.49",
26 | "@types/react-dom": "^16.9.8",
27 | "@types/react-relay": "^7.0.10",
28 | "@types/react-router-dom": "^5.1.5",
29 | "@types/webpack-dev-middleware": "^3.7.1",
30 | "@types/webpack-dev-server": "^3.11.0",
31 | "@typescript-eslint/eslint-plugin": "^3.9.0",
32 | "@typescript-eslint/parser": "^3.9.0",
33 | "babel-loader": "^8.1.0",
34 | "babel-plugin-relay": "^10.0.1",
35 | "babel-plugin-styled-components": "^1.11.1",
36 | "clean-webpack-plugin": "^3.0.0",
37 | "eslint": "^7.3.1",
38 | "eslint-config-canonical": "^23.0.1",
39 | "eslint-config-prettier": "^6.11.0",
40 | "get-graphql-schema": "^2.1.2",
41 | "graphql": "^15.1.0",
42 | "history": "^5.0.0",
43 | "html-webpack-plugin": "^4.4.1",
44 | "husky": "^4.2.5",
45 | "jsonlint": "^1.6.3",
46 | "lint-staged": "^10.2.11",
47 | "mini-css-extract-plugin": "^0.10.0",
48 | "optimize-css-assets-webpack-plugin": "^5.0.3",
49 | "prettier": "2.1.0",
50 | "react": "^0.0.0-experimental-94c0244ba",
51 | "react-dom": "^0.0.0-experimental-94c0244ba",
52 | "react-relay": "^0.0.0-experimental-183bdd28",
53 | "react-router": "^0.0.0-experimental-ffd8c7d0",
54 | "react-router-dom": "^0.0.0-experimental-ffd8c7d0",
55 | "relay-compiler": "^10.0.1",
56 | "relay-compiler-language-typescript": "^13.0.1",
57 | "relay-config": "^10.0.1",
58 | "relay-runtime": "^10.0.1",
59 | "terser-webpack-plugin": "^4.1.0",
60 | "typescript": "^4.0.2",
61 | "webpack": "^4.44.1",
62 | "webpack-assets-manifest": "^3.1.1",
63 | "webpack-cli": "^3.3.12",
64 | "webpack-dev-middleware": "^3.7.2",
65 | "webpack-dev-server": "^3.11.0",
66 | "webpack-merge": "^5.1.2"
67 | },
68 | "husky": {
69 | "hooks": {
70 | "pre-commit": "lint-staged",
71 | "pre-push": "yarn lint && yarn tsc && yarn test"
72 | }
73 | },
74 | "license": "UNLICENSED",
75 | "private": true,
76 | "scripts": {
77 | "build": "webpack --config=./webpack.config.production.js",
78 | "lint": "eslint src",
79 | "refetch-graphql-schema": "get-graphql-schema $API_URL > ./schema.graphql",
80 | "relay-compile": "relay-compiler --src ./src --schema ./schema.graphql --language typescript --artifactDirectory ./src/__generated__ --extensions js jsx ts tsx",
81 | "start": "yarn relay-compile && NODE_ENV=development webpack-dev-server --config=webpack.config.development.js --open",
82 | "start-server": "yarn refetch-graphql-schema && yarn start",
83 | "tsc": "tsc"
84 | }
85 | }
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-commonjs */
2 | /* eslint-disable import/unambiguous */
3 |
4 | /**
5 | * Options for Prettier.
6 | *
7 | * @see https://prettier.io/docs/en/options.html
8 | */
9 |
10 | module.exports = {
11 | singleQuote: true,
12 | };
13 |
--------------------------------------------------------------------------------
/relay.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-commonjs, import/unambiguous */
2 | module.exports = {
3 | exclude: [
4 | '**/node_modules/**',
5 | '**/__mocks__/**',
6 | '**/__generated__/**',
7 | ],
8 | schema: './schema.graphql',
9 | src: './src',
10 | };
11 |
--------------------------------------------------------------------------------
/schema.graphql:
--------------------------------------------------------------------------------
1 | type Colors {
2 | primary: String!
3 | secondary: String!
4 | text: String!
5 | background: String!
6 | }
7 |
8 | type Conference {
9 | id: ID!
10 | series: String!
11 | name: String!
12 | organizer: Contact!
13 | year: String!
14 | startDate: String!
15 | endDate: String!
16 | slogan: String!
17 | websiteUrl: String!
18 | locations: [Location!]
19 | organizers: [Contact!]!
20 | mcs: [Contact!]
21 | partners: [Contact!]
22 | sponsors: [Contact!]!
23 | goldSponsors: [Contact!]
24 | silverSponsors: [Contact!]
25 | bronzeSponsors: [Contact!]
26 | schedules: [Schedule!]!
27 | allSpeakers: [Contact!]
28 | speakers: [Contact!]
29 | keynoteSpeakers: [Contact!]
30 | fullTalkSpeakers: [Contact!]
31 | lightningTalkSpeakers: [Contact!]
32 | workshopInstructors: [Contact!]
33 | talks: [Session!]
34 | workshops: [Session!]
35 | attendees: [Contact!]!
36 | keynotes: [Session!]!
37 | fullTalks: [Session!]!
38 | lightningTalks: [Session!]!
39 | }
40 |
41 | type Contact {
42 | firstName: String!
43 | lastName: String!
44 | name: String!
45 | about: String!
46 | aboutShort: String
47 | company: String
48 | image: Image!
49 | type: [ContactType!]!
50 | social: Social!
51 | keywords: [String!]
52 | location: Location!
53 | talks: [Session!]
54 | workshops: [Session!]
55 | noPhotography: Boolean
56 | country: Country! @deprecated(reason: "Use `location` instead")
57 | }
58 |
59 | """Type of the contact"""
60 | enum ContactType {
61 | PRESS
62 | SPEAKER
63 | TALK
64 | LIGHTNING_TALK
65 | KEYNOTE
66 | WORKSHOP
67 | WORKSHOP_HOST
68 | ORGANIZER
69 | SPONSOR
70 | GOLD_SPONSOR
71 | SILVER_SPONSOR
72 | BRONZE_SPONSOR
73 | PARTNER
74 | ATTENDEE
75 | }
76 |
77 | type Country {
78 | name: String!
79 | code: String!
80 | }
81 |
82 | type Font {
83 | family: String
84 | weight: String
85 | style: String
86 | fileName: String
87 | formats: [String!]
88 | href: String
89 | }
90 |
91 | type Fonts {
92 | primary: String!
93 | secondary: String!
94 | variants: [Font!]!
95 | }
96 |
97 | type Image {
98 | url: String!
99 | title: String
100 | style: Style
101 | }
102 |
103 | type Interval {
104 | begin: String!
105 | end: String!
106 | title: String
107 | sessions: [Session!]!
108 | location: Location
109 | }
110 |
111 | type Location {
112 | name: String
113 | about: String
114 | image: Image
115 | social: Social
116 | country: Country
117 | city: String
118 | address: String
119 | }
120 |
121 | type Logos {
122 | black: WithWithoutText!
123 | colored: WithWithoutText!
124 | white: WithWithoutText!
125 | }
126 |
127 | type Query {
128 | conference(id: ID!): Conference!
129 | conferences: [Conference!]!
130 | allConferences: [Conference!]! @deprecated(reason: "Use `conferences` instead")
131 | contact(conferenceId: ID!, contactName: String!): Contact!
132 | locations: [Contact!]!
133 | people: [Contact!]!
134 | sponsors: [Contact!]!
135 | schedule(day: String!, conferenceId: ID!): Schedule!
136 | series(id: ID!): Series!
137 | allSeries: [Series!]!
138 | themes: [Theme!]!
139 | theme(conferenceId: ID!): Theme!
140 | }
141 |
142 | type Schedule {
143 | day: String!
144 | location: Location
145 | description: String
146 | intervals: [Interval!]!
147 | }
148 |
149 | type Series {
150 | id: ID!
151 | name: String!
152 | conferences: [Conference!]!
153 | }
154 |
155 | type Session {
156 | type: SessionType!
157 | title: String!
158 | hasTitle: Boolean!
159 | description: String
160 | keywords: [String!]
161 | location: Location
162 | people: [Contact!]
163 | urls: SessionUrls
164 | sessions: [Session!]
165 | speakers: [Contact!]! @deprecated(reason: "Use `people` instead")
166 | }
167 |
168 | """Type of the session"""
169 | enum SessionType {
170 | TALK
171 | LIGHTNING_TALK
172 | KEYNOTE
173 | WORKSHOP
174 | PANEL
175 | BREAKFAST
176 | LUNCH
177 | COFFEE_BREAK
178 | ORGANIZATIONAL
179 | PARTY
180 | }
181 |
182 | type SessionUrls {
183 | web: String
184 | slides: String
185 | video: String
186 | }
187 |
188 | type Social {
189 | homepage: String
190 | twitter: String
191 | github: String
192 | facebook: String
193 | googleMaps: String
194 | medium: String
195 | instagram: String
196 | linkedin: String
197 | youtube: String
198 | vk: String
199 | pinterest: String
200 | vimeo: String
201 | dribble: String
202 | devto: String
203 | }
204 |
205 | type Style {
206 | backgroundSize: String!
207 | }
208 |
209 | type Theme {
210 | id: ID!
211 | fonts: Fonts!
212 | textures: [Image!]!
213 | colors: Colors!
214 | logos: Logos!
215 | }
216 |
217 | type WithWithoutText {
218 | withoutText: Image!
219 | withText: Image!
220 | }
221 |
222 |
--------------------------------------------------------------------------------
/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { RelayEnvironmentProvider, preloadQuery } from 'react-relay/hooks';
3 | import { BrowserRouter, useRoutes } from 'react-router-dom';
4 | import { Home } from './Home';
5 | import ConferencesQuery from './__generated__/ConferencesQuery.graphql';
6 | import { relayEnvironment } from './relay/Environment';
7 |
8 | const conferencesQuery = () => {
9 | const preloadedQuery = preloadQuery(
10 | relayEnvironment,
11 | ConferencesQuery,
12 | {},
13 | {
14 | fetchPolicy: 'store-or-network',
15 | }
16 | );
17 |
18 | return {
19 | data: preloadedQuery,
20 | };
21 | };
22 |
23 | const Routes = () => {
24 | const element = useRoutes([
25 | {
26 | element: