Edited and formatted by ericwm76 Eric's gist https://gist.github.com/ericwm76/e1204fc03f14af4429add8225ff55f71 # Creating a React App 1. In the terminal run: `npx create-react-app NAME-OF-APP` 2. Cd into the new directory: `cd NAME-OF-APP` 3. Run: `npm install`. 4. You can run `npm start` to see if the app was set up correctly. ## Setup Redux 1. `npm i redux react-redux redux-devtools-extension -S` - redux - Allows us to have access to using Redux in our app. - react-redux - Allows us to connect our react components to our Redux store. - redux-devtools-extension - Useful for debugging in our devtools 2. In src/index.js `import { Provider } from 'react-redux';` - a component from react-redux that wraps our App component and allows each child component to be connected to the store `import { createStore } from 'redux';` - a function from Redux that uses the rootReducer to create the store `import { composeWithDevTools } from 'redux-devtools-extension';` - a method we brought in and can pass as an argument with createStore so that we have access to our devtools and can view our store. `import { rootReducer } from './reducers';` - our combined reducers to create our store (order matters here) ```const store = createStore(rootReducer, composeWithDevTools()) ReactDOM.render( , document.getElementById('root') ); ``` 3. In actions/index.js - 4. In src/reducers/index.js ``` import { combineReducers } from 'redux'; import { todos } from './todos'; export const rootReducer = combineReducers({ todos: todos }); ``` **Where we define the properties that will exsits in our global store 5. `npm i redux-thunk -S` ## Setup Backend 1. Clone repo, not nested in project directory 2. Globally install nodemon. Runs the server. 3. `npm install nodemon -g` 4. cd into repo 5. Run `npm install` 6. Run `npm start` 7. Use Postman to checkout the data ## Setting Up Testing 1. Install Enzyme: `npm i enzyme -D` 2. Install Enzyme Adapter: `npm install enzyme-adapter-react-16 -D` 3. Inside of /src create setupTests.js: `touch src/setupTests.js` 4. Put the following 3 lines of code in setupTests.js ``` import { configure } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; configure({ adapter: new Adapter() }); ``` 5. For snapshot testing, install Enzyme Wrappers: `npm install enzyme-to-json -D` 6. Add serializer to package.json: ``` "jest": { "snapshotSerializers": [ "enzyme-to-json/serializer" ] } ``` ***Don't forget the comma!*** 7. Add an extra line in App.js (just so there's a change in the file) and save. Then run `npm test` to check that the files are connected correctly. 8. Include the following lines as headers for all test files: ``` import React from 'react'; import { shallow } from 'enzyme'; import ClassName from './ClassName'; ``` ### Check Testing Coverage ```npm test -- --coverage --watchAll=false``` ### Setting Up ESLint 1. ESLint is already built in with create-react-app. Installing another eslint will likely break things. 2. Add a script called `"lint": "eslint src/" in your package.json` (in the scripts object) 3. In your terminal, run: `npm run lint` 4. Turing [Mod3 Linter](https://github.com/turingschool-examples/javascript/blob/master/linters/module-3/linter-setup.md) ### Install SCSS/Sass 1. Run: `npm install node-sass --save` 2. Add variable file: `touch src/variables.scss` 3. Change all `.css` file extentions to `.scss` - `index.css App.css` => `index.scss App.scss` 4. Remeber to update the file paths anywhere the style files are being imported 5. Add: `@import './src/variables.scss';` To any .scss files you want to uses variables 6. Format for creating variables: `$var-name: var-value;` ## propTypes ```npm install prop-types -S``` ```import PropTypes from 'prop-types'``` ``` Component.propTypes = { prop1: PropTypes.array.isRequired, prop2: PropTypes.any, prop3: PropTypes.func.isRequired, prop4: PropTypes.bool prop5: PropTypes.string.isRequired, prop6: PropTypes.number } ``` ## Router 1. `npm i react-router-dom -S` 2. In index.js ``` import { BrowserRouter } from 'react-router-dom' const router = ( ) ReactDOM.render(router, document.getElementById('root')); ``` 3. Import what is needed in components `import { Route, NavLink, Link, Redirect, Switch} from 'react-router-dom'` ## TESTING ``` import { shallow } from 'enzyme'; ``` ``` wrapper = shallow ( ``` ``` expect(wrapper).toMatchSnapshot() ``` **Execution** ``` wrapper.instance().methodName('optionalArgument') ``` ``` wrapper.instance().forceUpdate() ``` ``` wrapper.find('button').simulate('click', optionalEvent) ``` ``` wrapper.instance().setState({ prop: 'value', prop2: 'value2' }) ``` ``` wrapper.find('[name='thing']').simulate('change', mockThingEvent) ``` ``` wrapper.find('button').at(3).simulate('click') expect(mockWhatEverEvent).toHaveBeenCalledWith('argument/value') ``` **Mock Date.now()** ``` global.Date.now = jest.spyOn(global.Date, 'now').mockImplementation(() => 123) ``` Can assert that the value of Date.now() will be 123 or whatever is set as the return value. **Mock e.preventDefault()** Can be passed as arugments of event in other methods ``` const mockEvent = { preventDefault: jest.fn() } ``` **Mock Event** ``` const mockThingEvent = { target: { name: 'thing', value: 'Thing value' } } ``` **Expectation** ``` expect(wrapper.instance().handleChange).toHaveBeenCalledWith(mockThingEvent) ``` ``` expect(wrapper.state('thing')).toEqual('Thing value') or expect(wrapper.state()).toEqual(expected) ``` ``` expect(wrapper.instance().method).toHaveBeenCalled() ``` ## Network Requests **POST** ``` const options = { method: 'POST', body: JSON.stringify({ id: value, prop: value, }), headers: { 'Content-Type': 'application/json' } } return fetch('url', options) .then(res => { if(!res.ok) { throw Error('Something is not right, try again later') } return res.json()}) ``` **DELETE** ``` const options = { method: 'DELETE', headers: { 'Content-Type': 'application/json' } } return fetch(`url/${id}`, options) .then(res => { if(!res.ok) { throw Error('Something is not right, try again later') } return getFetch(); }).catch(error => { throw Error(error.message) }); ```