Skip to content

Instantly share code, notes, and snippets.

@MelodicCrypter
Last active February 5, 2022 08:27
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save MelodicCrypter/a236eb8c2b69f02c80a6aa23ed34f8c6 to your computer and use it in GitHub Desktop.
Starter settings for React (Web or Native) with TypeScript, ESLint, Prettier, and Apollo. Also, GraphQL, Bulma and Webfont Loader are included.
# dependencies
/node_modules
# testing
/coverage
# production
build/*
build
public/*
public
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
src/react-app-env.d.ts
src/serviceWorker.ts
{
"env": {
"browser": true,
"commonjs": true,
"es6": true,
"node": true,
"jest": true,
"react-native/react-native": true
},
"plugins": [
"react",
"react-native",
"react-hooks",
"prettier",
"@typescript-eslint",
"jsx-a11y"
],
"extends": [
"airbnb-typescript",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
"prettier/@typescript-eslint",
"prettier",
"prettier/react",
"plugin:prettier/recommended",
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2018,
"allowImportExportEverywhere": false,
"codeFrame": false,
"ecmaFeatures": {
"jsx": true
},
"project": "./tsconfig.json"
},
"settings": {
"react": {
"version": "detect"
},
"import/resolver": {
"typescript": {
"alwaysTryTypes": true
}
}
},
"rules": {
// React specifics
"react/jsx-key": "error",
"react/no-unused-prop-types": 0,
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"react/button-has-type": 0,
"react/jsx-filename-extension": ["off"],
"react/prop-types": "warn",
// ESLint specifics
"prefer-promise-reject-errors": ["off"],
"no-nested-ternary": "off",
"no-return-assign": ["off"],
"no-shadow": "off",
"no-lonely-if": "warn",
"consistent-return": "off",
"no-use-before-define": "warn",
"no-underscore-dangle": "off",
"no-unused-vars": "warn",
"import/no-unresolved": "off",
"no-unused-expressions": "off",
"radix": "off",
"spaced-comment": "off",
"jsx-a11y/alt-text": "warn",
"jsx-a11y/click-events-have-key-events": "warn",
"jsx-a11y/no-static-element-interactions": "warn",
"jsx-a11y/no-noninteractive-element-interactions": "warn",
"prettier/prettier": "error",
"arrow-parens": ["error", "always"],
"indent": "off",
"camelcase": "off",
// Typescript specifics
"@typescript-eslint/camelcase": [
"error",
{
"properties": "never",
"genericType": "never",
"ignoreDestructuring": true
}
],
"max-len": [
"error",
{
"code": 100
}
],
"@typescript-eslint/no-unused-vars": [
"warn",
{
"vars": "all",
"args": "none"
}
],
"@typescript-eslint/no-explicit-any": [
"error",
{
"ignoreRestArgs": true
}
]
}
}
{
"endOfLine": "lf",
"tabWidth": 4,
"printWidth": 100,
"trailingComma": "es5",
"semi": true,
"singleQuote": true,
"arrowParens": "always"
}
import React from 'react';
import { ApolloProvider } from '@apollo/react-hooks';
// Local Modules
import Main from './Components/Main';
// Apollo client
import client from './Utils/withApolloClient';
// <App /> Component
const App = () => {
return (
<ApolloProvider client={client}>
<Main />
</ApolloProvider>
);
};
export default App;
const fontsList: Array<any> = [
{
font: 'Roboto',
weights: [400, 500],
},
{
font: 'Teko',
weights: [500, 600],
},
{
font: 'Yanone Kaffeesatz',
weights: [400, 500],
},
{
font: 'Nunito',
weights: [400, 600, 700],
},
];
export default fontsList;
import React from 'react';
import { render } from 'react-dom';
import WebFont from 'webfontloader';
// Local Modules
import App from './App';
// Styles
import './Styles/main.scss';
// Google Fonts
WebFont.load({
google: {
families: ['Roboto', 'Nunito'],
},
});
// Launch 🚀
const root = document.getElementById('root');
if (root !== null) {
render(
<React.StrictMode>
<App />
</React.StrictMode>,
root,
);
}
import React from 'react';
import { PersistGate } from 'redux-persist/integration/react';
import { Provider } from 'react-redux';
import { ApolloProvider, ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client';
import { createUploadLink } from 'apollo-upload-client';
import { setContext } from '@apollo/client/link/context';
import { HelmetProvider } from 'react-helmet-async';
import GoogleFontLoader from 'react-google-font-loader';
import { Lines } from 'react-preloaders';
// Components
import MainGate from 'components/MainGate';
// Store
import { store, persistor } from 'store/store';
// Utils
import isDev from 'utils/devUtils';
// Other
import fontsList from './data/fontsList';
// Style
import 'styles/scss/main.scss';
import 'slick-carousel/slick/slick.css';
import 'slick-carousel/slick/slick-theme.css';
import 'antd/dist/antd.css';
// Apollo >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// 0. Determine if dev or production
const appUri = isDev() ? process.env.REACT_APP_GQL_DEV_SERVER_URI : process.env.REACT_APP_GQL_PROD_SERVER_URI;
// 1. Prepare the URI
const httpLink = createUploadLink({ uri: appUri });
// 2. Authorization, token
const authLink = setContext((_, { headers }) => {
const lsPrefix = process.env.REACT_APP_LS_PREFIX;
const token = localStorage.getItem(`${lsPrefix}app_token`);
return {
headers: {
...headers,
authorization: token ? `Bearer ${JSON.parse(token)}` : '',
},
};
});
// 3. The client
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
connectToDevTools: isDev(),
});
// The app >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
const App = (): React.ReactElement => {
return (
<ApolloProvider client={client}>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<HelmetProvider>
<GoogleFontLoader fonts={fontsList} />
<MainGate />
<Lines />
</HelmetProvider>
</PersistGate>
</Provider>
</ApolloProvider>
);
};
export default App;
import localforage from 'localforage';
const lfConfig = () => {
localforage.config({
driver: localforage.LOCALSTORAGE,
name: 'Betting',
version: 1,
storeName: 'BettingStore',
description: 'Local storage for Betting-App using localforage',
});
};
export default lfConfig;
// Start it up
npx create-react-app my-app --template typescript
# or
yarn create react-app my-app --template typescript
// Typescript with ESLint and Prettier (DEV Dependencies)
yarn add -D @typescript-eslint/eslint-plugin @typescript-eslint/parser babel-eslint eslint eslint-config-airbnb-typescript eslint-config-airbnb eslint-config-prettier eslint-config-react-app eslint-import-resolver-typescript eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-prettier eslint-plugin-react eslint-plugin-react-hooks prettier pretty-quick eslint-plugin-react-native
// If not used create-react-app with typescript above, you can also manually install along with others
yarn add typescript @testing-library/jest-dom @testing-library/react @ant-design/icons @types/jest @types/node @types/react @types/react-dom
// Creating the configs
touch .eslintignore .eslintrc .prettierrc .env .sample.env
// Apollo and GraphQL
yarn add @apollo/react-hooks apollo-boost graphql-tag graphql
// others
yarn add react-router-dom sass antd bootstrap@5.1.3 react-google-font-loader firebase
// Delete CRA unneeded files then create some dirs
rm src/logo.svg src/index.css src/App.css && cd src/ && mkdir styles components utils data assets api && cd components/ && mkdir app mainGate && cd ../styles/ && touch app.scss && cd ../utils/ && touch withApolloClient.ts && cd ../../
import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
import thunk from 'redux-thunk';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage'; // default
import { composeWithDevTools } from 'redux-devtools-extension';
import categories from './reducers/categoryReducer';
import cards from './reducers/cardReducer';
import users from './reducers/userReducer';
// All reducers
const rootReducer = combineReducers({
users,
});
// Middleware: Redux Persist Config
const persistConfig = {
key: 'root',
storage,
};
// Middleware: Redux Persist Persisted Reducer
const persistedReducer = persistReducer(persistConfig, rootReducer);
// This will prevent 'several store enhancers' error
// const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
// Redux: Store
const store: any = createStore(persistedReducer, composeWithDevTools(applyMiddleware(thunk)));
// Middleware: Redux Persist Persister
const persistor = persistStore(store);
// Exports
export { store, persistor };
Take Note:
- react-app inside .eslintrc (plugins) won't work alongside TypeScript? I'm not sure with this one as well..
// As of TypeScript 3.7.* this is the default
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react"
},
"include": [
"src"
]
}
// We need to add something to avoid 'some' errors
{
"compilerOptions": {
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"downlevelIteration": true,
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react",
"noImplicitAny": false
},
"include": [
"src"
],
"exclude": [
"node_modules"
]
}
import ApolloClient, { InMemoryCache } from 'apollo-boost';
import gql from 'graphql-tag';
// Apollo: GQLS
const LOCAL_MAIN_DEF_QUERY = gql`
query @client {
clickStat
identifierWrapper
trailerType
modalOn
}
`;
const LOCAL_MAIN_DEF_MUTATION = gql`
mutation LOCAL_DEF_MUTATION($obj: [String]) {
setDef(obj: $obj) @client
}
`;
// Query and Mutation
const resolvers = {
Mutation: {
setDef: (_root, args, { cache }) => {
// Get the new values
const {
obj: { modalOn, clickStat, identifierWrapper, trailerType },
} = args;
// Get the current state
const existingData = cache.readQuery({
query: LOCAL_MAIN_DEF_QUERY,
});
// Prepare data
const newData = {
data: {
...existingData,
modalOn,
clickStat,
identifierWrapper,
trailerType,
},
};
// Save new data
cache.writeData(newData);
return newData;
},
saveData: (_root, args, { cache }) => {
// type is just a identifier
// movArray is the movie array of objects
const { type, movArray } = args;
let newMovArray;
// Add __typename key, Apollo Client is strict
if (type !== 'heroMovData') {
newMovArray = movArray.map(v => ({ ...v, __typename: 'Movie' }));
} else if (type === 'heroMovData') {
newMovArray = { ...movArray, __typename: 'Movie' };
}
// Prepare data
const data = {
data: { [args.type]: newMovArray },
};
// Save new data
cache.writeData(data);
return data;
},
},
};
// TypeDefs
const typeDefs = gql`
type Movie {
original_title: String
}
`;
// Defaul Local States
const defaults = {
__typename: 'Defaults',
clickStat: '',
};
// The Apollo client
const client = new ApolloClient({
cache: new InMemoryCache(),
clientState: {
resolvers,
defaults,
typeDefs,
},
});
// export { };
export default client;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment