Skip to content

Instantly share code, notes, and snippets.

@qfarenwald
Last active May 19, 2020 21:43
Show Gist options
  • Save qfarenwald/ac30fb1ee18f749d6cdac7681ca7854c to your computer and use it in GitHub Desktop.
Save qfarenwald/ac30fb1ee18f749d6cdac7681ca7854c to your computer and use it in GitHub Desktop.

Mod 3 Notes

Change remote

https://help.github.com/en/articles/changing-a-remotes-url

https://github.com/turingschool-examples/webpack-starter-kit

$ git remote -v
> origin  git@github.com:USERNAME/REPOSITORY.git (fetch)
> origin  git@github.com:USERNAME/REPOSITORY.git (push)

$ git remote set-url origin https://github.com/USERNAME/REPOSITORY.git
Verify that the remote URL has changed.

$ git remote -v
> origin  https://github.com/USERNAME/REPOSITORY.git (fetch)
> origin  https://github.com/USERNAME/REPOSITORY.git (push)

git push -u origin master

Creating a React App

  1. In the terminal run npx create-react-app NAMEOFYOURAPP
  2. Cd into the new directory: cd NAMEOFYOURAPP
  3. Run npm install.
  4. You can run npm start to see if the app was set up correctly.

Setting Up Testing

  1. Install enzyme: npm i enzyme -D
  2. Install enzyme-adapter-react-16: 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-to-json npm install enzyme-to-json -D
  2. In package.json, add the following lines of code:
  "jest": {
    "snapshotSerializers": [
      "enzyme-to-json/serializer"
    ]
  }

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.
  2. Include the following lines as headers for all test files:
import React from 'react';
import { shallow } from 'enzyme';
import ClassName from './ClassName';

Backend Setup

git clone [https://github.com/ REPO NAME THEY GIVE US]
cd [CD INTO REPO]
npm install nodemon -g
npm i
npm start

Note that the frontend should be running on localhost:3000 and the backend should be running on localhost:3001.

Clone down the backend repo - but NOT inside your existing frontend repository!

Set Up React Router

https://frontend.turing.io/lessons/module-3/react-router-v4.html

npm i react-router-dom -S

index.js

import { BrowserRouter } from 'react-router-dom'

const router = (
  <BrowserRouter>
    <App />
  </BrowserRouter>
)

ReactDOM.render(router, document.getElementById('root'));

In neccessary files:

import { Route, Link } from 'react-router-dom'

<Route path="/" render={() => <Component />} />

<Route exact path="/" render={() => <Component prop={this.prop} />} />

<Link to="/" className="link"><h5>BACK</h5></Link>

Setup Redirect

Used when a change needs to happen based on state

Import: Redirect

Need: setRedirect and renderRedirect functions

import { Redirect } from 'react-router-dom';

export class ComponentName extends Component {
  constructor() {
    super();
    this.state = {
      redirect: false
    };
  }
  
  handleSubmit = (e) => {
    const { actionName } = this.props;
    e.preventDefault();
    actionName(this.state);
    this.setRedirect();
  }

  setRedirect = () => {
    this.setState({
      redirect: true,
    });
  }

  renderRedirect = () => {
    if (this.state.redirect) {
      return <Redirect to="/confirmation" />;
    }
  }
  
    render() {
    return (
      <form className="ComponentName">
        <input
          className="class-name"
          name="name"
          type="text"
          placeholder="Enter Name"
          value={this.state.name}
          onChange={this.handleChange}
        />

        {this.renderRedirect()}
        <button disabled={!this.state.formReady} type="button" className="class-name" onClick={this.handleSubmit}>SUBMIT BOOKING</button>

        <Link to="/" className="link"><h5>BACK</h5></Link>
      </form>
    );
  }
}
}

Boilerplates - Components

Class Component

import React, { Component } from 'react';
import Classname from './Classname';
import Funcname from '../Funcname/Funcname';
import './App.css';

class App extends Component {
  constructor() {
    super();
    this.state = {
      key: value
    }
  }
  
  method = () => {
    console.log('run something')
  }
  
  render() {
    return (
      <div>
        Return something
      </div>
    )
  }
}

export default App;

Functional Component

import React from 'react';
import anotherFuncname from '../anotherFuncname/anotherFuncname';
import './Funcname.css';

const Funcname = () => {
  return (
    <div>
      Return something
    </div>
  )
}

export default Funcname;

Form Functions needed

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

  submitNewData = event => {
    event.preventDefault()
    const newData = {
      match key/value pairs of required data
    }
    this.props.funcFromApp(newData)
    this.clearInputs()
  }

  clearInputs = () => {
    this.setState({
      key1: '',
      key2: ''
    })
  }

Input Structure

<input
  name=''
  placeholder=''
  value={this.state.key}
  onChange={() => {}}
/>

setState

.then(ideas => this.setState({
        ideas: ideas
      }))
      
.then(ideas => this.setState({ ideas }))

this.setState({
    value: key
  })

setState Is Async

functionName = (parameter) => {
  this.setState({
    value: key
  }, () => {
    anotherFunctionName((parameter) => {
     return something
   }))
  });
 };

apiCalls

export const funcName = async(url)  => {
  const response = await fetch(url)
  if(response.ok) {
    const data  = await response.json()
    return data;
  } else {
    throw Error(response.statusText)
  }
}

Boilerplates - Tests

Snapshot Test

import React from 'react';
import { shallow } from 'enzyme';
import NameOfTested from './NameOfTested';

describe('NameOfTested', () => {
  it('should', () => {
    const wrapper = shallow(
      <Component />
    )
    expect(wrapper).toMatchSnapshot();
  })
})

Test Dynamic Changes Template

Setup - What do we need to do in order to render the component (aka shallow or mount). What data needs to be mocked?
Execution - Let’s run the command or simulate the action.
Expectation - This is where our assertion happens. After running our function, what did we expect to happen?

Update State Test

import React from 'react';
import ReactDOM from 'react-dom';
import { shallow } from 'enzyme';
import NameOfTested from './NameOfTested';

describe('NameOfTested', () => {
  it('should', () => {
    const wrapper = shallow(
      <Component />
    )
   const mockData = ?
   const expected = ?
   
    // Expectation
    expect(wrapper.state('stateKeyName')).toEqual([]);

    // Execution
    wrapper.instance().funcName(argumentName);

    // Expectation
   expect(wrapper.state('stateKeyName')).toEqual(expected);
  })
})

Function Test

it('should change state based on an event', () => {
    const mockFunction = jest.fn()
    const wrapper = shallow(<
      Component 
      submitUser={mockFunction}
    />)
    const mockEvent = {
      target: {
        name: 'name', value: 'value' 
      } 
    }

    wrapper.instance().handleChange(mockEvent)
    expect(wrapper.state('name')).toEqual('value')
  })

Simulate A Click Test

wrapper.instance().forceUpdate();

wrapper.find('element').simulate('click');
wrapper.find('element').at(0).simulate('click');
wrapper.find('button').simulate('click', mockEvent);

expect(mockFunction).toHaveBeenCalledWith(argument);
expect(wrapper.instance().resetInputs).toHaveBeenCalled();

Ex:
it('should run funcName on click', () => {
    const mockFuncName = jest.fn();
    const wrapper = shallow(
      <Component
        prop={look to parent to see what gets passed down}
        prop={look to parent to see what gets passed down}
      />
    )
    wrapper.find('element').at(0).simulate('click');

    expect(mockFuncName).toHaveBeenCalled();
  });

apiCall Tests

import { fetchData } from './apiCalls';

describe('fetchData', () => {
  const mockResponse = {
    results: [{
      key: 46286
    },
    {
      key: 601365
    }],
  };

  const mockUrl = 'https://www.address.com/...';

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

  it('should fetch with the correct url', () => {
    fetchData(mockUrl);

    expect(window.fetch).toHaveBeenCalledWith(mockUrl);
  });

  it('should return an array of ?? (HAPPY)', () => {
    fetchData(mockUrl)

      .then((results) => expect(results).toEqual(mockResponse));
  });

  it('should return an error (SAD)', () => {
    window.fetch = jest.fn().mockImplementation(() => Promise.resolve({
      ok: false
    }));

    const mockUrl = 'https://www.address.com/...YOLO';

    expect(fetchData(mockUrl)).rejects.toEqual(Error());
  });

  it('should return an error if the server is down', () => {
    window.fetch = jest.fn().mockImplementation(() => ({
      status: 500
    }))

    expect(fetchData()).rejects.toEqual(Error());
  });

});

Mock A Function

const mockFunction = jest.fn();

Mock An Event

const mockEvent = {
  preventDefault: jest.fn();
}

Mock Date.now()

global.Date.now = jest.fn().mockImplementation(() => 12345)

const expected = { title: '', description: '', id: 12345 };

Debug

console.log(wrapper.debug());

Verbose In package.json, under scripts, edit the "test" script:

"test": "react-scripts test --verbose"

Test Percentage Chart In Terminal

npm test --  --coverage --watchAll=false

Fetch and Post

  componentDidMount() {
    fetch('URL')
      .then(response => response.json())
      .then(data => this.setState({
        key: data
      }))
      .catch(error => console.error(error))
  }

  addIdea = varName => {
    const options = {
      method: 'POST',
      body: JSON.stringify(varName),
      headers: {
        'Content-Type': 'application/json'
      }
    };

    fetch('URL', options)
      .then(response => response.json())
      .then(data => this.setState({
        key: [...this.state.key, data]
      }))
      .catch(error => console.error(error))
  }

  removeIdea = id => {
    const options = {
      method: 'DELETE',
      headers: {
        'Content-Type': 'application/json'
      }
    }

    fetch(`URL/${id}`, options)
      .then(() => fetch('URL'))
      .then(response => response.json())
      .then(data => this.setState({ 
        key: data
      }))
      .catch(error => console.error(error));
  }
}

Async / Await

export const fetchData = async (url) => {
  const response = await fetch()
  if (response.ok) {
    const data = await response.json()
  } else {
    throw Error(response.statusText)
  }
}

propTypes

import PropTypes from 'prop-types';


Component.propTypes = {
  title: PropTypes.string.isRequired
}

https://reactjs.org/docs/typechecking-with-proptypes.html#react.proptypes

Spread An Array

ideas: [...this.state.key, newVariableElement]

Favorite

const Component = ({ isFavorite }) => {
  const favoriteClass = isFavorite ? 'favorite' : 'card'

  return (
    <section className={favoriteClass}>
    </section>
  )
}

Extras

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

https://github.com/turingschool-examples/javascript/blob/master/linters/module-3/linter-setup.md

  1. In terminal, npm i eslint-config-airbnb -D
  2. In package.json,
// update the scripts and eslintConfig portions of your package.json to match below
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "lint": "eslint src/**/*.js --ignore-pattern node_modules/ --ignore-pattern src/serviceWorker.js"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "airbnb"
    ]
  },
  1. In terminal eslint src/**/*.js --ignore-pattern node_modules/ --ignore-pattern src/serviceWorker.js
  2. If want to fix, eslint src --fix

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. Remeber 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;

Any time .toHaveBeenCalledWIth() is called it needs to be used with a mock function so it can be spied on.

Background Image in CSS

body {
  background-image: url('../images/bright-colors-daylight-2742812.jpg');
  background-size: cover;
  background-position: 50% 30%;
  background-repeat: no-repeat;
  height: 100vh;
  width: 100%;
  font-family: $heading-font;
  margin: 0;
}

Dont Test Certain Things

package.json

"jest": {
    "snapshotSerializers": [
      "enzyme-to-json/serializer"
    ],
    "collectCoverageFrom": [
      "src/**/{!(serviceWorker),}.js",
      "!src/index.js",
      "!src/reducers/index.js"
    ]
  }

How To Pass Match

In App

<Route exact path="/shuttle/:trail" render={(match) => <ShuttleForm match={match}/>} />

In Component

<Link to={`/shuttle/${name}`}><button className="Trail-btn" id={name}>BOOK</button></Link>

How To Disable Button/check for values in inputs

this.state = {
      disabled: true
    };
  formReady = () => {
    this.setState({ disabled: true })
    if (this.state.name !== '' && this.state.ingredients !== []) {
      this.setState({ disabled: false })
    } else {
      this.setState({ disabled: true })
    }
  }
<button disabled={this.state.disabled} onClick={e => this.handleSubmit(e)}>

error message

<p>{ this.state.disabled ? 'PLEASE FILL OUT NAME' : null }</p>

How To Console Log In A Render

{console.log(this.props.match)}

Getting Your Site on GitHub Pages

Preparation

  1. Checkout your master branch and double-check to make sure it is up-to-date and there is nothing needed to be committed at this point
  2. Navigate to the root of your project directory
  3. Open your project in your text editor
  4. Double-check that all image tags in your HTML have the src attribute have ./ to start the path, e.g src="./images/turing-logo.png"

Required Steps

In the package.json file, within the "repository" object, edit the "url" value to be "git+https://github.com/USERNAME/REPONAME.git" where USERNAME and REPONAME are replaced with your GitHub username and your repository name, respectively

OR

In the package.json file, add the line: "homepage": "http://USERNAME.github.io/REPONAME", where USERNAME and REPONAME are replaced with your GitHub username and your repository name, respectively

OR Just "homepage": ".",

  1. Add these two lines to the scripts section of the package.json file:
"predeploy": "npm run build",
"deploy": "gh-pages -d build"
  1. In the terminal, run npm install --save-dev gh-pages
  2. You should see these lines of JSON added to your package.json file:
"devDependencies": {
"gh-pages": "^1.1.0"
}
  1. Run npm run build in the command line
  2. Run npm run deploy in the command line

All of this will create a gh-pages branch in your repo with the contents from the build directory.

If you go to the GitHub pages site (http://USERNAME.github.io/REPONAME) in a minute, you should see your app! If not, check out the console to see what errors you're getting and troubleshoot from there.

Deploying with React Router

  1. npm install --save gh-pages
  2. “homepage”: “.“,
  3. “predeploy”: “npm run build”, “deploy”: “gh-pages -d build”
  4. import { HashRouter as Router } from ‘react-router-dom’ in index.js file and change to just <Router></Router>
  5. run npm run and build commands, done, go check GitHub Pages.

When You Make New Changes

If you make new changes to your master branch, GitHub Pages doesn't automatically know about these changes, and your site won't be up-to-date. You need to update the gh-pages branch to see those changes live on GitHub Pages. Here is how to update and keep everything in sync:

  1. After you're done making changes, checkout your master branch and double-check to make sure it is up-to-date and there is nothing needed to be committed at this point
  2. Run npm run build in the command line
  3. Run npm run deploy in the command line

#Postman

body, raw, json

Original Sources

React I: The What and The Why https://frontend.turing.io/lessons/module-3/react-i.html

React II: The How, building IdeaBox https://frontend.turing.io/lessons/module-3/react-ii.html

React III: Workshop Ideabox With A Backend https://frontend.turing.io/lessons/module-3/react-iii.html

Unit Testing React Components https://frontend.turing.io/lessons/module-3/unit-testing-react.html

Get Your Site On GH Pages https://github.com/turingschool-examples/webpack-starter-kit/blob/master/gh-pages-procedure.md

Webpack Starter Kit https://github.com/turingschool-examples/webpack-starter-kit

Network Request GET/POST Requests https://frontend.turing.io/lessons/module-3/network-request-exercises.html

All Lessons https://frontend.turing.io/lessons/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment