Skip to content

Instantly share code, notes, and snippets.

@vladd-png
Forked from Garrett-Iannuzzi/Do-The_Thing.md
Last active April 1, 2020 15:44
Show Gist options
  • Save vladd-png/1d52b7bba644303a2e6ff5b01a475919 to your computer and use it in GitHub Desktop.
Save vladd-png/1d52b7bba644303a2e6ff5b01a475919 to your computer and use it in GitHub Desktop.
Do-it-do-it-now

Do the thing

  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 React Native

  1. In the terminal run: npm install -g create-react-native-app
  2. Then run npm install -g expo-cli
  3. Now that CRNA is installed, we can create a new project by running the command create-react-native-app NAME-OF-APP
  4. Cd NAME-OF-APP
  5. Run: npm install.

Optional Dependencies

  • yarn add redux react-redux redux-thunk for managing the app’s state
  • is a middleware for Redux that allows us to write action creators that return a function instead of an action
  • yarn add react-native-router-flux for our apps navigation
  • yarn add react-native-elements as our UI Toolkit
  • yarn add react-native-size-matters for scaling the size your apps UI across different sized devices
  • yarn add axios a promised-based HTTP client for making calls to our API
  • yarn add moment moment.js

Final: Run expo start to test in browser

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
  1. 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(
  <Provider store={store} >
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </Provider>, 
  document.getElementById('root')
);
  1. In actions/index.js
export const addToDo = todo => ({
 type: 'ADD_TODO',
 todo
})
  1. In src/reducers/index.js
import { combineReducers } from 'redux';
import { todos } from './todos';

export const rootReducer = combineReducers({
  todos: todos
});

4A. Reducer Example: goes in own file 'isLoggedIn.js'

export const isLoggedIn = (state = null, action) => {
  switch(action.type) {
    case 'GET_LOGIN':
      return action.isLoggedIn
    default:
      return state
  }
}
  1. Connecting components to the Redux store:
import { connect } from 'react-redux';

mapState example:

export const mapState = state => ({
  intentions: state.intentions
})

export default connect(mapState)(IntentionsContainer)

mapDispatch example:

export const mapDispatch = dispatch => ({
  postIntention: intention => dispatch(postIntention(intention))
})

export default connect(null, mapDispatch)(Form);

**Where we define the properties that will exsits in our global store

  1. 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() });
  1. For snapshot testing, install Enzyme Wrappers: npm install enzyme-to-json --save-dev
  2. Add serializer to package.json, after devDependencies:
"jest": {
    "snapshotSerializers": [
      "enzyme-to-json/serializer"
    ],
    "coveragePathIgnorePatterns": [
      "src/index.js",
      "src/serviceWorker.js"
    ]
  }

Don't forget the comma!

  1. Get those sweet, sweet checkmarks in your tests - edit scripts line in package.json:
  "test": "react-scripts test --verbose"

Don't forget the comma!

  1. 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. If you get an error about fsEvents not being a function, run the following commands in terminal:
npm audit fix --force

Run npm test to see if that worked. If not, run:

brew install watchman
  1. 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

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
  1. Remember to update the file paths anywhere the style files are being imported
  2. Add: @import './src/variables.scss'; To any .scss files you want to uses variables
  3. Format for creating variables: $var-name: var-value;
  4. @mixins: touch src/mixins.scss
  5. create scss directory and move scss files (just variables and mixins) to the scss directory.
  6. Rounded corners:
@mixin rounded-corners($radius) {
  -webkit-border-radius: $radius;
     -moz-border-radius: $radius;
      -ms-border-radius: $radius;
          border-radius: $radius;
}

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 = (
  <BrowserRouter>
    <App />
  </BrowserRouter>
)

ReactDOM.render(router, document.getElementById('root'));
  1. Import what is needed in components import { Route, NavLink, Link, Redirect, Switch} from 'react-router-dom'

TESTING

import { shallow } from 'enzyme';
wrapper = shallow (<Component prop='string' prop2={2} method={jest.fn()} />
expect(wrapper).toMatchSnapshot()
  • Remember: you might need to mock apiCalls `jest.mock('file path to api call') at the top

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 res.json()
    }).catch(error => {
      throw Error(error.message)
    });

handleChange

  handleChange = e => {
    this.setState({ [e.target.name]: e.target.value})
  }

Testing async

import { getIdeas, postIdea } from './apiCalls'
// no need to import react
// no need to import enzyme
describe('apiCalls', () => {
  describe('getIdeas', () => {
    let mockResponse = [
      {
        id: 1,
        title: "Sweaters for pugs",
        description: "To keep them warm"
      }
    ]

    beforeEach(() => {
      window.fetch = jest.fn().mockImplementation(() => {
        return Promise.resolve({
          ok: true,
          json: () => {
            return Promise.resolve(mockResponse)
          }
        })
      })
    })

    it('should be passed the correct URL', () => {
      getIdeas()

      expect(window.fetch).toHaveBeenCalledWith('http://localhost:3001/api/v1/ideas')
    })
  
    it('should return an array of ideas', () => {
      expect(getIdeas()).resolves.toEqual(mockResponse)
    })
  
    it('should return an error for response that is !ok', () => {
      window.fetch = jest.fn().mockImplementation(() => {
        return Promise.resolve({
          ok: false
        })
      })
      expect(getIdeas()).rejects.toEqual(Error('Error fetching ideas'))
    })
  })

Testing Actions

import * as actions from '../actions';

describe('actions', () => {
 it('should have a type of ADD_TODO', () => {
   // Setup
   const text = "Go to the Vault";
   const id = 1;
   const expectedAction = {
     type: 'ADD_TODO',
     text: "Go to the Vault",
     id: 1
   };

   // Execution
   const result = actions.addTodo(text, id);

   // Expectation
   expect(result).toEqual(expectedAction);
 });
});

Testing Reducers

import { todosReducer } from '../reducers/todosReducer';

describe('todosReducer', () => {
  it('should return the initial state', () => {
    // Setup
    const expected = [];

    // Execution
    const result = todosReducer(undefined, {});

    // Expectation
    expect(result).toEqual(expected);
  });
  
    it('Should return the correct state if the correct action is GET_LOGIN', () => {
    const initialState = null;

    const action = { 
        type: 'GET_LOGIN',
        isLoggedIn: false
      }

    const result = isLoggedIn(initialState, action);
    const expected = false

    expect(result).toEqual(expected)
  });
});

Testing mapStateToProps

  describe('mapStateToProps', () => {
    it('should return an object with the todos array', () => {
      // Setup
      const mockState = {
        todos: [{text: 'Learn Redux!', id: 1}],
        filter: 'SHOW_ALL'
      };
      const expected = {
        todos: [{text: 'Learn Redux!', id: 1}]
      };

      // Execution
      const mappedProps = mapStateToProps(mockState);

      // Expectation
      expect(mappedProps).toEqual(expected);
    });
  });

Test mapDispatchToProps

  describe('mapDispatchToProps', () => {
  it('calls dispatch with an addTodo action when handleSubmit is called', () => {
    // Setup
    const mockDispatch = jest.fn();
    const actionToDispatch = addTodo('Learn Redux!', 1);

    // Execution
    const mappedProps = mapDispatchToProps(mockDispatch);
    mappedProps.handleSubmit('Learn Redux!', 1);

    // Expectaion
    expect(mockDispatch).toHaveBeenCalledWith(actionToDispatch);
  });
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment