...
 
Commits (9)
import React from 'react';
import ReactDOM from 'react-dom';
import registerServiceWorker from './registerServiceWorker';
import React from "react";
import ReactDOM from "react-dom";
import registerServiceWorker from "./registerServiceWorker";
export default class MyComponent extends React.Component {
constructor() {
super();
// Set up the state of this component in the following
// structure:
// - count should default to 0
// - obj should be an object, with:
// - age set to 9
// - favoriteNumbers set to an array: [4, 8, 15, 16, 23, 42]
// - id set to "MyObj"
// - updated set to false
// - set as the array [1, 2, 3]
// Re-bind the following three functions explicitly
// to the context this:
// - addToSet
// - increment
// - updateObj
this.state = {
count: 0,
obj: {
age: 9,
favoriteNumbers: [4, 8, 15, 16, 23, 42],
id: "MyObj",
updated: false
},
set: [1, 2, 3]
};
this.addToSet = this.addToSet.bind(this);
this.increment = this.increment.bind(this);
this.updateObj = this.updateObj.bind(this);
}
// Increment the count value from state,
// and set it as the new value of count.
increment() {
this.setState({ count: this.state.count + 1 });
}
// Update the implementation of updateObj
// to ensure that the reference of obj in state
// is updated.
updateObj() {
const { obj } = this.state;
obj.age += 1;
obj.favoriteNumbers.push(69);
obj.updated = true;
const newObj = Object.assign({}, obj, {
age: obj.age + 1,
favoriteNumbers: [].concat(obj.favoriteNumbers, 42),
updated: true
});
this.setState({ obj: obj });
this.setState({ obj: newObj });
}
// Update the implementation of addToSet
// to ensure that the reference of the array in state
// is updated.
addToSet() {
const { set } = this.state;
set.push(45);
this.setState({ set: set });
this.setState({ set: [].concat(set, 45) });
}
render() {
// Pull the following variables out of state:
// - count
// - inside of obj:
// - age
// - favoriteNumbers
// - id
// - updated
// - set
// - Add id from state inside of h2
// - Add "My favorite numbers: " with the array myFavoriteNumbers
// (joined by a comma and a space) to h3
// - Add "Set: " with the array set (joined by a comma and a space)
// to h4
// - Add a button with the onClick property `this.increment`.
// Give it the className "increment". You can choose the text,
// but you might prefer something like "Add 1 to " and the count
// variable.
// - Add a button with the onClick property `this.updateObj`.
// Give it the className "updateObj". You can choose the text,
// but you might prefer something like "Update object".
// - Add a button with the onClick property `this.addToSet`.
// Give it the className "addToSet". You can choose the text,
// but you might prefer something like "Add to set".
const {
count,
obj: { age, favoriteNumbers, id, updated },
set
} = this.state;
return (
<div>
<h2>{id}</h2>
<h3>My favorite numbers: {favoriteNumbers.join(', ')}</h3>
<h4>Set: {set.join(', ')}</h4>
<h3>My favorite numbers: {favoriteNumbers.join(", ")}</h3>
<h4>Set: {set.join(", ")}</h4>
<button onClick={this.increment} className="increment">
Add 1 to {count}
</button>
......@@ -90,10 +65,12 @@ export default class MyComponent extends React.Component {
Add to set
</button>
</div>
)
);
}
}
const root = document.getElementById('root');
if ( root ) { ReactDOM.render(<MyComponent />, root); }
const root = document.getElementById("root");
if (root) {
ReactDOM.render(<MyComponent />, root);
}
registerServiceWorker();
import React from 'react';
import ReactDOM from 'react-dom';
import React from "react";
import ReactDOM from "react-dom";
var TEXT_HERE, FUNCTION_HERE;
class MyFirstComponent extends React.Component {
constructor() {
super()
super();
this.state = {
ping: null,
hello: 'world'
hello: "world"
};
this.clickedButton = this.clickedButton.bind(this);
}
clickedButton() {
this.setState({ ping: 'pong' })
this.setState({ ping: "pong" });
}
render() {
......@@ -22,7 +22,7 @@ class MyFirstComponent extends React.Component {
return (
<div>
<h1>Hello!</h1>
<h1>{spanText}</h1>
<button onClick={this.clickedButton}>Click me</button>
</div>
);
......@@ -30,6 +30,8 @@ class MyFirstComponent extends React.Component {
}
const root = document.getElementById("root");
if (root) { ReactDOM.render(<MyFirstComponent />, root); }
if (root) {
ReactDOM.render(<MyFirstComponent />, root);
}
export default MyFirstComponent;
import React from 'react';
import ReactDOM from 'react-dom';
import { shallow } from 'enzyme';
import React from "react";
import ReactDOM from "react-dom";
import { shallow } from "enzyme";
import MyFirstComponent from './index';
import MyFirstComponent from "./index";
it('can do equality checks', () => {
// Tests are written in the format expect(thing)
// with a "check" appended to the end.
// For instance, we might want to verify that 1 + 1 = 2.
expect(1 + 1).toEqual(2)
// We can also do "equality" checks with .toEqual.
expect(1 == 1).toEqual(true)
// Ultimately, we want to simplify _what_ we're checking
// in .toEqual. A complicated statement inside of expect
// should only be used to ensure that the `.toEqual` value
// is fairly simple.
it("can do equality checks", () => {
expect(1 + 1).toEqual(2);
expect(1 == 1).toEqual(true);
var array = [1, 2, 3];
expect(Math.floor(array[0] * 3 + 10 - 4 * 0.5)).toEqual(11)
})
it('renders without crashing', () => {
const div = document.createElement('div');
expect(Math.floor(array[0] * 3 + 10 - 4 * 0.5)).toEqual(11);
});
// ReactDOM.render takes a component and an
// "element" to render the component onto a
// webpage. What component are we testing in
// this test? The import above might give you
// give you a hint. The element that the component
// will be rendered into has been defined above.
//
// ReactDOM.render(component, element)
//
// Replace the below line with your test.
expect(false).toEqual(true)
it("renders without crashing", () => {
const div = document.createElement("div");
ReactDOM.render(<MyFirstComponent />, div);
});
it('has initial state set', () => {
it("has initial state set", () => {
const wrapper = shallow(<MyFirstComponent />);
// Hint: the state of a component can be referenced
// from `<component>.state(key)`
//
// wrapper.state(key)
//
// What would you expect the value of the key 'hello' in
// state `.toEqual`?
//
// Replace the below line with your test.
expect(false).toEqual(true)
expect(wrapper.state("hello")).toEqual("world");
});
it('displays the text variable in the <span>', () => {
it("displays the text variable in the <span>", () => {
const wrapper = shallow(<MyFirstComponent />);
// Hint: you can inspect values in a component
// (like text) using `.contains`.
//
// Try `.contains` with text you know is in the component.
// `.contains` returns a boolean - what would you expect
// it `.toEqual` for text that is found on the page?
//
// wrapper.contains(string)
//
// Replace the below line with your test.
expect(false).toEqual(true)
expect(wrapper.contains("Hello!")).toEqual(true);
});
it('clicks the button and updates state', () => {
it("clicks the button and updates state", () => {
const wrapper = shallow(<MyFirstComponent />);
const button = wrapper.find('button')
// Clicking a button in the UI is done with `.simulate`:
// in this case, we want to simulate click-this can be provided
// as the string 'click'.
//
// button.simulate(event)
//
// Once you've done this, what would you expect to change?
// Is there a value that clicking the button in our component
// changes? What would you expect it `.toEqual`?
//
// Replace the below line with your test.
expect(false).toEqual(true)
const button = wrapper.find("button");
expect(wrapper.state(""), null);
button.simulate("click");
expect(wrapper.state("ping"), "pong");
});
......@@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"devDependencies": {
"react-scripts": "1.0.11"
"react-scripts": "^2.1.3"
},
"dependencies": {
"react": "^15.6.1",
......@@ -14,5 +14,11 @@
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}
import React from 'react';
import ReactDOM from 'react-dom';
import registerServiceWorker from './registerServiceWorker';
import './bootstrap.css';
import React from "react";
import ReactDOM from "react-dom";
import registerServiceWorker from "./registerServiceWorker";
import "./bootstrap.css";
class MyHTMLComponent extends React.Component {
render() {
return (
<div />
)
<div>
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<a className="navbar-brand">Navbar</a>
<div className="navbar-nav">
<li className="nav-item active">
<a className="nav-link">Home</a>
</li>
<li className="nav-item">
<a className="nav-link">About</a>
</li>
</div>
</nav>
<div className="container">
<h1>My simple React component</h1>
<p className="lead">
This is my simple React component! It has a navbar and a couple
other things.
</p>
<p>How neat!</p>
</div>
</div>
);
}
}
ReactDOM.render(<MyHTMLComponent />, document.getElementById('root'));
ReactDOM.render(<MyHTMLComponent />, document.getElementById("root"));
registerServiceWorker();
This diff is collapsed.
......@@ -8,6 +8,7 @@
"dependencies": {
"chai": "^4.1.1",
"chai-enzyme": "^0.8.0",
"classnames": "^2.2.6",
"enzyme": "^2.9.1",
"react": "^15.6.1",
"react-dom": "^15.6.1",
......
import React, { Component } from 'react';
import classnames from 'classnames';
import './App.css';
import React, { Component } from "react";
import classnames from "classnames";
import "./App.css";
class App extends Component {
render() {
const color = "red";
/*
* Define a variable that calls the classnames
* function with the following classes:
* - 'header'
* - 'text-red', if color === 'red'
*
* Pass this variable as the class of the h1 tag below.
*/
const h1Class = classnames({
header: true,
"text-red": color === "red"
});
/*
* Define a style object with the following keys and
* values:
* - 'fontFamily', set to 'sans-serif'
* - 'fontWeight', set to 'bold'
* - 'lineHeight', set to 1.5
* - 'color', set to the variable color
* Pass this style object as the style of the p tag below.
*/
const style = {
fontFamily: "sans-serif",
fontWeight: "bold",
lineHeight: 1.5,
color
};
/*
* Finally, fix the following classNames function below,
* so that the properties below are true:
* - bg-red is true
* - largePadding is true _if_ style.lineHeight is greater than 1
*/
const divClasses = classnames({
'bg-red': false,
largePadding: style.linHeight > 1
})
"bg-red": true,
largePadding: style.lineHeight > 1
});
return (
<div className={divClasses}>
<h1>Welcome to React</h1>
<p>
<h1 className={h1Class}>Welcome to React</h1>
<p style={style}>
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
......
......@@ -1387,6 +1387,11 @@ clap@^1.0.9:
dependencies:
chalk "^1.1.3"
classnames@^2.2.6:
version "2.2.6"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==
clean-css@4.1.x:
version "4.1.7"
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.7.tgz#b9aea4f85679889cf3eae8b40349ec4ebdfdd032"
......
This diff is collapsed.
import React from 'react';
import Loading from './Loading'
import 'whatwg-fetch';
import React from "react";
import Loading from "./Loading";
import "whatwg-fetch";
class Networked extends React.Component {
constructor() {
......@@ -8,31 +8,23 @@ class Networked extends React.Component {
this.state = {
loading: false,
string: 'n/a'
}
string: "n/a"
};
}
componentDidMount() {
this.setState({ loading: true });
// In this code from a previous exercise, we make a request
// on the network to a different route. Of all the code in this
// example, this is the most likely to "blow up". We'll come back and
// look at this again later.
var url = 'https://bytesized-training-assets.herokuapp.com/exercise.json';
var url = "https://bytesized-training-assets.herokuapp.com/exercise.json";
// If you've come back from index.js and are ready to simulate a failure,
// simply add a thrown JavaScript error at the beginning of componentDidMount.
// e.g. throw new Error('Something went wrong!')
//
// Now return to index.js.
throw new Error("Something went wrong!");
fetch(url).then(resp => {
resp.json().then(json => {
this.setState({
loading: false,
string: json.string
})
});
});
});
}
......@@ -44,4 +36,4 @@ class Networked extends React.Component {
}
}
export default Networked
export default Networked;
import React from 'react';
import ReactDOM from 'react-dom';
import Networked from './Networked'
import React from "react";
import ReactDOM from "react-dom";
import Networked from "./Networked";
// No changes are needed above this line.
// In a larger application, if a single component fails,
// it would be ideal to have the rest of the application
// not fall apart as well. By default, React's architecture
// doesn't easily handle graceful component failures.
//
// Wrap Networked in a div, and add a couple more components
// around it:
// 1) A h1 with the text "My application"
// 2) Add a few elements of your choice. You could add some inputs
// (it's ok to leave them uncontrolled for this exercise) or
// something else of your choice. Add two or three more elements
// before proceeding.
class Application extends React.Component {
render() {
return <Networked />
return (
<div>
<h1>My application</h1>
<h2>And a subtitle</h2>
<input type="text" placeholder="My test input" />
<ErrorBoundary>
<Networked />
</ErrorBoundary>
</div>
);
}
}
// Let's go look at Networked.js, and simulate a failure.
class ErrorBoundary extends React.Component {
constructor(args) {
super(args);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
this.setState({ hasError: true });
}
// Once you've thrown an error in the fetch code of Networked,
// we need to properly define an "error boundary" and handle the error.
//
// Define a new component, ErrorBoundary.
// ErrorBoundary should have state, with a hasError key defaulting to false.
//
// In order to define an error boundary, we need to implement two things:
// 1) A `componentDidCatch` function -- this is new React 16.3 functionality.
// The function takes two arguments, error, and info.
// Inside of the function, use this.setState to set hasError to true.
// 2) Also define a render function, which does the following:
// - If this.state.hasError, return an 'h1' with the text 'Something went wrong!'
// - Otherwise, return this.props.children.
//
// This ErrorBoundary component can be placed at any level of your application.
// It is, as the same says, a "boundary" to catch any errors from its children,
// and stop it from propagating up through the application.
//
// Let's wrap Networked inside of ErrorBoundary, making it a child of the new component.
// Now, when a failure occurs, we should expect the rest of the application to continue
// working, after safely wrapping a "brittle" or potentially error-raising component
// inside the ErrorBoundary.
render() {
return this.state.hasError ? (
<h1>Something went wrong!</h1>
) : (
this.props.children
);
}
}
var root = document.getElementById('root');
var root = document.getElementById("root");
if (root) {
ReactDOM.render(<Application />, root);
}
import React from 'react';
import ReactDOM from 'react-dom';
import React from "react";
import ReactDOM from "react-dom";
var TEXT_HERE, FUNCTION_HERE;
......@@ -7,29 +7,18 @@ var TEXT_HERE, FUNCTION_HERE;
class MyFirstComponent extends React.Component {
constructor() {
super()
/* Data is added to state in the format:
* key: 'string'
*
* Try adding the key "hello" with the value
* "world" to this.state.
*/
super();
this.state = {
ping: null,
hello: "world",
ping: null
};
/* This function can be referred to in the render
* function as this.clickedButton.
*/
this.clickedButton = this.clickedButton.bind(this);
}
clickedButton() {
/* This will change state.ping
* from null to 'pong'.
*/
this.setState({ ping: 'pong' })
this.setState({ ping: "pong" });
}
render() {
......@@ -39,16 +28,9 @@ class MyFirstComponent extends React.Component {
return (
<div>
<h1>Hello!</h1>
<span>{TEXT_HERE}</span>
<button onClick={FUNCTION_HERE}>Click me</button>
{/*
If `ping` from `state` is set, it will
appear in pink text here.
By the way, this is a JSX comment!
*/}
<span style={{color: "pink"}}>{ping}</span>
<span>{spanText}</span>
<button onClick={this.clickedButton}>Click me</button>
<span style={{ color: "pink" }}>{ping}</span>
</div>
);
}
......@@ -57,6 +39,8 @@ class MyFirstComponent extends React.Component {
// No changes are needed below this line.
const root = document.getElementById("root");
if (root) { ReactDOM.render(<MyFirstComponent />, root); }
if (root) {
ReactDOM.render(<MyFirstComponent />, root);
}
export default MyFirstComponent;
import React from 'react';
import ReactDOM from 'react-dom';
import registerServiceWorker from './registerServiceWorker';
import React from "react";
import ReactDOM from "react-dom";
import registerServiceWorker from "./registerServiceWorker";
import { Map } from "immutable";
import parser from "./parser";
// Let's add immutable.js to our project by running
// `yarn add immutable` in the command-line.
// The `Map` class from Immutable is defined as a named
// export in the package 'immutable' -- try importing
// it below.
// The local module `parser.js` has a `parser` function
// that we call below, in the render function. Go to
// `parser.js` and fix the module export definition
// so that it can be imported in the below line.
// This component should be exported. If we want
// to import it in other files as `import MyComponent`,
// how should the export function be declared?
// Note that you can add export to the beginning of the line
// below, or you can specify a separate export line after the
// class definition.
class MyComponent extends React.Component {
render() {
const simpleMap = Map({ key: "value" });
return (
<div>
{parser(simpleMap.get("key"))}
</div>
)
return <div>{parser(simpleMap.get("key"))}</div>;
}
}
const root = document.getElementById('root');
if (root) { ReactDOM.render(<App />, root); }
export default MyComponent;
const root = document.getElementById("root");
if (root) {
ReactDOM.render(<App />, root);
}
registerServiceWorker();
// Export this function so it can be imported
// in a different file in the following format:
// import parser from './parser';
const parser = (string) => {
const parser = string => {
if (string === "value") {
return "Hello, world!";
} else {
return "Not sure what to do.";
}
}
};
export default parser;
import React from 'react';
import ReactDOM from 'react-dom';
import React from "react";
import ReactDOM from "react-dom";
import omitProps from './omitProps'
// Import the PropsOmitted component from './PropsOmitted'
import PropsOmitted from "./PropsOmitted";
class Basic extends React.Component {
render() {
// Replace the code inside of the return part of render
// with PropsOmitted, using render props-style:
// 1) Pass a prop 'omitted', which is an array. It should have
// one item: 'dataRetrieved'.
// 2) Pass a render prop. It should be a function with one argument,
// props, that returns:
// - An h1 with the text of your 'class' prop
// - An h2 with the text of your 'exercise' prop
// - An h3 with the text of your 'dataRetrieved' prop,
// rendered _if_ this.props.dataRetrieved exists.
//
// (Note that this is identical code to what exists now: you can
// simply put it inside the render function.)
// 3) Finally, make sure to pass _all_ of your props into PropsOmitted.
// You can do this with the spread operator:
// <SampleComponent {...this.props} />
return (
<div>
<h1>{this.props.class}</h1>
<h2>{this.props.exercise}</h2>
<h3>{this.props.dataRetrieved && 'Data loaded!'}</h3>
</div>
<PropsOmitted
omitted={["dataRetrieved"]}
render={props => (
<div>
<h1>{props.class}</h1>
<h2>{props.exercise}</h2>
<h3>{props.dataRetrieved && "Data loaded!"}</h3>
</div>
)}
{...this.props}
/>
);
}
}
// Replace this export with a simple default export of the Basic component.
export default omitProps(Basic, ['dataRetrieved']);
export default Basic;
import React from 'react';
import ReactDOM from 'react-dom';
import React from "react";
import ReactDOM from "react-dom";
// Import the lodash package as '_'.
import _ from "lodash";
class PropsOmitted extends React.Component {
render() {
// The PropsOmitted component should call the render function inside of props:
// 1) Use _.omit, with the arguments this.props and this.props.omitted,
// to create a prop variable that contains all the props for this component
// MINUS those we've specified should be omitted.
// 2) Pass this variable as the argument to the render prop.
return null;
const props = _.omit(this.props, this.props.omitted);
return this.props.render(props);
}
}
// Export PropsOmitted as the default export for this module.
export default PropsOmitted;
default:
yarn run start
import React, { Component } from "react";
import React, { Component, useState, useEffect } from "react";
import logo from "./logo.svg";
import "./App.css";
// Welcome to React hooks!
/*
A few things need to happen in this example:
1. The App component should be updated from a "class" component
to a class-less, function-based component.
From:
class MyComponent extends Component {
render() {
return <h1>Hello!</h1>
}
}
to:
const MyComponent = () => {
return <h1>Hello!</h1>
}
Instead of copy-pasting the class code into the new function,
try writing it side-by-side so you can start with a clean slate.
The function should return the current content of the render function
in our App class. Rename the old class component to "OldApp" so that
you can ensure you're using your new function component.
2. Next, you should import the two "hooks" we'll use in our example,
`useState` and `useEffect`. These are "named" imports from inside
the React package. How would you import them, similarly to how
"Component" is imported now?
3. Inside our new App function, before returning HTML, we should
use the `useState` hook to set an initial state for `count`, and get
a `setState` function we can use in our component. For instance, if we
wanted to track `num`:
const [num, setNum] = useState(defaultValue)
Do something similar, but instead of `num`, use `count`. The default
value should be 0.
4. Replace the usage of our old `this.state.count` value inside of the render
code. `this.state.count` should just become `count`, and `increment` should
be replaced with a call to `setCount`, with an argument of `count + 1`.
5. Finally, let's use `useEffect` to safely handle a "side-effect": something
that doesn't perfectly fit inside of React's functional code semantics.
In our class component, in the `componentDidUpdate` function, we check
for a change in `this.state.count`: if one exists, we update document.title.
Let's do something similar with `useEffect`, which takes one argument -
a function to perform when a change occurs.
useEffect(() => {
doSomethingHere
})
After you set up `count` and `setCount`, use `useEffect` to set the
document.title to the value of `count` inside of `useEffect`.
6. To confirm everything works, open up the application in your browser
and confirm the following still works:
- The "count" starts at 0
- Clicking the increment button increments the count (from 0 to 1, 1 to 2, etc)
- The document title (the title of the page in your browser) tracks count's value
*/
class App extends Component {
constructor() {
super();
this.state = { count: 0 };
}
componentDidMount() {
document.title = this.state.count;
}
componentDidUpdate(_prevProps, prevState) {
if (prevState.count !== this.state.count) {
document.title = this.state.count;
}
}
increment = () => this.setState({ count: this.state.count + 1 });
render() {
return (
<div className="App">
<header className="App-header">
<p>Welcome to React Hooks</p>
<p>The count is {this.state.count}</p>
<button onClick={this.increment}>Increment!</button>
</header>
</div>
);
}
}
const App = () => {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = count
})
const increment = () => setCount(count + 1)
return (
<div className="App">
<header className="App-header">
<p>Welcome to React Hooks</p>
<p>The count is {count}</p>
<button onClick={increment}>Increment!</button>
</header>
</div>
);
};
export default App;