feat: add 'back/front end' in curriculum (#42596)
* chore: rename APIs and Microservices to include "Backend" (#42515) * fix typo * fix typo * undo change * Corrected grammar mistake Corrected a grammar mistake by removing a comma. * change APIs and Microservices cert title * update title * Change APIs and Microservices certi title * Update translations.json * update title * feat(curriculum): rename apis and microservices cert * rename folder structure * rename certificate * rename learn Markdown * apis-and-microservices -> back-end-development-and-apis * update backend meta * update i18n langs and cypress test Co-authored-by: Shaun Hamilton <shauhami020@gmail.com> * fix: add development to front-end libraries (#42512) * fix: added-the-word-Development-to-front-end-libraries * fix/added-the-word-Development-to-front-end-libraries * fix/added-word-development-to-front-end-libraries-in-other-related-files * fix/added-the-word-Development-to-front-end-and-all-related-files * fix/removed-typos-from-last-commit-in-index.md * fix/reverted-changes-that-i-made-to-dependecies * fix/removed xvfg * fix/reverted changes that i made to package.json * remove unwanted changes * front-end-development-libraries changes * rename backend certSlug and README * update i18n folder names and keys * test: add legacy path redirect tests This uses serve.json from the client-config repo, since we currently use that in production * fix: create public dir before moving serve.json * fix: add missing script * refactor: collect redirect tests * test: convert to cy.location for stricter tests * rename certificate folder to 00-certificates * change crowdin config to recognise new certificates location * allow translations to be used Co-authored-by: Nicholas Carrigan (he/him) <nhcarrigan@gmail.com> * add forwards slashes to path redirects * fix cypress path tests again * plese cypress * fix: test different challenge Okay so I literally have no idea why this one particular challenge fails in Cypress Firefox ONLY. Tom and I paired and spun a full build instance and confirmed in Firefox the page loads and redirects as expected. Changing to another bootstrap challenge passes Cypress firefox locally. Absolutely boggled by this. AAAAAAAAAAAAAAA * fix: separate the test Okay apparently the test does not work unless we separate it into a different `it` statement. >:( >:( >:( >:( Co-authored-by: Sujal Gupta <55016909+heysujal@users.noreply.github.com> Co-authored-by: Noor Fakhry <65724923+NoorFakhry@users.noreply.github.com> Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Co-authored-by: Nicholas Carrigan (he/him) <nhcarrigan@gmail.com>
This commit is contained in:
@ -0,0 +1,165 @@
|
||||
---
|
||||
id: 5a24c314108439a4d403616e
|
||||
title: Access Props Using this.props
|
||||
challengeType: 6
|
||||
forumTopicId: 301375
|
||||
dashedName: access-props-using-this-props
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The last several challenges covered the basic ways to pass props to child components. But what if the child component that you're passing a prop to is an ES6 class component, rather than a stateless functional component? The ES6 class component uses a slightly different convention to access props.
|
||||
|
||||
Anytime you refer to a class component within itself, you use the `this` keyword. To access props within a class component, you preface the code that you use to access it with `this`. For example, if an ES6 class component has a prop called `data`, you write `{this.props.data}` in JSX.
|
||||
|
||||
# --instructions--
|
||||
|
||||
Render an instance of the `ReturnTempPassword` component in the parent component `ResetPassword`. Here, give `ReturnTempPassword` a prop of `tempPassword` and assign it a value of a string that is at least 8 characters long. Within the child, `ReturnTempPassword`, access the `tempPassword` prop within the `strong` tags to make sure the user sees the temporary password.
|
||||
|
||||
# --hints--
|
||||
|
||||
The `ResetPassword` component should return a single `div` element.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(ResetPassword));
|
||||
return mockedComponent.children().type() === 'div';
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The fourth child of `ResetPassword` should be the `ReturnTempPassword` component.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(ResetPassword));
|
||||
return (
|
||||
mockedComponent.children().childAt(3).name() === 'ReturnTempPassword'
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `ReturnTempPassword` component should have a prop called `tempPassword`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(ResetPassword));
|
||||
return mockedComponent.find('ReturnTempPassword').props().tempPassword;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `tempPassword` prop of `ReturnTempPassword` should be equal to a string of at least 8 characters.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(ResetPassword));
|
||||
const temp = mockedComponent.find('ReturnTempPassword').props()
|
||||
.tempPassword;
|
||||
return typeof temp === 'string' && temp.length >= 8;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `ReturnTempPassword` component should display the password you create as the `tempPassword` prop within `strong` tags.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(ResetPassword));
|
||||
return (
|
||||
mockedComponent.find('strong').text() ===
|
||||
mockedComponent.find('ReturnTempPassword').props().tempPassword
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<ResetPassword />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class ReturnTempPassword extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{ /* Change code below this line */ }
|
||||
<p>Your temporary password is: <strong></strong></p>
|
||||
{ /* Change code above this line */ }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
class ResetPassword extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h2>Reset Password</h2>
|
||||
<h3>We've generated a new temporary password for you.</h3>
|
||||
<h3>Please reset this password from your account settings ASAP.</h3>
|
||||
{ /* Change code below this line */ }
|
||||
|
||||
{ /* Change code above this line */ }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class ReturnTempPassword extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<p>Your temporary password is: <strong>{this.props.tempPassword}</strong></p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
class ResetPassword extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h2>Reset Password</h2>
|
||||
<h3>We've generated a new temporary password for you.</h3>
|
||||
<h3>Please reset this password from your account settings ASAP.</h3>
|
||||
{ /* Change code below this line */ }
|
||||
<ReturnTempPassword tempPassword="serrPbqrPnzc" />
|
||||
{ /* Change code above this line */ }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,82 @@
|
||||
---
|
||||
id: 5a24bbe0dba28a8d3cbd4c5e
|
||||
title: Add Comments in JSX
|
||||
challengeType: 6
|
||||
forumTopicId: 301376
|
||||
dashedName: add-comments-in-jsx
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
JSX is a syntax that gets compiled into valid JavaScript. Sometimes, for readability, you might need to add comments to your code. Like most programming languages, JSX has its own way to do this.
|
||||
|
||||
To put comments inside JSX, you use the syntax `{/* */}` to wrap around the comment text.
|
||||
|
||||
# --instructions--
|
||||
|
||||
The code editor has a JSX element similar to what you created in the last challenge. Add a comment somewhere within the provided `div` element, without modifying the existing `h1` or `p` elements.
|
||||
|
||||
# --hints--
|
||||
|
||||
The constant `JSX` should return a `div` element.
|
||||
|
||||
```js
|
||||
assert(JSX.type === 'div');
|
||||
```
|
||||
|
||||
The `div` should contain an `h1` tag as the first element.
|
||||
|
||||
```js
|
||||
assert(JSX.props.children[0].type === 'h1');
|
||||
```
|
||||
|
||||
The `div` should contain a `p` tag as the second element.
|
||||
|
||||
```js
|
||||
assert(JSX.props.children[1].type === 'p');
|
||||
```
|
||||
|
||||
The existing `h1` and `p` elements should not be modified.
|
||||
|
||||
```js
|
||||
assert(
|
||||
JSX.props.children[0].props.children === 'This is a block of JSX' &&
|
||||
JSX.props.children[1].props.children === "Here's a subtitle"
|
||||
);
|
||||
```
|
||||
|
||||
The `JSX` should use valid comment syntax.
|
||||
|
||||
```js
|
||||
assert(/<div>[\s\S]*{\s*\/\*[\s\S]*\*\/\s*}[\s\S]*<\/div>/.test(code));
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(JSX, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
const JSX = (
|
||||
<div>
|
||||
<h1>This is a block of JSX</h1>
|
||||
<p>Here's a subtitle</p>
|
||||
</div>
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
const JSX = (
|
||||
<div>
|
||||
<h1>This is a block of JSX</h1>
|
||||
{ /* this is a JSX comment */ }
|
||||
<p>Here's a subtitle</p>
|
||||
</div>);
|
||||
```
|
@ -0,0 +1,179 @@
|
||||
---
|
||||
id: 5a24c314108439a4d403617e
|
||||
title: Add Event Listeners
|
||||
challengeType: 6
|
||||
forumTopicId: 301377
|
||||
dashedName: add-event-listeners
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The `componentDidMount()` method is also the best place to attach any event listeners you need to add for specific functionality. React provides a synthetic event system which wraps the native event system present in browsers. This means that the synthetic event system behaves exactly the same regardless of the user's browser - even if the native events may behave differently between different browsers.
|
||||
|
||||
You've already been using some of these synthetic event handlers such as `onClick()`. React's synthetic event system is great to use for most interactions you'll manage on DOM elements. However, if you want to attach an event handler to the document or window objects, you have to do this directly.
|
||||
|
||||
# --instructions--
|
||||
|
||||
Attach an event listener in the `componentDidMount()` method for `keydown` events and have these events trigger the callback `handleKeyPress()`. You can use `document.addEventListener()` which takes the event (in quotes) as the first argument and the callback as the second argument.
|
||||
|
||||
Then, in `componentWillUnmount()`, remove this same event listener. You can pass the same arguments to `document.removeEventListener()`. It's good practice to use this lifecycle method to do any clean up on React components before they are unmounted and destroyed. Removing event listeners is an example of one such clean up action.
|
||||
|
||||
# --hints--
|
||||
|
||||
`MyComponent` should render a `div` element which wraps an `h1` tag.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
return mockedComponent.find('div').children().find('h1').length === 1;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
A `keydown` listener should be attached to the document in `componentDidMount`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
const didMountString = mockedComponent
|
||||
.instance()
|
||||
.componentDidMount.toString();
|
||||
return new RegExp(
|
||||
'document.addEventListener(.|\n|\r)+keydown(.|\n|\r)+this.handleKeyPress'
|
||||
).test(didMountString);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `keydown` listener should be removed from the document in `componentWillUnmount`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
const willUnmountString = mockedComponent
|
||||
.instance()
|
||||
.componentWillUnmount.toString();
|
||||
return new RegExp(
|
||||
'document.removeEventListener(.|\n|\r)+keydown(.|\n|\r)+this.handleKeyPress'
|
||||
).test(willUnmountString);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
Once the component has mounted, pressing `enter` should update its state and the rendered `h1` tag.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const waitForIt = (fn) =>
|
||||
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
const beforeState = mockedComponent.state('message');
|
||||
const beforeText = mockedComponent.find('h1').text();
|
||||
const pressEnterKey = () => {
|
||||
mockedComponent.instance().handleKeyPress({ keyCode: 13 });
|
||||
return waitForIt(() => {
|
||||
mockedComponent.update();
|
||||
return {
|
||||
state: mockedComponent.state('message'),
|
||||
text: mockedComponent.find('h1').text()
|
||||
};
|
||||
});
|
||||
};
|
||||
const afterKeyPress = await pressEnterKey();
|
||||
assert(
|
||||
beforeState !== afterKeyPress.state && beforeText !== afterKeyPress.text
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<MyComponent />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
message: ''
|
||||
};
|
||||
this.handleEnter = this.handleEnter.bind(this);
|
||||
this.handleKeyPress = this.handleKeyPress.bind(this);
|
||||
}
|
||||
// Change code below this line
|
||||
componentDidMount() {
|
||||
|
||||
}
|
||||
componentWillUnmount() {
|
||||
|
||||
}
|
||||
// Change code above this line
|
||||
handleEnter() {
|
||||
this.setState((state) => ({
|
||||
message: state.message + 'You pressed the enter key! '
|
||||
}));
|
||||
}
|
||||
handleKeyPress(event) {
|
||||
if (event.keyCode === 13) {
|
||||
this.handleEnter();
|
||||
}
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>{this.state.message}</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
message: ''
|
||||
};
|
||||
this.handleKeyPress = this.handleKeyPress.bind(this);
|
||||
this.handleEnter = this.handleEnter.bind(this); }
|
||||
componentDidMount() {
|
||||
// Change code below this line
|
||||
document.addEventListener('keydown', this.handleKeyPress);
|
||||
// Change code above this line
|
||||
}
|
||||
componentWillUnmount() {
|
||||
// Change code below this line
|
||||
document.removeEventListener('keydown', this.handleKeyPress);
|
||||
// Change code above this line
|
||||
}
|
||||
handleEnter() {
|
||||
this.setState((state) => ({
|
||||
message: state.message + 'You pressed the enter key! '
|
||||
}));
|
||||
}
|
||||
handleKeyPress(event) {
|
||||
if (event.keyCode === 13) {
|
||||
this.handleEnter();
|
||||
}
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>{this.state.message}</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,112 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036182
|
||||
title: Add Inline Styles in React
|
||||
challengeType: 6
|
||||
forumTopicId: 301378
|
||||
dashedName: add-inline-styles-in-react
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
You may have noticed in the last challenge that there were several other syntax differences from HTML inline styles in addition to the `style` attribute set to a JavaScript object. First, the names of certain CSS style properties use camel case. For example, the last challenge set the size of the font with `fontSize` instead of `font-size`. Hyphenated words like `font-size` are invalid syntax for JavaScript object properties, so React uses camel case. As a rule, any hyphenated style properties are written using camel case in JSX.
|
||||
|
||||
All property value length units (like `height`, `width`, and `fontSize`) are assumed to be in `px` unless otherwise specified. If you want to use `em`, for example, you wrap the value and the units in quotes, like `{fontSize: "4em"}`. Other than the length values that default to `px`, all other property values should be wrapped in quotes.
|
||||
|
||||
# --instructions--
|
||||
|
||||
If you have a large set of styles, you can assign a style `object` to a constant to keep your code organized. Declare your styles constant as a global variable at the top of the file. Initialize `styles` constant and assign an `object` with three style properties and their values to it. Give the `div` a color of `purple`, a font-size of `40`, and a border of `2px solid purple`. Then set the `style` attribute equal to the `styles` constant.
|
||||
|
||||
# --hints--
|
||||
|
||||
The `styles` variable should be an `object` with three properties.
|
||||
|
||||
```js
|
||||
assert(Object.keys(styles).length === 3);
|
||||
```
|
||||
|
||||
The `styles` variable should have a `color` property set to a value of `purple`.
|
||||
|
||||
```js
|
||||
assert(styles.color === 'purple');
|
||||
```
|
||||
|
||||
The `styles` variable should have a `fontSize` property set to a value of `40`.
|
||||
|
||||
```js
|
||||
assert(styles.fontSize === 40);
|
||||
```
|
||||
|
||||
The `styles` variable should have a `border` property set to a value of `2px solid purple`.
|
||||
|
||||
```js
|
||||
assert(styles.border === '2px solid purple');
|
||||
```
|
||||
|
||||
The component should render a `div` element.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.shallow(React.createElement(Colorful));
|
||||
return mockedComponent.type() === 'div';
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `div` element should have its styles defined by the `styles` object.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.shallow(React.createElement(Colorful));
|
||||
return (
|
||||
mockedComponent.props().style.color === 'purple' &&
|
||||
mockedComponent.props().style.fontSize === 40 &&
|
||||
mockedComponent.props().style.border === '2px solid purple'
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<Colorful />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
// Change code above this line
|
||||
class Colorful extends React.Component {
|
||||
render() {
|
||||
// Change code below this line
|
||||
return (
|
||||
<div style={{color: "yellow", fontSize: 24}}>Style Me!</div>
|
||||
);
|
||||
// Change code above this line
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
const styles = {
|
||||
color: "purple",
|
||||
fontSize: 40,
|
||||
border: "2px solid purple"
|
||||
};
|
||||
// Change code above this line
|
||||
class Colorful extends React.Component {
|
||||
render() {
|
||||
// Change code below this line
|
||||
return (
|
||||
<div style={styles}>Style Me!</div>
|
||||
);
|
||||
// Change code above this line
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,136 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036174
|
||||
title: Bind 'this' to a Class Method
|
||||
challengeType: 6
|
||||
forumTopicId: 301379
|
||||
dashedName: bind-this-to-a-class-method
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
In addition to setting and updating `state`, you can also define methods for your component class. A class method typically needs to use the `this` keyword so it can access properties on the class (such as `state` and `props`) inside the scope of the method. There are a few ways to allow your class methods to access `this`.
|
||||
|
||||
One common way is to explicitly bind `this` in the constructor so `this` becomes bound to the class methods when the component is initialized. You may have noticed the last challenge used `this.handleClick = this.handleClick.bind(this)` for its `handleClick` method in the constructor. Then, when you call a function like `this.setState()` within your class method, `this` refers to the class and will not be `undefined`.
|
||||
|
||||
**Note:** The `this` keyword is one of the most confusing aspects of JavaScript but it plays an important role in React. Although its behavior here is totally normal, these lessons aren't the place for an in-depth review of `this` so please refer to other lessons if the above is confusing!
|
||||
|
||||
# --instructions--
|
||||
|
||||
The code editor has a component with a `state` that keeps track of the text. It also has a method which allows you to set the text to `You clicked!`. However, the method doesn't work because it's using the `this` keyword that is undefined. Fix it by explicitly binding `this` to the `handleClick()` method in the component's constructor.
|
||||
|
||||
Next, add a click handler to the `button` element in the render method. It should trigger the `handleClick()` method when the button receives a click event. Remember that the method you pass to the `onClick` handler needs curly braces because it should be interpreted directly as JavaScript.
|
||||
|
||||
Once you complete the above steps you should be able to click the button and see `You clicked!`.
|
||||
|
||||
# --hints--
|
||||
|
||||
`MyComponent` should return a `div` element which wraps two elements, a button and an `h1` element, in that order.
|
||||
|
||||
```js
|
||||
assert(
|
||||
Enzyme.mount(React.createElement(MyComponent)).find('div').length === 1 &&
|
||||
Enzyme.mount(React.createElement(MyComponent))
|
||||
.find('div')
|
||||
.childAt(0)
|
||||
.type() === 'button' &&
|
||||
Enzyme.mount(React.createElement(MyComponent))
|
||||
.find('div')
|
||||
.childAt(1)
|
||||
.type() === 'h1'
|
||||
);
|
||||
```
|
||||
|
||||
The state of `MyComponent` should initialize with the key value pair `{ text: "Hello" }`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
Enzyme.mount(React.createElement(MyComponent)).state('text') === 'Hello'
|
||||
);
|
||||
```
|
||||
|
||||
Clicking the `button` element should run the `handleClick` method and set the state `text` to `You clicked!`.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const waitForIt = (fn) =>
|
||||
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
const first = () => {
|
||||
mockedComponent.setState({ text: 'Hello' });
|
||||
return waitForIt(() => mockedComponent.state('text'));
|
||||
};
|
||||
const second = () => {
|
||||
mockedComponent.find('button').simulate('click');
|
||||
return waitForIt(() => mockedComponent.state('text'));
|
||||
};
|
||||
const firstValue = await first();
|
||||
const secondValue = await second();
|
||||
assert(firstValue === 'Hello' && secondValue === 'You clicked!');
|
||||
};
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<MyComponent />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
text: "Hello"
|
||||
};
|
||||
// Change code below this line
|
||||
|
||||
// Change code above this line
|
||||
}
|
||||
handleClick() {
|
||||
this.setState({
|
||||
text: "You clicked!"
|
||||
});
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{ /* Change code below this line */ }
|
||||
<button>Click Me</button>
|
||||
{ /* Change code above this line */ }
|
||||
<h1>{this.state.text}</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
text: "Hello"
|
||||
};
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
}
|
||||
handleClick() {
|
||||
this.setState({
|
||||
text: "You clicked!"
|
||||
});
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<button onClick = {this.handleClick}>Click Me</button>
|
||||
<h1>{this.state.text}</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,176 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036189
|
||||
title: Change Inline CSS Conditionally Based on Component State
|
||||
challengeType: 6
|
||||
forumTopicId: 301380
|
||||
dashedName: change-inline-css-conditionally-based-on-component-state
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
At this point, you've seen several applications of conditional rendering and the use of inline styles. Here's one more example that combines both of these topics. You can also render CSS conditionally based on the state of a React component. To do this, you check for a condition, and if that condition is met, you modify the styles object that's assigned to the JSX elements in the render method.
|
||||
|
||||
This paradigm is important to understand because it is a dramatic shift from the more traditional approach of applying styles by modifying DOM elements directly (which is very common with jQuery, for example). In that approach, you must keep track of when elements change and also handle the actual manipulation directly. It can become difficult to keep track of changes, potentially making your UI unpredictable. When you set a style object based on a condition, you describe how the UI should look as a function of the application's state. There is a clear flow of information that only moves in one direction. This is the preferred method when writing applications with React.
|
||||
|
||||
# --instructions--
|
||||
|
||||
The code editor has a simple controlled input component with a styled border. You want to style this border red if the user types more than 15 characters of text in the input box. Add a condition to check for this and, if the condition is valid, set the input border style to `3px solid red`. You can try it out by entering text in the input.
|
||||
|
||||
# --hints--
|
||||
|
||||
The `GateKeeper` component should render a `div` element.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
|
||||
return mockedComponent.find('div').length === 1;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `GateKeeper` component should be initialized with a state key `input` set to an empty string.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
|
||||
return mockedComponent.state().input === '';
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `GateKeeper` component should render an `h3` tag and an `input` tag.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
|
||||
return (
|
||||
mockedComponent.find('h3').length === 1 &&
|
||||
mockedComponent.find('input').length === 1
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `input` tag should initially have a style of `1px solid black` for the `border` property.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
|
||||
return (
|
||||
mockedComponent.find('input').props().style.border === '1px solid black'
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `input` tag should be styled with a border of `3px solid red` if the input value in state is longer than 15 characters.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const waitForIt = (fn) =>
|
||||
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100));
|
||||
const mockedComponent = Enzyme.mount(React.createElement(GateKeeper));
|
||||
const simulateChange = (el, value) =>
|
||||
el.simulate('change', { target: { value } });
|
||||
let initialStyle = mockedComponent.find('input').props().style.border;
|
||||
const state_1 = () => {
|
||||
mockedComponent.setState({ input: 'this is 15 char' });
|
||||
return waitForIt(() => mockedComponent.find('input').props().style.border);
|
||||
};
|
||||
const state_2 = () => {
|
||||
mockedComponent.setState({
|
||||
input: 'A very long string longer than 15 characters.'
|
||||
});
|
||||
return waitForIt(() => mockedComponent.find('input').props().style.border);
|
||||
};
|
||||
const style_1 = await state_1();
|
||||
const style_2 = await state_2();
|
||||
assert(
|
||||
initialStyle === '1px solid black' &&
|
||||
style_1 === '1px solid black' &&
|
||||
style_2 === '3px solid red'
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<GateKeeper />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class GateKeeper extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
input: ''
|
||||
};
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
}
|
||||
handleChange(event) {
|
||||
this.setState({ input: event.target.value })
|
||||
}
|
||||
render() {
|
||||
let inputStyle = {
|
||||
border: '1px solid black'
|
||||
};
|
||||
// Change code below this line
|
||||
|
||||
// Change code above this line
|
||||
return (
|
||||
<div>
|
||||
<h3>Don't Type Too Much:</h3>
|
||||
<input
|
||||
type="text"
|
||||
style={inputStyle}
|
||||
value={this.state.input}
|
||||
onChange={this.handleChange} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class GateKeeper extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
input: ''
|
||||
};
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
}
|
||||
handleChange(event) {
|
||||
this.setState({ input: event.target.value })
|
||||
}
|
||||
render() {
|
||||
let inputStyle = {
|
||||
border: '1px solid black'
|
||||
};
|
||||
if (this.state.input.length > 15) {
|
||||
inputStyle.border = '3px solid red';
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<h3>Don't Type Too Much:</h3>
|
||||
<input
|
||||
type="text"
|
||||
style={inputStyle}
|
||||
value={this.state.input}
|
||||
onChange={this.handleChange} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,199 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036166
|
||||
title: Compose React Components
|
||||
challengeType: 6
|
||||
forumTopicId: 301381
|
||||
dashedName: compose-react-components
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
As the challenges continue to use more complex compositions with React components and JSX, there is one important point to note. Rendering ES6 style class components within other components is no different than rendering the simple components you used in the last few challenges. You can render JSX elements, stateless functional components, and ES6 class components within other components.
|
||||
|
||||
# --instructions--
|
||||
|
||||
In the code editor, the `TypesOfFood` component is already rendering a component called `Vegetables`. Also, there is the `Fruits` component from the last challenge.
|
||||
|
||||
Nest two components inside of `Fruits` — first `NonCitrus`, and then `Citrus`. Both of these components are provided for you behind the scenes. Next, nest the `Fruits` class component into the `TypesOfFood` component, below the `h1` header and above `Vegetables`. The result should be a series of nested components, which uses two different component types.
|
||||
|
||||
# --hints--
|
||||
|
||||
The `TypesOfFood` component should return a single `div` element.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
|
||||
return mockedComponent.children().type() === 'div';
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `TypesOfFood` component should return the `Fruits` component.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
|
||||
return mockedComponent.children().childAt(1).name() === 'Fruits';
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `Fruits` component should return the `NonCitrus` component and the `Citrus` component.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
|
||||
return (
|
||||
mockedComponent.find('Fruits').children().find('NonCitrus').length ===
|
||||
1 &&
|
||||
mockedComponent.find('Fruits').children().find('Citrus').length === 1
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `TypesOfFood` component should return the `Vegetables` component below the `Fruits` component.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
|
||||
return mockedComponent.children().childAt(2).name() === 'Vegetables';
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --before-user-code--
|
||||
|
||||
```jsx
|
||||
class NonCitrus extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h4>Non-Citrus:</h4>
|
||||
<ul>
|
||||
<li>Apples</li>
|
||||
<li>Blueberries</li>
|
||||
<li>Strawberries</li>
|
||||
<li>Bananas</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
class Citrus extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h4>Citrus:</h4>
|
||||
<ul>
|
||||
<li>Lemon</li>
|
||||
<li>Lime</li>
|
||||
<li>Orange</li>
|
||||
<li>Grapefruit</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
class Vegetables extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h2>Vegetables:</h2>
|
||||
<ul>
|
||||
<li>Brussel Sprouts</li>
|
||||
<li>Broccoli</li>
|
||||
<li>Squash</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<TypesOfFood />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class Fruits extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h2>Fruits:</h2>
|
||||
{ /* Change code below this line */ }
|
||||
|
||||
{ /* Change code above this line */ }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
class TypesOfFood extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Types of Food:</h1>
|
||||
{ /* Change code below this line */ }
|
||||
|
||||
{ /* Change code above this line */ }
|
||||
<Vegetables />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class Fruits extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h2>Fruits:</h2>
|
||||
{ /* Change code below this line */ }
|
||||
<NonCitrus />
|
||||
<Citrus />
|
||||
{ /* Change code above this line */ }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class TypesOfFood extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Types of Food:</h1>
|
||||
{ /* Change code below this line */ }
|
||||
<Fruits />
|
||||
{ /* Change code above this line */ }
|
||||
<Vegetables />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,110 @@
|
||||
---
|
||||
id: 5a24bbe0dba28a8d3cbd4c5d
|
||||
title: Create a Complex JSX Element
|
||||
challengeType: 6
|
||||
forumTopicId: 301382
|
||||
dashedName: create-a-complex-jsx-element
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The last challenge was a simple example of JSX, but JSX can represent more complex HTML as well.
|
||||
|
||||
One important thing to know about nested JSX is that it must return a single element.
|
||||
|
||||
This one parent element would wrap all of the other levels of nested elements.
|
||||
|
||||
For instance, several JSX elements written as siblings with no parent wrapper element will not transpile.
|
||||
|
||||
Here's an example:
|
||||
|
||||
**Valid JSX:**
|
||||
|
||||
```jsx
|
||||
<div>
|
||||
<p>Paragraph One</p>
|
||||
<p>Paragraph Two</p>
|
||||
<p>Paragraph Three</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
**Invalid JSX:**
|
||||
|
||||
```jsx
|
||||
<p>Paragraph One</p>
|
||||
<p>Paragraph Two</p>
|
||||
<p>Paragraph Three</p>
|
||||
```
|
||||
|
||||
# --instructions--
|
||||
|
||||
Define a new constant `JSX` that renders a `div` which contains the following elements in order:
|
||||
|
||||
An `h1`, a `p`, and an unordered list that contains three `li` items. You can include any text you want within each element.
|
||||
|
||||
**Note:** When rendering multiple elements like this, you can wrap them all in parentheses, but it's not strictly required. Also notice this challenge uses a `div` tag to wrap all the child elements within a single parent element. If you remove the `div`, the JSX will no longer transpile. Keep this in mind, since it will also apply when you return JSX elements in React components.
|
||||
|
||||
# --hints--
|
||||
|
||||
The constant `JSX` should return a `div` element.
|
||||
|
||||
```js
|
||||
assert(JSX.type === 'div');
|
||||
```
|
||||
|
||||
The `div` should contain an `h1` tag as the first element.
|
||||
|
||||
```js
|
||||
assert(JSX.props.children[0].type === 'h1');
|
||||
```
|
||||
|
||||
The `div` should contain a `p` tag as the second element.
|
||||
|
||||
```js
|
||||
assert(JSX.props.children[1].type === 'p');
|
||||
```
|
||||
|
||||
The `div` should contain a `ul` tag as the third element.
|
||||
|
||||
```js
|
||||
assert(JSX.props.children[2].type === 'ul');
|
||||
```
|
||||
|
||||
The `ul` should contain three `li` elements.
|
||||
|
||||
```js
|
||||
assert(
|
||||
JSX.props.children
|
||||
.filter((ele) => ele.type === 'ul')[0]
|
||||
.props.children.filter((ele) => ele.type === 'li').length === 3
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(JSX, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
const JSX = (
|
||||
<div>
|
||||
<h1>Hello JSX!</h1>
|
||||
<p>Some info</p>
|
||||
<ul>
|
||||
<li>An item</li>
|
||||
<li>Another item</li>
|
||||
<li>A third item</li>
|
||||
</ul>
|
||||
</div>);
|
||||
```
|
@ -0,0 +1,134 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036164
|
||||
title: Create a Component with Composition
|
||||
challengeType: 6
|
||||
forumTopicId: 301383
|
||||
dashedName: create-a-component-with-composition
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now we will look at how we can compose multiple React components together. Imagine you are building an app and have created three components: a `Navbar`, `Dashboard`, and `Footer`.
|
||||
|
||||
To compose these components together, you could create an `App` *parent* component which renders each of these three components as *children*. To render a component as a child in a React component, you include the component name written as a custom HTML tag in the JSX. For example, in the `render` method you could write:
|
||||
|
||||
```jsx
|
||||
return (
|
||||
<App>
|
||||
<Navbar />
|
||||
<Dashboard />
|
||||
<Footer />
|
||||
</App>
|
||||
)
|
||||
```
|
||||
|
||||
When React encounters a custom HTML tag that references another component (a component name wrapped in `< />` like in this example), it renders the markup for that component in the location of the tag. This should illustrate the parent/child relationship between the `App` component and the `Navbar`, `Dashboard`, and `Footer`.
|
||||
|
||||
# --instructions--
|
||||
|
||||
In the code editor, there is a simple functional component called `ChildComponent` and a class component called `ParentComponent`. Compose the two together by rendering the `ChildComponent` within the `ParentComponent`. Make sure to close the `ChildComponent` tag with a forward slash.
|
||||
|
||||
**Note:** `ChildComponent` is defined with an ES6 arrow function because this is a very common practice when using React. However, know that this is just a function. If you aren't familiar with the arrow function syntax, please refer to the JavaScript section.
|
||||
|
||||
# --hints--
|
||||
|
||||
The React component should return a single `div` element.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
var shallowRender = Enzyme.shallow(React.createElement(ParentComponent));
|
||||
return shallowRender.type() === 'div';
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The component should return two nested elements.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
var shallowRender = Enzyme.shallow(React.createElement(ParentComponent));
|
||||
return shallowRender.children().length === 2;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The component should return the `ChildComponent` as its second child.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(ParentComponent));
|
||||
return (
|
||||
mockedComponent.find('ParentComponent').find('ChildComponent').length ===
|
||||
1
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<ParentComponent />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
const ChildComponent = () => {
|
||||
return (
|
||||
<div>
|
||||
<p>I am the child</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
class ParentComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>I am the parent</h1>
|
||||
{ /* Change code below this line */ }
|
||||
|
||||
|
||||
{ /* Change code above this line */ }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
const ChildComponent = () => {
|
||||
return (
|
||||
<div>
|
||||
<p>I am the child</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
class ParentComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>I am the parent</h1>
|
||||
{ /* Change code below this line */ }
|
||||
<ChildComponent />
|
||||
{ /* Change code above this line */ }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,220 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036179
|
||||
title: Create a Controlled Form
|
||||
challengeType: 6
|
||||
forumTopicId: 301384
|
||||
dashedName: create-a-controlled-form
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The last challenge showed that React can control the internal state for certain elements like `input` and `textarea`, which makes them controlled components. This applies to other form elements as well, including the regular HTML `form` element.
|
||||
|
||||
# --instructions--
|
||||
|
||||
The `MyForm` component is set up with an empty `form` with a submit handler. The submit handler will be called when the form is submitted.
|
||||
|
||||
We've added a button which submits the form. You can see it has the `type` set to `submit` indicating it is the button controlling the form. Add the `input` element in the `form` and set its `value` and `onChange()` attributes like the last challenge. You should then complete the `handleSubmit` method so that it sets the component state property `submit` to the current input value in the local `state`.
|
||||
|
||||
**Note:** You also must call `event.preventDefault()` in the submit handler, to prevent the default form submit behavior which will refresh the web page. For camper convenience, the default behavior has been disabled here to prevent refreshes from resetting challenge code.
|
||||
|
||||
Finally, create an `h1` tag after the `form` which renders the `submit` value from the component's `state`. You can then type in the form and click the button (or press enter), and you should see your input rendered to the page.
|
||||
|
||||
# --hints--
|
||||
|
||||
`MyForm` should return a `div` element which contains a `form` and an `h1` tag. The form should include an `input` and a `button`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyForm));
|
||||
return (
|
||||
mockedComponent.find('div').children().find('form').length === 1 &&
|
||||
mockedComponent.find('div').children().find('h1').length === 1 &&
|
||||
mockedComponent.find('form').children().find('input').length === 1 &&
|
||||
mockedComponent.find('form').children().find('button').length === 1
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The state of `MyForm` should initialize with `input` and `submit` properties, both set to empty strings.
|
||||
|
||||
```js
|
||||
assert(
|
||||
Enzyme.mount(React.createElement(MyForm)).state('input') === '' &&
|
||||
Enzyme.mount(React.createElement(MyForm)).state('submit') === ''
|
||||
);
|
||||
```
|
||||
|
||||
Typing in the `input` element should update the `input` property of the component's state.
|
||||
|
||||
```js
|
||||
(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyForm));
|
||||
const _1 = () => {
|
||||
mockedComponent.setState({ input: '' });
|
||||
return mockedComponent.state('input');
|
||||
};
|
||||
const _2 = () => {
|
||||
mockedComponent
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: 'TestInput' } });
|
||||
return {
|
||||
state: mockedComponent.state('input'),
|
||||
inputVal: mockedComponent.find('input').props().value
|
||||
};
|
||||
};
|
||||
const before = _1();
|
||||
const after = _2();
|
||||
assert(
|
||||
before === '' &&
|
||||
after.state === 'TestInput' &&
|
||||
after.inputVal === 'TestInput'
|
||||
);
|
||||
})();
|
||||
```
|
||||
|
||||
Submitting the form should run `handleSubmit` which should set the `submit` property in state equal to the current input.
|
||||
|
||||
```js
|
||||
(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyForm));
|
||||
const _1 = () => {
|
||||
mockedComponent.setState({ input: '' });
|
||||
mockedComponent.setState({ submit: '' });
|
||||
mockedComponent
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: 'SubmitInput' } });
|
||||
return mockedComponent.state('submit');
|
||||
};
|
||||
const _2 = () => {
|
||||
mockedComponent.find('form').simulate('submit');
|
||||
return mockedComponent.state('submit');
|
||||
};
|
||||
const before = _1();
|
||||
const after = _2();
|
||||
assert(before === '' && after === 'SubmitInput');
|
||||
})();
|
||||
```
|
||||
|
||||
`handleSubmit` should call `event.preventDefault`
|
||||
|
||||
```js
|
||||
assert(
|
||||
__helpers.isCalledWithNoArgs(
|
||||
'event.preventDefault',
|
||||
MyForm.prototype.handleSubmit.toString()
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
The `h1` header should render the value of the `submit` field from the component's state.
|
||||
|
||||
```js
|
||||
(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyForm));
|
||||
const _1 = () => {
|
||||
mockedComponent.setState({ input: '' });
|
||||
mockedComponent.setState({ submit: '' });
|
||||
mockedComponent
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: 'TestInput' } });
|
||||
return mockedComponent.find('h1').text();
|
||||
};
|
||||
const _2 = () => {
|
||||
mockedComponent.find('form').simulate('submit');
|
||||
return mockedComponent.find('h1').text();
|
||||
};
|
||||
const before = _1();
|
||||
const after = _2();
|
||||
assert(before === '' && after === 'TestInput');
|
||||
})();
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<MyForm />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class MyForm extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
input: '',
|
||||
submit: ''
|
||||
};
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
}
|
||||
handleChange(event) {
|
||||
this.setState({
|
||||
input: event.target.value
|
||||
});
|
||||
}
|
||||
handleSubmit(event) {
|
||||
// Change code below this line
|
||||
|
||||
// Change code above this line
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
{/* Change code below this line */}
|
||||
|
||||
{/* Change code above this line */}
|
||||
<button type='submit'>Submit!</button>
|
||||
</form>
|
||||
{/* Change code below this line */}
|
||||
|
||||
{/* Change code above this line */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class MyForm extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
input: '',
|
||||
submit: ''
|
||||
};
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
}
|
||||
handleChange(event) {
|
||||
this.setState({
|
||||
input: event.target.value
|
||||
});
|
||||
}
|
||||
handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
this.setState(state => ({
|
||||
submit: state.input
|
||||
}));
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<input value={this.state.input} onChange={this.handleChange} />
|
||||
<button type='submit'>Submit!</button>
|
||||
</form>
|
||||
<h1>{this.state.submit}</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
@ -0,0 +1,150 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036178
|
||||
title: Create a Controlled Input
|
||||
challengeType: 6
|
||||
forumTopicId: 301385
|
||||
dashedName: create-a-controlled-input
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Your application may have more complex interactions between `state` and the rendered UI. For example, form control elements for text input, such as `input` and `textarea`, maintain their own state in the DOM as the user types. With React, you can move this mutable state into a React component's `state`. The user's input becomes part of the application `state`, so React controls the value of that input field. Typically, if you have React components with input fields the user can type into, it will be a controlled input form.
|
||||
|
||||
# --instructions--
|
||||
|
||||
The code editor has the skeleton of a component called `ControlledInput` to create a controlled `input` element. The component's `state` is already initialized with an `input` property that holds an empty string. This value represents the text a user types into the `input` field.
|
||||
|
||||
First, create a method called `handleChange()` that has a parameter called `event`. When the method is called, it receives an `event` object that contains a string of text from the `input` element. You can access this string with `event.target.value` inside the method. Update the `input` property of the component's `state` with this new string.
|
||||
|
||||
In the `render` method, create the `input` element above the `h4` tag. Add a `value` attribute which is equal to the `input` property of the component's `state`. Then add an `onChange()` event handler set to the `handleChange()` method.
|
||||
|
||||
When you type in the input box, that text is processed by the `handleChange()` method, set as the `input` property in the local `state`, and rendered as the value in the `input` box on the page. The component `state` is the single source of truth regarding the input data.
|
||||
|
||||
Last but not least, don't forget to add the necessary bindings in the constructor.
|
||||
|
||||
# --hints--
|
||||
|
||||
`ControlledInput` should return a `div` element which contains an `input` and a `p` tag.
|
||||
|
||||
```js
|
||||
assert(
|
||||
Enzyme.mount(React.createElement(ControlledInput))
|
||||
.find('div')
|
||||
.children()
|
||||
.find('input').length === 1 &&
|
||||
Enzyme.mount(React.createElement(ControlledInput))
|
||||
.find('div')
|
||||
.children()
|
||||
.find('p').length === 1
|
||||
);
|
||||
```
|
||||
|
||||
The state of `ControlledInput` should initialize with an `input` property set to an empty string.
|
||||
|
||||
```js
|
||||
assert.strictEqual(
|
||||
Enzyme.mount(React.createElement(ControlledInput)).state('input'),
|
||||
''
|
||||
);
|
||||
```
|
||||
|
||||
Typing in the input element should update the state and the value of the input, and the `p` element should render this state as you type.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const waitForIt = (fn) =>
|
||||
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
|
||||
const mockedComponent = Enzyme.mount(React.createElement(ControlledInput));
|
||||
const _1 = () => {
|
||||
mockedComponent.setState({ input: '' });
|
||||
return waitForIt(() => mockedComponent.state('input'));
|
||||
};
|
||||
const _2 = () => {
|
||||
mockedComponent
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: 'TestInput' } });
|
||||
return waitForIt(() => ({
|
||||
state: mockedComponent.state('input'),
|
||||
text: mockedComponent.find('p').text(),
|
||||
inputVal: mockedComponent.find('input').props().value
|
||||
}));
|
||||
};
|
||||
const before = await _1();
|
||||
const after = await _2();
|
||||
assert(
|
||||
before === '' &&
|
||||
after.state === 'TestInput' &&
|
||||
after.text === 'TestInput' &&
|
||||
after.inputVal === 'TestInput'
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<ControlledInput />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class ControlledInput extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
input: ''
|
||||
};
|
||||
// Change code below this line
|
||||
|
||||
// Change code above this line
|
||||
}
|
||||
// Change code below this line
|
||||
|
||||
// Change code above this line
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{ /* Change code below this line */}
|
||||
|
||||
{ /* Change code above this line */}
|
||||
<h4>Controlled Input:</h4>
|
||||
<p>{this.state.input}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class ControlledInput extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
input: ''
|
||||
};
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
}
|
||||
handleChange(event) {
|
||||
this.setState({
|
||||
input: event.target.value
|
||||
});
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
value={this.state.input}
|
||||
onChange={this.handleChange} />
|
||||
<h4>Controlled Input:</h4>
|
||||
|
||||
<p>{this.state.input}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,102 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036163
|
||||
title: Create a React Component
|
||||
challengeType: 6
|
||||
forumTopicId: 301386
|
||||
dashedName: create-a-react-component
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The other way to define a React component is with the ES6 `class` syntax. In the following example, `Kitten` extends `React.Component`:
|
||||
|
||||
```jsx
|
||||
class Kitten extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<h1>Hi</h1>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This creates an ES6 class `Kitten` which extends the `React.Component` class. So the `Kitten` class now has access to many useful React features, such as local state and lifecycle hooks. Don't worry if you aren't familiar with these terms yet, they will be covered in greater detail in later challenges. Also notice the `Kitten` class has a `constructor` defined within it that calls `super()`. It uses `super()` to call the constructor of the parent class, in this case `React.Component`. The constructor is a special method used during the initialization of objects that are created with the `class` keyword. It is best practice to call a component's `constructor` with `super`, and pass `props` to both. This makes sure the component is initialized properly. For now, know that it is standard for this code to be included. Soon you will see other uses for the constructor as well as `props`.
|
||||
|
||||
# --instructions--
|
||||
|
||||
`MyComponent` is defined in the code editor using class syntax. Finish writing the `render` method so it returns a `div` element that contains an `h1` with the text `Hello React!`.
|
||||
|
||||
# --hints--
|
||||
|
||||
The React component should return a `div` element.
|
||||
|
||||
```js
|
||||
assert(Enzyme.shallow(React.createElement(MyComponent)).type() === 'div');
|
||||
```
|
||||
|
||||
The returned `div` should render an `h1` header within it.
|
||||
|
||||
```js
|
||||
assert(
|
||||
/<div><h1>.*<\/h1><\/div>/.test(
|
||||
Enzyme.shallow(React.createElement(MyComponent)).html()
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
The `h1` header should contain the string `Hello React!`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
Enzyme.shallow(React.createElement(MyComponent)).html() ===
|
||||
'<div><h1>Hello React!</h1></div>'
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<MyComponent />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
// Change code below this line
|
||||
|
||||
|
||||
|
||||
// Change code above this line
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
// Change code below this line
|
||||
return (
|
||||
<div>
|
||||
<h1>Hello React!</h1>
|
||||
</div>
|
||||
);
|
||||
// Change code above this line
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,57 @@
|
||||
---
|
||||
id: 587d7dbc367417b2b2512bb1
|
||||
title: Create a Simple JSX Element
|
||||
challengeType: 6
|
||||
forumTopicId: 301390
|
||||
dashedName: create-a-simple-jsx-element
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
React is an Open Source view library created and maintained by Facebook. It's a great tool to render the User Interface (UI) of modern web applications.
|
||||
|
||||
React uses a syntax extension of JavaScript called JSX that allows you to write HTML directly within JavaScript. This has several benefits. It lets you use the full programmatic power of JavaScript within HTML, and helps to keep your code readable. For the most part, JSX is similar to the HTML that you have already learned, however there are a few key differences that will be covered throughout these challenges.
|
||||
|
||||
For instance, because JSX is a syntactic extension of JavaScript, you can actually write JavaScript directly within JSX. To do this, you simply include the code you want to be treated as JavaScript within curly braces: `{ 'this is treated as JavaScript code' }`. Keep this in mind, since it's used in several future challenges.
|
||||
|
||||
However, because JSX is not valid JavaScript, JSX code must be compiled into JavaScript. The transpiler Babel is a popular tool for this process. For your convenience, it's already added behind the scenes for these challenges. If you happen to write syntactically invalid JSX, you will see the first test in these challenges fail.
|
||||
|
||||
It's worth noting that under the hood the challenges are calling `ReactDOM.render(JSX, document.getElementById('root'))`. This function call is what places your JSX into React's own lightweight representation of the DOM. React then uses snapshots of its own DOM to optimize updating only specific parts of the actual DOM.
|
||||
|
||||
# --instructions--
|
||||
|
||||
The current code uses JSX to assign a `div` element to the constant `JSX`. Replace the `div` with an `h1` element and add the text `Hello JSX!` inside it.
|
||||
|
||||
# --hints--
|
||||
|
||||
The constant `JSX` should return an `h1` element.
|
||||
|
||||
```js
|
||||
assert(JSX.type === 'h1');
|
||||
```
|
||||
|
||||
The `h1` tag should include the text `Hello JSX!`
|
||||
|
||||
```js
|
||||
assert(Enzyme.shallow(JSX).contains('Hello JSX!'));
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(JSX, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
const JSX = <div></div>;
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
const JSX = <h1>Hello JSX!</h1>;
|
||||
```
|
@ -0,0 +1,134 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036170
|
||||
title: Create a Stateful Component
|
||||
challengeType: 6
|
||||
forumTopicId: 301391
|
||||
dashedName: create-a-stateful-component
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
One of the most important topics in React is `state`. State consists of any data your application needs to know about, that can change over time. You want your apps to respond to state changes and present an updated UI when necessary. React offers a nice solution for the state management of modern web applications.
|
||||
|
||||
You create state in a React component by declaring a `state` property on the component class in its `constructor`. This initializes the component with `state` when it is created. The `state` property must be set to a JavaScript `object`. Declaring it looks like this:
|
||||
|
||||
```jsx
|
||||
this.state = {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
You have access to the `state` object throughout the life of your component. You can update it, render it in your UI, and pass it as props to child components. The `state` object can be as complex or as simple as you need it to be. Note that you must create a class component by extending `React.Component` in order to create `state` like this.
|
||||
|
||||
# --instructions--
|
||||
|
||||
There is a component in the code editor that is trying to render a `name` property from its `state`. However, there is no `state` defined. Initialize the component with `state` in the `constructor` and assign your name to a property of `name`.
|
||||
|
||||
# --hints--
|
||||
|
||||
`StatefulComponent` should exist and render.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(
|
||||
React.createElement(StatefulComponent)
|
||||
);
|
||||
return mockedComponent.find('StatefulComponent').length === 1;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
`StatefulComponent` should render a `div` and an `h1` element.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(
|
||||
React.createElement(StatefulComponent)
|
||||
);
|
||||
return (
|
||||
mockedComponent.find('div').length === 1 &&
|
||||
mockedComponent.find('h1').length === 1
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The state of `StatefulComponent` should be initialized with a property `name` set to a string.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(
|
||||
React.createElement(StatefulComponent)
|
||||
);
|
||||
const initialState = mockedComponent.state();
|
||||
return (
|
||||
typeof initialState === 'object' && typeof initialState.name === 'string'
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The property `name` in the state of `StatefulComponent` should render in the `h1` element.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(
|
||||
React.createElement(StatefulComponent)
|
||||
);
|
||||
const initialState = mockedComponent.state();
|
||||
return mockedComponent.find('h1').text() === initialState.name;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<StatefulComponent />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class StatefulComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
// Only change code below this line
|
||||
|
||||
// Only change code above this line
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>{this.state.name}</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class StatefulComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
name: 'freeCodeCamp!'
|
||||
}
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>{this.state.name}</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,102 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036162
|
||||
title: Create a Stateless Functional Component
|
||||
challengeType: 6
|
||||
forumTopicId: 301392
|
||||
dashedName: create-a-stateless-functional-component
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Components are the core of React. Everything in React is a component and here you will learn how to create one.
|
||||
|
||||
There are two ways to create a React component. The first way is to use a JavaScript function. Defining a component in this way creates a *stateless functional component*. The concept of state in an application will be covered in later challenges. For now, think of a stateless component as one that can receive data and render it, but does not manage or track changes to that data. (We'll cover the second way to create a React component in the next challenge.)
|
||||
|
||||
To create a component with a function, you simply write a JavaScript function that returns either JSX or `null`. One important thing to note is that React requires your function name to begin with a capital letter. Here's an example of a stateless functional component that assigns an HTML class in JSX:
|
||||
|
||||
```jsx
|
||||
const DemoComponent = function() {
|
||||
return (
|
||||
<div className='customClass' />
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
After being transpiled, the `<div>` will have a CSS class of `customClass`.
|
||||
|
||||
Because a JSX component represents HTML, you could put several components together to create a more complex HTML page. This is one of the key advantages of the component architecture React provides. It allows you to compose your UI from many separate, isolated components. This makes it easier to build and maintain complex user interfaces.
|
||||
|
||||
# --instructions--
|
||||
|
||||
The code editor has a function called `MyComponent`. Complete this function so it returns a single `div` element which contains some string of text.
|
||||
|
||||
**Note:** The text is considered a child of the `div` element, so you will not be able to use a self-closing tag.
|
||||
|
||||
# --hints--
|
||||
|
||||
`MyComponent` should return JSX.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
return mockedComponent.length === 1;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
`MyComponent` should return a `div` element.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
return mockedComponent.children().type() === 'div';
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `div` element should contain a string of text.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
return mockedComponent.find('div').text() !== '';
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<MyComponent />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
const MyComponent = function() {
|
||||
// Change code below this line
|
||||
|
||||
|
||||
|
||||
// Change code above this line
|
||||
}
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
const MyComponent = function() {
|
||||
// Change code below this line
|
||||
return (
|
||||
<div>
|
||||
Demo Solution
|
||||
</div>
|
||||
);
|
||||
// Change code above this line
|
||||
}
|
||||
```
|
@ -0,0 +1,62 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036160
|
||||
title: Define an HTML Class in JSX
|
||||
challengeType: 6
|
||||
forumTopicId: 301393
|
||||
dashedName: define-an-html-class-in-jsx
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now that you're getting comfortable writing JSX, you may be wondering how it differs from HTML.
|
||||
|
||||
So far, it may seem that HTML and JSX are exactly the same.
|
||||
|
||||
One key difference in JSX is that you can no longer use the word `class` to define HTML classes. This is because `class` is a reserved word in JavaScript. Instead, JSX uses `className`.
|
||||
|
||||
In fact, the naming convention for all HTML attributes and event references in JSX become camelCase. For example, a click event in JSX is `onClick`, instead of `onclick`. Likewise, `onchange` becomes `onChange`. While this is a subtle difference, it is an important one to keep in mind moving forward.
|
||||
|
||||
# --instructions--
|
||||
|
||||
Apply a class of `myDiv` to the `div` provided in the JSX code.
|
||||
|
||||
# --hints--
|
||||
|
||||
The constant `JSX` should return a `div` element.
|
||||
|
||||
```js
|
||||
assert.strictEqual(JSX.type, 'div');
|
||||
```
|
||||
|
||||
The `div` should have a class of `myDiv`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(JSX.props.className, 'myDiv');
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(JSX, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
const JSX = (
|
||||
<div>
|
||||
<h1>Add a class to this div</h1>
|
||||
</div>
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
const JSX = (
|
||||
<div className = 'myDiv'>
|
||||
<h1>Add a class to this div</h1>
|
||||
</div>);
|
||||
```
|
@ -0,0 +1,147 @@
|
||||
---
|
||||
id: 5a24c314108439a4d403618b
|
||||
title: Give Sibling Elements a Unique Key Attribute
|
||||
challengeType: 6
|
||||
forumTopicId: 301394
|
||||
dashedName: give-sibling-elements-a-unique-key-attribute
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The last challenge showed how the `map` method is used to dynamically render a number of elements based on user input. However, there was an important piece missing from that example. When you create an array of elements, each one needs a `key` attribute set to a unique value. React uses these keys to keep track of which items are added, changed, or removed. This helps make the re-rendering process more efficient when the list is modified in any way.
|
||||
|
||||
**Note:** Keys only need to be unique between sibling elements, they don't need to be globally unique in your application.
|
||||
|
||||
# --instructions--
|
||||
|
||||
The code editor has an array with some front end frameworks and a stateless functional component named `Frameworks()`. `Frameworks()` needs to map the array to an unordered list, much like in the last challenge. Finish writing the `map` callback to return an `li` element for each framework in the `frontEndFrameworks` array. This time, make sure to give each `li` a `key` attribute, set to a unique value. The `li` elements should also contain text from `frontEndFrameworks`.
|
||||
|
||||
Normally, you want to make the key something that uniquely identifies the element being rendered. As a last resort the array index may be used, but typically you should try to use a unique identification.
|
||||
|
||||
# --hints--
|
||||
|
||||
The `Frameworks` component should exist and render to the page.
|
||||
|
||||
```js
|
||||
assert(
|
||||
Enzyme.mount(React.createElement(Frameworks)).find('Frameworks').length === 1
|
||||
);
|
||||
```
|
||||
|
||||
`Frameworks` should render an `h1` element.
|
||||
|
||||
```js
|
||||
assert(Enzyme.mount(React.createElement(Frameworks)).find('h1').length === 1);
|
||||
```
|
||||
|
||||
`Frameworks` should render a `ul` element.
|
||||
|
||||
```js
|
||||
assert(Enzyme.mount(React.createElement(Frameworks)).find('ul').length === 1);
|
||||
```
|
||||
|
||||
The `ul` tag should render 6 child `li` elements.
|
||||
|
||||
```js
|
||||
assert(
|
||||
Enzyme.mount(React.createElement(Frameworks)).find('ul').children().length ===
|
||||
6 &&
|
||||
Enzyme.mount(React.createElement(Frameworks))
|
||||
.find('ul')
|
||||
.childAt(0)
|
||||
.name() === 'li' &&
|
||||
Enzyme.mount(React.createElement(Frameworks)).find('li').length === 6
|
||||
);
|
||||
```
|
||||
|
||||
Each list item element should have a unique `key` attribute.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(() => {
|
||||
const ul = Enzyme.mount(React.createElement(Frameworks)).find('ul');
|
||||
const keys = new Set([
|
||||
ul.childAt(0).key(),
|
||||
ul.childAt(1).key(),
|
||||
ul.childAt(2).key(),
|
||||
ul.childAt(3).key(),
|
||||
ul.childAt(4).key(),
|
||||
ul.childAt(5).key()
|
||||
]);
|
||||
return keys.size === 6;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
Each list item element should contain text from `frontEndFrameworks`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(() => {
|
||||
const li = Enzyme.mount(React.createElement(Frameworks))
|
||||
.find('ul')
|
||||
.children();
|
||||
return [...Array(5)].every((_, i) =>
|
||||
frontEndFrameworks.includes(li.at(i).text())
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<Frameworks />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
const frontEndFrameworks = [
|
||||
'React',
|
||||
'Angular',
|
||||
'Ember',
|
||||
'Knockout',
|
||||
'Backbone',
|
||||
'Vue'
|
||||
];
|
||||
|
||||
function Frameworks() {
|
||||
const renderFrameworks = null; // Change this line
|
||||
return (
|
||||
<div>
|
||||
<h1>Popular Front End JavaScript Frameworks</h1>
|
||||
<ul>
|
||||
{renderFrameworks}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
const frontEndFrameworks = [
|
||||
'React',
|
||||
'Angular',
|
||||
'Ember',
|
||||
'Knockout',
|
||||
'Backbone',
|
||||
'Vue'
|
||||
];
|
||||
|
||||
function Frameworks() {
|
||||
const renderFrameworks = frontEndFrameworks.map((fw, i) => <li key={i}>{fw}</li>);
|
||||
return (
|
||||
<div>
|
||||
<h1>Popular Front End JavaScript Frameworks</h1>
|
||||
<ul>
|
||||
{renderFrameworks}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
@ -0,0 +1,104 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036181
|
||||
title: Introduzir estilos em linha
|
||||
challengeType: 6
|
||||
forumTopicId: 301395
|
||||
dashedName: introducing-inline-styles
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Existem outros conceitos complexos que adicionam recursos poderosos ao seu código React. Mas você pode estar se perguntando sobre o problema mais simples de como estilizar esses elementos JSX que você cria em React. Você provavelmente sabe que não será exatamente o mesmo que trabalhar com HTML por causa [da forma como aplica classes aos elementos JSX](/learn/front-end-development-libraries/react/define-an-html-class-in-jsx).
|
||||
|
||||
Se você importar estilos de uma folha de estilos, não é muito diferente. Você aplica uma classe ao seu elemento JSX usando o atributo `className` e aplica estilos à classe em sua folha de estilos. Outra opção é aplicar estilos em linha, que são muito comuns no desenvolvimento de ReactJS.
|
||||
|
||||
Você aplica estilos em linha em elementos JSX similares a como você faz isso em HTML, mas com algumas diferenças em JSX. Aqui está um exemplo de estilo integrado em HTML:
|
||||
|
||||
```jsx
|
||||
<div style="color: yellow; font-size: 16px">Mellow Yellow</div>
|
||||
```
|
||||
|
||||
Elementos JSX usam o atributo `style`, mas por causa da forma como o JSX é transpilado, você não pode definir o valor para uma `string`. Em vez disso, você o definiu para ser igual a um `objeto` JavaScript. Exemplo:
|
||||
|
||||
```jsx
|
||||
<div style={{color: "yellow", fontSize: 16}}>Mellow Yellow</div>
|
||||
```
|
||||
|
||||
Percebeu como colocamos em camelCase a propriedade `fontSize`? Isso é porque o React não aceitará chaves hifenizadas (em kebab-case) no objeto de estilo. React aplicará o nome de propriedade correto para nós no HTML.
|
||||
|
||||
# --instructions--
|
||||
|
||||
Adicione um atributo `style` para o `div` no editor de código para dar ao texto a cor vermelha e tamanho da fonte de `72px`.
|
||||
|
||||
Note que você pode opcionalmente definir o tamanho da fonte como um número. omitindo as unidades `px`, ou escreva-o como `72px`.
|
||||
|
||||
# --hints--
|
||||
|
||||
O componente deve renderizar um elemento `div`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(Colorful));
|
||||
return mockedComponent.children().type() === 'div';
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
O elemento `div` deve ter a cor `red`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(Colorful));
|
||||
return mockedComponent.children().props().style.color === 'red';
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
O elemento `div` deve ter um tamanho de fonte de `72px`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(Colorful));
|
||||
return (
|
||||
mockedComponent.children().props().style.fontSize === 72 ||
|
||||
mockedComponent.children().props().style.fontSize === '72' ||
|
||||
mockedComponent.children().props().style.fontSize === '72px'
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<Colorful />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class Colorful extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>Big Red</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class Colorful extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div style={{color: "red", fontSize: 72}}>Big Red</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,75 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036161
|
||||
title: Learn About Self-Closing JSX Tags
|
||||
challengeType: 6
|
||||
forumTopicId: 301396
|
||||
dashedName: learn-about-self-closing-jsx-tags
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
So far, you’ve seen how JSX differs from HTML in a key way with the use of `className` vs. `class` for defining HTML classes.
|
||||
|
||||
Another important way in which JSX differs from HTML is in the idea of the self-closing tag.
|
||||
|
||||
In HTML, almost all tags have both an opening and closing tag: `<div></div>`; the closing tag always has a forward slash before the tag name that you are closing. However, there are special instances in HTML called “self-closing tags”, or tags that don’t require both an opening and closing tag before another tag can start.
|
||||
|
||||
For example the line-break tag can be written as `<br>` or as `<br />`, but should never be written as `<br></br>`, since it doesn't contain any content.
|
||||
|
||||
In JSX, the rules are a little different. Any JSX element can be written with a self-closing tag, and every element must be closed. The line-break tag, for example, must always be written as `<br />` in order to be valid JSX that can be transpiled. A `<div>`, on the other hand, can be written as `<div />` or `<div></div>`. The difference is that in the first syntax version there is no way to include anything in the `<div />`. You will see in later challenges that this syntax is useful when rendering React components.
|
||||
|
||||
# --instructions--
|
||||
|
||||
Fix the errors in the code editor so that it is valid JSX and successfully transpiles. Make sure you don't change any of the content - you only need to close tags where they are needed.
|
||||
|
||||
# --hints--
|
||||
|
||||
The constant `JSX` should return a `div` element.
|
||||
|
||||
```js
|
||||
assert.strictEqual(JSX.type, 'div');
|
||||
```
|
||||
|
||||
The `div` should contain a `br` tag.
|
||||
|
||||
```js
|
||||
assert(Enzyme.shallow(JSX).find('br').length === 1);
|
||||
```
|
||||
|
||||
The `div` should contain an `hr` tag.
|
||||
|
||||
```js
|
||||
assert(Enzyme.shallow(JSX).find('hr').length === 1);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(JSX, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
const JSX = (
|
||||
<div>
|
||||
<h2>Welcome to React!</h2> <br >
|
||||
<p>Be sure to close all tags!</p>
|
||||
<hr >
|
||||
</div>
|
||||
);
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
const JSX = (
|
||||
<div>
|
||||
<h2>Welcome to React!</h2> <br />
|
||||
<p>Be sure to close all tags!</p>
|
||||
<hr />
|
||||
</div>
|
||||
);
|
||||
```
|
@ -0,0 +1,187 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036180
|
||||
title: Optimize Re-Renders with shouldComponentUpdate
|
||||
challengeType: 6
|
||||
forumTopicId: 301398
|
||||
dashedName: optimize-re-renders-with-shouldcomponentupdate
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
So far, if any component receives new `state` or new `props`, it re-renders itself and all its children. This is usually okay. But React provides a lifecycle method you can call when child components receive new `state` or `props`, and declare specifically if the components should update or not. The method is `shouldComponentUpdate()`, and it takes `nextProps` and `nextState` as parameters.
|
||||
|
||||
This method is a useful way to optimize performance. For example, the default behavior is that your component re-renders when it receives new `props`, even if the `props` haven't changed. You can use `shouldComponentUpdate()` to prevent this by comparing the `props`. The method must return a `boolean` value that tells React whether or not to update the component. You can compare the current props (`this.props`) to the next props (`nextProps`) to determine if you need to update or not, and return `true` or `false` accordingly.
|
||||
|
||||
# --instructions--
|
||||
|
||||
The `shouldComponentUpdate()` method is added in a component called `OnlyEvens`. Currently, this method returns `true` so `OnlyEvens` re-renders every time it receives new `props`. Modify the method so `OnlyEvens` updates only if the `value` of its new props is even. Click the `Add` button and watch the order of events in your browser's console as the lifecycle hooks are triggered.
|
||||
|
||||
# --hints--
|
||||
|
||||
The `Controller` component should render the `OnlyEvens` component as a child.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(Controller));
|
||||
return (
|
||||
mockedComponent.find('Controller').length === 1 &&
|
||||
mockedComponent.find('OnlyEvens').length === 1
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `shouldComponentUpdate` method should be defined on the `OnlyEvens` component.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(() => {
|
||||
const child = React.createElement(OnlyEvens)
|
||||
.type.prototype.shouldComponentUpdate.toString()
|
||||
.replace(/s/g, '');
|
||||
return child !== 'undefined';
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `OnlyEvens` component should return an `h1` tag which renders the value of `this.props.value`.
|
||||
|
||||
```js
|
||||
(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(Controller));
|
||||
const first = () => {
|
||||
mockedComponent.setState({ value: 1000 });
|
||||
return mockedComponent.find('h1').html();
|
||||
};
|
||||
const second = () => {
|
||||
mockedComponent.setState({ value: 10 });
|
||||
return mockedComponent.find('h1').html();
|
||||
};
|
||||
const firstValue = first();
|
||||
const secondValue = second();
|
||||
assert(firstValue === '<h1>1000</h1>' && secondValue === '<h1>10</h1>');
|
||||
})();
|
||||
```
|
||||
|
||||
`OnlyEvens` should re-render only when `nextProps.value` is even.
|
||||
|
||||
```js
|
||||
(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(Controller));
|
||||
const first = () => {
|
||||
mockedComponent.setState({ value: 8 });
|
||||
return mockedComponent.find('h1').text();
|
||||
};
|
||||
const second = () => {
|
||||
mockedComponent.setState({ value: 7 });
|
||||
return mockedComponent.find('h1').text();
|
||||
};
|
||||
const third = () => {
|
||||
mockedComponent.setState({ value: 42 });
|
||||
return mockedComponent.find('h1').text();
|
||||
};
|
||||
const firstValue = first();
|
||||
const secondValue = second();
|
||||
const thirdValue = third();
|
||||
assert(firstValue === '8' && secondValue === '8' && thirdValue === '42');
|
||||
})();
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<Controller />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class OnlyEvens extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
console.log('Should I update?');
|
||||
// Change code below this line
|
||||
return true;
|
||||
// Change code above this line
|
||||
}
|
||||
componentDidUpdate() {
|
||||
console.log('Component re-rendered.');
|
||||
}
|
||||
render() {
|
||||
return <h1>{this.props.value}</h1>;
|
||||
}
|
||||
}
|
||||
|
||||
class Controller extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
value: 0
|
||||
};
|
||||
this.addValue = this.addValue.bind(this);
|
||||
}
|
||||
addValue() {
|
||||
this.setState(state => ({
|
||||
value: state.value + 1
|
||||
}));
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.addValue}>Add</button>
|
||||
<OnlyEvens value={this.state.value} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class OnlyEvens extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
console.log('Should I update?');
|
||||
// Change code below this line
|
||||
return nextProps.value % 2 === 0;
|
||||
// Change code above this line
|
||||
}
|
||||
componentDidUpdate() {
|
||||
console.log('Component re-rendered.');
|
||||
}
|
||||
render() {
|
||||
return <h1>{this.props.value}</h1>;
|
||||
}
|
||||
}
|
||||
|
||||
class Controller extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
value: 0
|
||||
};
|
||||
this.addValue = this.addValue.bind(this);
|
||||
}
|
||||
addValue() {
|
||||
this.setState(state => ({
|
||||
value: state.value + 1
|
||||
}));
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.addValue}>Add</button>
|
||||
<OnlyEvens value={this.state.value} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
@ -0,0 +1,112 @@
|
||||
---
|
||||
id: 5a24c314108439a4d403616c
|
||||
title: Override Default Props
|
||||
challengeType: 6
|
||||
forumTopicId: 301399
|
||||
dashedName: override-default-props
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The ability to set default props is a useful feature in React. The way to override the default props is to explicitly set the prop values for a component.
|
||||
|
||||
# --instructions--
|
||||
|
||||
The `ShoppingCart` component now renders a child component `Items`. This `Items` component has a default prop `quantity` set to the integer `0`. Override the default prop by passing in a value of `10` for `quantity`.
|
||||
|
||||
**Note:** Remember that the syntax to add a prop to a component looks similar to how you add HTML attributes. However, since the value for `quantity` is an integer, it won't go in quotes but it should be wrapped in curly braces. For example, `{100}`. This syntax tells JSX to interpret the value within the braces directly as JavaScript.
|
||||
|
||||
# --hints--
|
||||
|
||||
The component `ShoppingCart` should render.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
|
||||
return mockedComponent.find('ShoppingCart').length === 1;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The component `Items` should render.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
|
||||
return mockedComponent.find('Items').length === 1;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `Items` component should have a prop of `{ quantity: 10 }` passed from the `ShoppingCart` component.
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
|
||||
return (
|
||||
mockedComponent.find('Items').props().quantity == 10 &&
|
||||
getUserInput('index')
|
||||
.replace(/ /g, '')
|
||||
.includes('<Itemsquantity={10}/>')
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<ShoppingCart />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
const Items = (props) => {
|
||||
return <h1>Current Quantity of Items in Cart: {props.quantity}</h1>
|
||||
}
|
||||
|
||||
Items.defaultProps = {
|
||||
quantity: 0
|
||||
}
|
||||
|
||||
class ShoppingCart extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
{ /* Change code below this line */ }
|
||||
return <Items />
|
||||
{ /* Change code above this line */ }
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
const Items = (props) => {
|
||||
return <h1>Current Quantity of Items in Cart: {props.quantity}</h1>
|
||||
}
|
||||
|
||||
Items.defaultProps = {
|
||||
quantity: 0
|
||||
}
|
||||
|
||||
class ShoppingCart extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
{ /* Change code below this line */ }
|
||||
return <Items quantity = {10} />
|
||||
{ /* Change code above this line */ }
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,217 @@
|
||||
---
|
||||
id: 5a24c314108439a4d403617b
|
||||
title: Pass a Callback as Props
|
||||
challengeType: 6
|
||||
forumTopicId: 301400
|
||||
dashedName: pass-a-callback-as-props
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
You can pass `state` as props to child components, but you're not limited to passing data. You can also pass handler functions or any method that's defined on a React component to a child component. This is how you allow child components to interact with their parent components. You pass methods to a child just like a regular prop. It's assigned a name and you have access to that method name under `this.props` in the child component.
|
||||
|
||||
# --instructions--
|
||||
|
||||
There are three components outlined in the code editor. The `MyApp` component is the parent that will render the `GetInput` and `RenderInput` child components. Add the `GetInput` component to the render method in `MyApp`, then pass it a prop called `input` assigned to `inputValue` from `MyApp`'s `state`. Also create a prop called `handleChange` and pass the input handler `handleChange` to it.
|
||||
|
||||
Next, add `RenderInput` to the render method in `MyApp`, then create a prop called `input` and pass the `inputValue` from `state` to it. Once you are finished you will be able to type in the `input` field in the `GetInput` component, which then calls the handler method in its parent via props. This updates the input in the `state` of the parent, which is passed as props to both children. Observe how the data flows between the components and how the single source of truth remains the `state` of the parent component. Admittedly, this example is a bit contrived, but should serve to illustrate how data and callbacks can be passed between React components.
|
||||
|
||||
# --hints--
|
||||
|
||||
The `MyApp` component should render.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
|
||||
return mockedComponent.find('MyApp').length === 1;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `GetInput` component should render.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
|
||||
return mockedComponent.find('GetInput').length === 1;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `RenderInput` component should render.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
|
||||
return mockedComponent.find('RenderInput').length === 1;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `GetInput` component should receive the `MyApp` state property `inputValue` as props and contain an `input` element which modifies `MyApp` state.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const waitForIt = (fn) =>
|
||||
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
|
||||
const state_1 = () => {
|
||||
mockedComponent.setState({ inputValue: '' });
|
||||
return waitForIt(() => mockedComponent.state());
|
||||
};
|
||||
const state_2 = () => {
|
||||
mockedComponent
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: 'TestInput' } });
|
||||
return waitForIt(() => mockedComponent.state());
|
||||
};
|
||||
const updated_1 = await state_1();
|
||||
const updated_2 = await state_2();
|
||||
assert(updated_1.inputValue === '' && updated_2.inputValue === 'TestInput');
|
||||
};
|
||||
```
|
||||
|
||||
The `RenderInput` component should receive the `MyApp` state property `inputValue` as props.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const waitForIt = (fn) =>
|
||||
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
|
||||
const state_1 = () => {
|
||||
mockedComponent.setState({ inputValue: 'TestName' });
|
||||
return waitForIt(() => mockedComponent);
|
||||
};
|
||||
const updated_1 = await state_1();
|
||||
assert(updated_1.find('p').text().includes('TestName'));
|
||||
};
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<MyApp />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class MyApp extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
inputValue: ''
|
||||
}
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
}
|
||||
handleChange(event) {
|
||||
this.setState({
|
||||
inputValue: event.target.value
|
||||
});
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{ /* Change code below this line */ }
|
||||
|
||||
{ /* Change code above this line */ }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
class GetInput extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h3>Get Input:</h3>
|
||||
<input
|
||||
value={this.props.input}
|
||||
onChange={this.props.handleChange}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
class RenderInput extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h3>Input Render:</h3>
|
||||
<p>{this.props.input}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class MyApp extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
inputValue: ''
|
||||
}
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
}
|
||||
handleChange(event) {
|
||||
this.setState({
|
||||
inputValue: event.target.value
|
||||
});
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<GetInput
|
||||
input={this.state.inputValue}
|
||||
handleChange={this.handleChange}/>
|
||||
<RenderInput
|
||||
input={this.state.inputValue}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
class GetInput extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h3>Get Input:</h3>
|
||||
<input
|
||||
value={this.props.input}
|
||||
onChange={this.props.handleChange}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
class RenderInput extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h3>Input Render:</h3>
|
||||
<p>{this.props.input}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,186 @@
|
||||
---
|
||||
id: 5a24c314108439a4d403616a
|
||||
title: Pass an Array as Props
|
||||
challengeType: 6
|
||||
forumTopicId: 301401
|
||||
dashedName: pass-an-array-as-props
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The last challenge demonstrated how to pass information from a parent component to a child component as `props` or properties. This challenge looks at how arrays can be passed as `props`. To pass an array to a JSX element, it must be treated as JavaScript and wrapped in curly braces.
|
||||
|
||||
```jsx
|
||||
<ParentComponent>
|
||||
<ChildComponent colors={["green", "blue", "red"]} />
|
||||
</ParentComponent>
|
||||
```
|
||||
|
||||
The child component then has access to the array property `colors`. Array methods such as `join()` can be used when accessing the property. `const ChildComponent = (props) => <p>{props.colors.join(', ')}</p>` This will join all `colors` array items into a comma separated string and produce: `<p>green, blue, red</p>` Later, we will learn about other common methods to render arrays of data in React.
|
||||
|
||||
# --instructions--
|
||||
|
||||
There are `List` and `ToDo` components in the code editor. When rendering each `List` from the `ToDo` component, pass in a `tasks` property assigned to an array of to-do tasks, for example `["walk dog", "workout"]`. Then access this `tasks` array in the `List` component, showing its value within the `p` element. Use `join(", ")` to display the `props.tasks`array in the `p` element as a comma separated list. Today's list should have at least 2 tasks and tomorrow's should have at least 3 tasks.
|
||||
|
||||
# --hints--
|
||||
|
||||
The `ToDo` component should return a single outer `div`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
|
||||
return mockedComponent.children().first().type() === 'div';
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The third child of the `ToDo` component should be an instance of the `List` component.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
|
||||
return mockedComponent.children().first().childAt(2).name() === 'List';
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The fifth child of the `ToDo` component should be an instance of the `List` component.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
|
||||
return mockedComponent.children().first().childAt(4).name() === 'List';
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
Both instances of the `List` component should have a property called `tasks` and `tasks` should be of type array.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
|
||||
return (
|
||||
Array.isArray(mockedComponent.find('List').get(0).props.tasks) &&
|
||||
Array.isArray(mockedComponent.find('List').get(1).props.tasks)
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The first `List` component representing the tasks for today should have 2 or more items.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
|
||||
return mockedComponent.find('List').get(0).props.tasks.length >= 2;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The second `List` component representing the tasks for tomorrow should have 3 or more items.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
|
||||
return mockedComponent.find('List').get(1).props.tasks.length >= 3;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `List` component should render the value from the `tasks` prop in the `p` tag.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(ToDo));
|
||||
return (
|
||||
mockedComponent
|
||||
.find('p')
|
||||
.get(0)
|
||||
.props.children.replace(/\s*,\s*/g, ',') ===
|
||||
mockedComponent
|
||||
.find('List')
|
||||
.get(0)
|
||||
.props.tasks.join(',')
|
||||
.replace(/\s*,\s*/g, ',') &&
|
||||
mockedComponent
|
||||
.find('p')
|
||||
.get(1)
|
||||
.props.children.replace(/\s*,\s*/g, ',') ===
|
||||
mockedComponent
|
||||
.find('List')
|
||||
.get(1)
|
||||
.props.tasks.join(',')
|
||||
.replace(/\s*,\s*/g, ',')
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<ToDo />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
const List = (props) => {
|
||||
{ /* Change code below this line */ }
|
||||
return <p>{}</p>
|
||||
{ /* Change code above this line */ }
|
||||
};
|
||||
|
||||
class ToDo extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>To Do Lists</h1>
|
||||
<h2>Today</h2>
|
||||
{ /* Change code below this line */ }
|
||||
<List/>
|
||||
<h2>Tomorrow</h2>
|
||||
<List/>
|
||||
{ /* Change code above this line */ }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
const List= (props) => {
|
||||
return <p>{props.tasks.join(', ')}</p>
|
||||
};
|
||||
|
||||
class ToDo extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>To Do Lists</h1>
|
||||
<h2>Today</h2>
|
||||
<List tasks={['study', 'exercise']} />
|
||||
<h2>Tomorrow</h2>
|
||||
<List tasks={['call Sam', 'grocery shopping', 'order tickets']} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,164 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036169
|
||||
title: Pass Props to a Stateless Functional Component
|
||||
challengeType: 6
|
||||
forumTopicId: 301402
|
||||
dashedName: pass-props-to-a-stateless-functional-component
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The previous challenges covered a lot about creating and composing JSX elements, functional components, and ES6 style class components in React. With this foundation, it's time to look at another feature very common in React: **props**. In React, you can pass props, or properties, to child components. Say you have an `App` component which renders a child component called `Welcome` which is a stateless functional component. You can pass `Welcome` a `user` property by writing:
|
||||
|
||||
```jsx
|
||||
<App>
|
||||
<Welcome user='Mark' />
|
||||
</App>
|
||||
```
|
||||
|
||||
You use **custom HTML attributes** created by you and supported by React to be passed to the component. In this case, the created property `user` is passed to the component `Welcome`. Since `Welcome` is a stateless functional component, it has access to this value like so:
|
||||
|
||||
```jsx
|
||||
const Welcome = (props) => <h1>Hello, {props.user}!</h1>
|
||||
```
|
||||
|
||||
It is standard to call this value `props` and when dealing with stateless functional components, you basically consider it as an argument to a function which returns JSX. You can access the value of the argument in the function body. With class components, you will see this is a little different.
|
||||
|
||||
# --instructions--
|
||||
|
||||
There are `Calendar` and `CurrentDate` components in the code editor. When rendering `CurrentDate` from the `Calendar` component, pass in a property of `date` assigned to the current date from JavaScript's `Date` object. Then access this `prop` in the `CurrentDate` component, showing its value within the `p` tags. Note that for `prop` values to be evaluated as JavaScript, they must be enclosed in curly brackets, for instance `date={Date()}`.
|
||||
|
||||
# --hints--
|
||||
|
||||
The `Calendar` component should return a single `div` element.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(Calendar));
|
||||
return mockedComponent.children().type() === 'div';
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The second child of the `Calendar` component should be the `CurrentDate` component.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(Calendar));
|
||||
return mockedComponent.children().childAt(1).name() === 'CurrentDate';
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `CurrentDate` component should have a prop called `date`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(Calendar));
|
||||
return mockedComponent.children().childAt(1).props().date;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `date` prop of the `CurrentDate` should contain a string of text.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(Calendar));
|
||||
const prop = mockedComponent.children().childAt(1).props().date;
|
||||
return typeof prop === 'string' && prop.length > 0;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `date` prop should be generated by calling `Date()`
|
||||
|
||||
```js
|
||||
assert(/<CurrentDatedate={Date\(\)}\/>/.test(__helpers.removeWhiteSpace(code)));
|
||||
```
|
||||
|
||||
The `CurrentDate` component should render the value from the `date` prop in the `p` tag.
|
||||
|
||||
```js
|
||||
let date = 'dummy date';
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(
|
||||
React.createElement(CurrentDate, { date })
|
||||
);
|
||||
return mockedComponent.find('p').html().includes(date);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<Calendar />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
const CurrentDate = (props) => {
|
||||
return (
|
||||
<div>
|
||||
{ /* Change code below this line */ }
|
||||
<p>The current date is: </p>
|
||||
{ /* Change code above this line */ }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
class Calendar extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h3>What date is it?</h3>
|
||||
{ /* Change code below this line */ }
|
||||
<CurrentDate />
|
||||
{ /* Change code above this line */ }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
const CurrentDate = (props) => {
|
||||
return (
|
||||
<div>
|
||||
{ /* Change code below this line */ }
|
||||
<p>The current date is: {props.date}</p>
|
||||
{ /* Change code above this line */ }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
class Calendar extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h3>What date is it?</h3>
|
||||
{ /* Change code below this line */ }
|
||||
<CurrentDate date={Date()} />
|
||||
{ /* Change code above this line */ }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,145 @@
|
||||
---
|
||||
id: 5a24c314108439a4d403617a
|
||||
title: Pass State as Props to Child Components
|
||||
challengeType: 6
|
||||
forumTopicId: 301403
|
||||
dashedName: pass-state-as-props-to-child-components
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
You saw a lot of examples that passed props to child JSX elements and child React components in previous challenges. You may be wondering where those props come from. A common pattern is to have a stateful component containing the `state` important to your app, that then renders child components. You want these components to have access to some pieces of that `state`, which are passed in as props.
|
||||
|
||||
For example, maybe you have an `App` component that renders a `Navbar`, among other components. In your `App`, you have `state` that contains a lot of user information, but the `Navbar` only needs access to the user's username so it can display it. You pass that piece of `state` to the `Navbar` component as a prop.
|
||||
|
||||
This pattern illustrates some important paradigms in React. The first is *unidirectional data flow*. State flows in one direction down the tree of your application's components, from the stateful parent component to child components. The child components only receive the state data they need. The second is that complex stateful apps can be broken down into just a few, or maybe a single, stateful component. The rest of your components simply receive state from the parent as props, and render a UI from that state. It begins to create a separation where state management is handled in one part of code and UI rendering in another. This principle of separating state logic from UI logic is one of React's key principles. When it's used correctly, it makes the design of complex, stateful applications much easier to manage.
|
||||
|
||||
# --instructions--
|
||||
|
||||
The `MyApp` component is stateful and renders a `Navbar` component as a child. Pass the `name` property in its `state` down to the child component, then show the `name` in the `h1` tag that's part of the `Navbar` render method. `name` should appear after the text `Hello, my name is:`.
|
||||
|
||||
# --hints--
|
||||
|
||||
The `MyApp` component should render with a `Navbar` component inside.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
|
||||
return (
|
||||
mockedComponent.find('MyApp').length === 1 &&
|
||||
mockedComponent.find('Navbar').length === 1
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `Navbar` component should receive the `MyApp` state property `name` as props.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const waitForIt = (fn) =>
|
||||
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
|
||||
const setState = () => {
|
||||
mockedComponent.setState({ name: 'TestName' });
|
||||
return waitForIt(() => mockedComponent.find('Navbar').props());
|
||||
};
|
||||
const navProps = await setState();
|
||||
assert(navProps.name === 'TestName');
|
||||
};
|
||||
```
|
||||
|
||||
The `h1` element in `Navbar` should render the `name` prop.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const waitForIt = (fn) =>
|
||||
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyApp));
|
||||
const navH1Before = mockedComponent.find('Navbar').find('h1').text();
|
||||
const setState = () => {
|
||||
mockedComponent.setState({ name: 'TestName' });
|
||||
return waitForIt(() => mockedComponent.find('Navbar').find('h1').text());
|
||||
};
|
||||
const navH1After = await setState();
|
||||
assert(new RegExp('TestName').test(navH1After) && navH1After !== navH1Before);
|
||||
};
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<MyApp />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class MyApp extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
name: 'CamperBot'
|
||||
}
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{/* Change code below this line */}
|
||||
<Navbar />
|
||||
{/* Change code above this line */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
class Navbar extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{/* Change code below this line */}
|
||||
<h1>Hello, my name is: </h1>
|
||||
{/* Change code above this line */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class MyApp extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
name: 'CamperBot'
|
||||
}
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Navbar name={this.state.name}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
class Navbar extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Hello, my name is: {this.props.name}</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,159 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036167
|
||||
title: Render a Class Component to the DOM
|
||||
challengeType: 6
|
||||
forumTopicId: 301404
|
||||
dashedName: render-a-class-component-to-the-dom
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
You may remember using the ReactDOM API in an earlier challenge to render JSX elements to the DOM. The process for rendering React components will look very similar. The past few challenges focused on components and composition, so the rendering was done for you behind the scenes. However, none of the React code you write will render to the DOM without making a call to the ReactDOM API.
|
||||
|
||||
Here's a refresher on the syntax: `ReactDOM.render(componentToRender, targetNode)`. The first argument is the React component that you want to render. The second argument is the DOM node that you want to render that component within.
|
||||
|
||||
React components are passed into `ReactDOM.render()` a little differently than JSX elements. For JSX elements, you pass in the name of the element that you want to render. However, for React components, you need to use the same syntax as if you were rendering a nested component, for example `ReactDOM.render(<ComponentToRender />, targetNode)`. You use this syntax for both ES6 class components and functional components.
|
||||
|
||||
# --instructions--
|
||||
|
||||
Both the `Fruits` and `Vegetables` components are defined for you behind the scenes. Render both components as children of the `TypesOfFood` component, then render `TypesOfFood` to the DOM. There is a `div` with `id='challenge-node'` available for you to use.
|
||||
|
||||
# --hints--
|
||||
|
||||
The `TypesOfFood` component should return a single `div` element.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
|
||||
return mockedComponent.children().type() === 'div';
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `TypesOfFood` component should render the `Fruits` component after the `h1` element.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
|
||||
return mockedComponent.children().childAt(1).name() === 'Fruits';
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `TypesOfFood` component should render the `Vegetables` component after `Fruits`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood));
|
||||
return mockedComponent.children().childAt(2).name() === 'Vegetables';
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `TypesOfFood` component should render to the DOM within the `div` with the id `challenge-node`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const html = document.getElementById('challenge-node').childNodes[0]
|
||||
.innerHTML;
|
||||
return (
|
||||
html.includes(
|
||||
'<div><h2>Fruits:</h2><h4>Non-Citrus:</h4><ul><li>Apples</li><li>Blueberries</li><li>Strawberries</li><li>Bananas</li></ul><h4>Citrus:</h4><ul><li>Lemon</li><li>Lime</li><li>Orange</li><li>Grapefruit</li></ul></div>'
|
||||
) &&
|
||||
html.includes(
|
||||
'<div><h2>Vegetables:</h2><ul><li>Brussel Sprouts</li><li>Broccoli</li><li>Squash</li></ul></div>'
|
||||
)
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --before-user-code--
|
||||
|
||||
```jsx
|
||||
const Fruits = () => {
|
||||
return (
|
||||
<div>
|
||||
<h2>Fruits:</h2>
|
||||
<h4>Non-Citrus:</h4>
|
||||
<ul>
|
||||
<li>Apples</li>
|
||||
<li>Blueberries</li>
|
||||
<li>Strawberries</li>
|
||||
<li>Bananas</li>
|
||||
</ul>
|
||||
<h4>Citrus:</h4>
|
||||
<ul>
|
||||
<li>Lemon</li>
|
||||
<li>Lime</li>
|
||||
<li>Orange</li>
|
||||
<li>Grapefruit</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
const Vegetables = () => {
|
||||
return (
|
||||
<div>
|
||||
<h2>Vegetables:</h2>
|
||||
<ul>
|
||||
<li>Brussel Sprouts</li>
|
||||
<li>Broccoli</li>
|
||||
<li>Squash</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class TypesOfFood extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Types of Food:</h1>
|
||||
{/* Change code below this line */}
|
||||
|
||||
{/* Change code above this line */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Change code below this line
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class TypesOfFood extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Types of Food:</h1>
|
||||
{/* Change code below this line */}
|
||||
<Fruits />
|
||||
<Vegetables />
|
||||
{/* Change code above this line */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Change code below this line
|
||||
ReactDOM.render(<TypesOfFood />, document.getElementById('challenge-node'));
|
||||
```
|
@ -0,0 +1,303 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036188
|
||||
title: Render Conditionally from Props
|
||||
challengeType: 6
|
||||
forumTopicId: 301405
|
||||
dashedName: render-conditionally-from-props
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
So far, you've seen how to use `if/else`, `&&`, and the ternary operator (`condition ? expressionIfTrue : expressionIfFalse`) to make conditional decisions about what to render and when. However, there's one important topic left to discuss that lets you combine any or all of these concepts with another powerful React feature: props. Using props to conditionally render code is very common with React developers — that is, they use the value of a given prop to automatically make decisions about what to render.
|
||||
|
||||
In this challenge, you'll set up a child component to make rendering decisions based on props. You'll also use the ternary operator, but you can see how several of the other concepts that were covered in the last few challenges might be just as useful in this context.
|
||||
|
||||
# --instructions--
|
||||
|
||||
The code editor has two components that are partially defined for you: a parent called `GameOfChance`, and a child called `Results`. They are used to create a simple game where the user presses a button to see if they win or lose.
|
||||
|
||||
First, you'll need a simple expression that randomly returns a different value every time it is run. You can use `Math.random()`. This method returns a value between `0` (inclusive) and `1` (exclusive) each time it is called. So for 50/50 odds, use `Math.random() >= .5` in your expression. Statistically speaking, this expression will return `true` 50% of the time, and `false` the other 50%. In the render method, replace `null` with the above expression to complete the variable declaration.
|
||||
|
||||
Now you have an expression that you can use to make a randomized decision in the code. Next you need to implement this. Render the `Results` component as a child of `GameOfChance`, and pass in `expression` as a prop called `fiftyFifty`. In the `Results` component, write a ternary expression to render the `h1` element with the text `You Win!` or `You Lose!` based on the `fiftyFifty` prop that's being passed in from `GameOfChance`. Finally, make sure the `handleClick()` method is correctly counting each turn so the user knows how many times they've played. This also serves to let the user know the component has actually updated in case they win or lose twice in a row.
|
||||
|
||||
# --hints--
|
||||
|
||||
The `GameOfChance` component should exist and render to the page.
|
||||
|
||||
```js
|
||||
assert.strictEqual(
|
||||
Enzyme.mount(React.createElement(GameOfChance)).find('GameOfChance').length,
|
||||
1
|
||||
);
|
||||
```
|
||||
|
||||
`GameOfChance` should return a single `button` element.
|
||||
|
||||
```js
|
||||
assert.strictEqual(
|
||||
Enzyme.mount(React.createElement(GameOfChance)).find('button').length,
|
||||
1
|
||||
);
|
||||
```
|
||||
|
||||
`GameOfChance` should return a single instance of the `Results` component, which has a prop called `fiftyFifty`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
Enzyme.mount(React.createElement(GameOfChance)).find('Results').length ===
|
||||
1 &&
|
||||
Enzyme.mount(React.createElement(GameOfChance))
|
||||
.find('Results')
|
||||
.props()
|
||||
.hasOwnProperty('fiftyFifty') === true
|
||||
);
|
||||
```
|
||||
|
||||
`GameOfChance` state should be initialized with a property of `counter` set to a value of `1`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(
|
||||
Enzyme.mount(React.createElement(GameOfChance)).state().counter,
|
||||
1
|
||||
);
|
||||
```
|
||||
|
||||
When the `GameOfChance` component is first rendered to the DOM, a `p` element should be returned with the inner text of `Turn: 1`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(
|
||||
Enzyme.mount(React.createElement(GameOfChance)).find('p').text(),
|
||||
'Turn: 1'
|
||||
);
|
||||
```
|
||||
|
||||
Each time the button is clicked, the counter state should be incremented by a value of 1, and a single `p` element should be rendered to the DOM that contains the text `Turn: N`, where `N` is the value of the counter state.
|
||||
|
||||
```js
|
||||
(() => {
|
||||
const comp = Enzyme.mount(React.createElement(GameOfChance));
|
||||
const simulate = () => {
|
||||
comp.find('button').simulate('click');
|
||||
};
|
||||
const result = () => ({
|
||||
count: comp.state('counter'),
|
||||
text: comp.find('p').text()
|
||||
});
|
||||
const _1 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _2 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _3 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _4 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _5 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _1_val = _1();
|
||||
const _2_val = _2();
|
||||
const _3_val = _3();
|
||||
const _4_val = _4();
|
||||
const _5_val = _5();
|
||||
assert(
|
||||
_1_val.count === 2 &&
|
||||
_1_val.text === 'Turn: 2' &&
|
||||
_2_val.count === 3 &&
|
||||
_2_val.text === 'Turn: 3' &&
|
||||
_3_val.count === 4 &&
|
||||
_3_val.text === 'Turn: 4' &&
|
||||
_4_val.count === 5 &&
|
||||
_4_val.text === 'Turn: 5' &&
|
||||
_5_val.count === 6 &&
|
||||
_5_val.text === 'Turn: 6'
|
||||
);
|
||||
})();
|
||||
```
|
||||
|
||||
When the `GameOfChance` component is first mounted to the DOM and each time the button is clicked thereafter, a single `h1` element should be returned that randomly renders either `You Win!` or `You Lose!`.
|
||||
|
||||
```js
|
||||
(() => {
|
||||
const comp = Enzyme.mount(React.createElement(GameOfChance));
|
||||
const simulate = () => {
|
||||
comp.find('button').simulate('click');
|
||||
};
|
||||
const result = () => ({
|
||||
h1: comp.find('h1').length,
|
||||
text: comp.find('h1').text()
|
||||
});
|
||||
const _1 = result();
|
||||
const _2 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _3 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _4 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _5 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _6 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _7 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _8 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _9 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _10 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _2_val = _2();
|
||||
const _3_val = _3();
|
||||
const _4_val = _4();
|
||||
const _5_val = _5();
|
||||
const _6_val = _6();
|
||||
const _7_val = _7();
|
||||
const _8_val = _8();
|
||||
const _9_val = _9();
|
||||
const _10_val = _10();
|
||||
const __text = new Set([
|
||||
_1.text,
|
||||
_2_val.text,
|
||||
_3_val.text,
|
||||
_4_val.text,
|
||||
_5_val.text,
|
||||
_6_val.text,
|
||||
_7_val.text,
|
||||
_8_val.text,
|
||||
_9_val.text,
|
||||
_10_val.text
|
||||
]);
|
||||
const __h1 = new Set([
|
||||
_1.h1,
|
||||
_2_val.h1,
|
||||
_3_val.h1,
|
||||
_4_val.h1,
|
||||
_5_val.h1,
|
||||
_6_val.h1,
|
||||
_7_val.h1,
|
||||
_8_val.h1,
|
||||
_9_val.h1,
|
||||
_10_val.h1
|
||||
]);
|
||||
assert(__text.size === 2 && __h1.size === 1);
|
||||
})();
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<GameOfChance />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class Results extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
{/* Change code below this line */}
|
||||
return <h1></h1>;
|
||||
{/* Change code above this line */}
|
||||
}
|
||||
}
|
||||
|
||||
class GameOfChance extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
counter: 1
|
||||
};
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
}
|
||||
handleClick() {
|
||||
this.setState(prevState => {
|
||||
// Complete the return statement:
|
||||
return {
|
||||
counter: prevState
|
||||
}
|
||||
});
|
||||
}
|
||||
render() {
|
||||
const expression = null; // Change this line
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.handleClick}>Play Again</button>
|
||||
{/* Change code below this line */}
|
||||
|
||||
{/* Change code above this line */}
|
||||
<p>{'Turn: ' + this.state.counter}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class Results extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return <h1>{this.props.fiftyFifty ? 'You Win!' : 'You Lose!'}</h1>;
|
||||
}
|
||||
}
|
||||
|
||||
class GameOfChance extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
counter: 1
|
||||
};
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
}
|
||||
handleClick() {
|
||||
this.setState(prevState => {
|
||||
return {
|
||||
counter: prevState.counter + 1
|
||||
}
|
||||
});
|
||||
}
|
||||
render() {
|
||||
const expression = Math.random() >= 0.5;
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.handleClick}>Play Again</button>
|
||||
<Results fiftyFifty={expression} />
|
||||
<p>{'Turn: ' + this.state.counter}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
@ -0,0 +1,75 @@
|
||||
---
|
||||
id: 5a24bbe0dba28a8d3cbd4c5f
|
||||
title: Render HTML Elements to the DOM
|
||||
challengeType: 6
|
||||
forumTopicId: 301406
|
||||
dashedName: render-html-elements-to-the-dom
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
So far, you've learned that JSX is a convenient tool to write readable HTML within JavaScript. With React, we can render this JSX directly to the HTML DOM using React's rendering API known as ReactDOM.
|
||||
|
||||
ReactDOM offers a simple method to render React elements to the DOM which looks like this: `ReactDOM.render(componentToRender, targetNode)`, where the first argument is the React element or component that you want to render, and the second argument is the DOM node that you want to render the component to.
|
||||
|
||||
As you would expect, `ReactDOM.render()` must be called after the JSX element declarations, just like how you must declare variables before using them.
|
||||
|
||||
# --instructions--
|
||||
|
||||
The code editor has a simple JSX component. Use the `ReactDOM.render()` method to render this component to the page. You can pass defined JSX elements directly in as the first argument and use `document.getElementById()` to select the DOM node to render them to. There is a `div` with `id='challenge-node'` available for you to use. Make sure you don't change the `JSX` constant.
|
||||
|
||||
# --hints--
|
||||
|
||||
The constant `JSX` should return a `div` element.
|
||||
|
||||
```js
|
||||
assert(JSX.type === 'div');
|
||||
```
|
||||
|
||||
The `div` should contain an `h1` tag as the first element.
|
||||
|
||||
```js
|
||||
assert(JSX.props.children[0].type === 'h1');
|
||||
```
|
||||
|
||||
The `div` should contain a `p` tag as the second element.
|
||||
|
||||
```js
|
||||
assert(JSX.props.children[1].type === 'p');
|
||||
```
|
||||
|
||||
The provided JSX element should render to the DOM node with id `challenge-node`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
document.getElementById('challenge-node').childNodes[0].innerHTML ===
|
||||
'<h1>Hello World</h1><p>Lets render this to the DOM</p>'
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
const JSX = (
|
||||
<div>
|
||||
<h1>Hello World</h1>
|
||||
<p>Lets render this to the DOM</p>
|
||||
</div>
|
||||
);
|
||||
// Change code below this line
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
const JSX = (
|
||||
<div>
|
||||
<h1>Hello World</h1>
|
||||
<p>Lets render this to the DOM</p>
|
||||
</div>
|
||||
);
|
||||
// Change code below this line
|
||||
ReactDOM.render(JSX, document.getElementById('challenge-node'));
|
||||
```
|
@ -0,0 +1,76 @@
|
||||
---
|
||||
id: 5a24c314108439a4d403618d
|
||||
title: Render React on the Server with renderToString
|
||||
challengeType: 6
|
||||
forumTopicId: 301407
|
||||
dashedName: render-react-on-the-server-with-rendertostring
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
So far, you have been rendering React components on the client. Normally, this is what you will always do. However, there are some use cases where it makes sense to render a React component on the server. Since React is a JavaScript view library and you can run JavaScript on the server with Node, this is possible. In fact, React provides a `renderToString()` method you can use for this purpose.
|
||||
|
||||
There are two key reasons why rendering on the server may be used in a real world app. First, without doing this, your React apps would consist of a relatively empty HTML file and a large bundle of JavaScript when it's initially loaded to the browser. This may not be ideal for search engines that are trying to index the content of your pages so people can find you. If you render the initial HTML markup on the server and send this to the client, the initial page load contains all of the page's markup which can be crawled by search engines. Second, this creates a faster initial page load experience because the rendered HTML is smaller than the JavaScript code of the entire app. React will still be able to recognize your app and manage it after the initial load.
|
||||
|
||||
# --instructions--
|
||||
|
||||
The `renderToString()` method is provided on `ReactDOMServer`, which is available here as a global object. The method takes one argument which is a React element. Use this to render `App` to a string.
|
||||
|
||||
# --hints--
|
||||
|
||||
The `App` component should render to a string using `ReactDOMServer.renderToString`.
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
assert(
|
||||
getUserInput('index')
|
||||
.replace(/ /g, '')
|
||||
.includes('ReactDOMServer.renderToString(<App/>)') &&
|
||||
Enzyme.mount(React.createElement(App)).children().name() === 'div'
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --before-user-code--
|
||||
|
||||
```jsx
|
||||
var ReactDOMServer = { renderToString(x) { return null; } };
|
||||
```
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<App />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class App extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return <div/>
|
||||
}
|
||||
};
|
||||
|
||||
// Change code below this line
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class App extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return <div/>
|
||||
}
|
||||
};
|
||||
|
||||
// Change code below this line
|
||||
ReactDOMServer.renderToString(<App/>);
|
||||
```
|
@ -0,0 +1,119 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036172
|
||||
title: Render State in the User Interface Another Way
|
||||
challengeType: 6
|
||||
forumTopicId: 301408
|
||||
dashedName: render-state-in-the-user-interface-another-way
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
There is another way to access `state` in a component. In the `render()` method, before the `return` statement, you can write JavaScript directly. For example, you could declare functions, access data from `state` or `props`, perform computations on this data, and so on. Then, you can assign any data to variables, which you have access to in the `return` statement.
|
||||
|
||||
# --instructions--
|
||||
|
||||
In the `MyComponent` render method, define a `const` called `name` and set it equal to the name value in the component's `state`. Because you can write JavaScript directly in this part of the code, you don't have to enclose this reference in curly braces.
|
||||
|
||||
Next, in the return statement, render this value in an `h1` tag using the variable `name`. Remember, you need to use the JSX syntax (curly braces for JavaScript) in the return statement.
|
||||
|
||||
# --hints--
|
||||
|
||||
`MyComponent` should have a key `name` with value `freeCodeCamp` stored in its state.
|
||||
|
||||
```js
|
||||
assert(
|
||||
Enzyme.mount(React.createElement(MyComponent)).state('name') ===
|
||||
'freeCodeCamp'
|
||||
);
|
||||
```
|
||||
|
||||
`MyComponent` should render an `h1` header enclosed in a single `div`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
/<div><h1>.*<\/h1><\/div>/.test(
|
||||
Enzyme.mount(React.createElement(MyComponent)).html()
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
The rendered `h1` tag should include a reference to `{name}`.
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
assert(/<h1>\n*\s*\{\s*name\s*\}\s*\n*<\/h1>/.test(getUserInput('index')));
|
||||
```
|
||||
|
||||
The rendered `h1` header should contain text rendered from the component's state.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const waitForIt = (fn) =>
|
||||
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
const first = () => {
|
||||
mockedComponent.setState({ name: 'TestName' });
|
||||
return waitForIt(() => mockedComponent.html());
|
||||
};
|
||||
const firstValue = await first();
|
||||
assert(firstValue === '<div><h1>TestName</h1></div>');
|
||||
};
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<MyComponent />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
name: 'freeCodeCamp'
|
||||
}
|
||||
}
|
||||
render() {
|
||||
// Change code below this line
|
||||
|
||||
// Change code above this line
|
||||
return (
|
||||
<div>
|
||||
{ /* Change code below this line */ }
|
||||
|
||||
{ /* Change code above this line */ }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
name: 'freeCodeCamp'
|
||||
}
|
||||
}
|
||||
render() {
|
||||
// Change code below this line
|
||||
const name = this.state.name;
|
||||
// Change code above this line
|
||||
return (
|
||||
<div>
|
||||
{ /* Change code below this line */ }
|
||||
<h1>{name}</h1>
|
||||
{ /* Change code above this line */ }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,113 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036171
|
||||
title: Render State in the User Interface
|
||||
challengeType: 6
|
||||
forumTopicId: 301409
|
||||
dashedName: render-state-in-the-user-interface
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Once you define a component's initial state, you can display any part of it in the UI that is rendered. If a component is stateful, it will always have access to the data in `state` in its `render()` method. You can access the data with `this.state`.
|
||||
|
||||
If you want to access a state value within the `return` of the render method, you have to enclose the value in curly braces.
|
||||
|
||||
`state` is one of the most powerful features of components in React. It allows you to track important data in your app and render a UI in response to changes in this data. If your data changes, your UI will change. React uses what is called a virtual DOM, to keep track of changes behind the scenes. When state data updates, it triggers a re-render of the components using that data - including child components that received the data as a prop. React updates the actual DOM, but only where necessary. This means you don't have to worry about changing the DOM. You simply declare what the UI should look like.
|
||||
|
||||
Note that if you make a component stateful, no other components are aware of its `state`. Its `state` is completely encapsulated, or local to that component, unless you pass state data to a child component as `props`. This notion of encapsulated `state` is very important because it allows you to write certain logic, then have that logic contained and isolated in one place in your code.
|
||||
|
||||
# --instructions--
|
||||
|
||||
In the code editor, `MyComponent` is already stateful. Define an `h1` tag in the component's render method which renders the value of `name` from the component's state.
|
||||
|
||||
**Note:** The `h1` should only render the value from `state` and nothing else. In JSX, any code you write with curly braces `{ }` will be treated as JavaScript. So to access the value from `state` just enclose the reference in curly braces.
|
||||
|
||||
# --hints--
|
||||
|
||||
`MyComponent` should have a key `name` with value `freeCodeCamp` stored in its state.
|
||||
|
||||
```js
|
||||
assert(
|
||||
Enzyme.mount(React.createElement(MyComponent)).state('name') ===
|
||||
'freeCodeCamp'
|
||||
);
|
||||
```
|
||||
|
||||
`MyComponent` should render an `h1` header enclosed in a single `div`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
/<div><h1>.*<\/h1><\/div>/.test(
|
||||
Enzyme.mount(React.createElement(MyComponent)).html()
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
The rendered `h1` header should only contain text rendered from the component's state.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const waitForIt = (fn) =>
|
||||
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
const first = () => {
|
||||
mockedComponent.setState({ name: 'TestName' });
|
||||
return waitForIt(() => mockedComponent.html());
|
||||
};
|
||||
const firstValue = await first();
|
||||
const getValue = firstValue.replace(/\s/g, '');
|
||||
assert(getValue === '<div><h1>TestName</h1></div>');
|
||||
};
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<MyComponent />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
name: 'freeCodeCamp'
|
||||
}
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{ /* Change code below this line */ }
|
||||
|
||||
{ /* Change code above this line */ }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
name: 'freeCodeCamp'
|
||||
}
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{ /* Change code below this line */ }
|
||||
<h1>{this.state.name}</h1>
|
||||
{ /* Change code above this line */ }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,155 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036184
|
||||
title: Render with an If-Else Condition
|
||||
challengeType: 6
|
||||
forumTopicId: 301410
|
||||
dashedName: render-with-an-if-else-condition
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Another application of using JavaScript to control your rendered view is to tie the elements that are rendered to a condition. When the condition is true, one view renders. When it's false, it's a different view. You can do this with a standard `if/else` statement in the `render()` method of a React component.
|
||||
|
||||
# --instructions--
|
||||
|
||||
MyComponent contains a `boolean` in its state which tracks whether you want to display some element in the UI or not. The `button` toggles the state of this value. Currently, it renders the same UI every time. Rewrite the `render()` method with an `if/else` statement so that if `display` is `true`, you return the current markup. Otherwise, return the markup without the `h1` element.
|
||||
|
||||
**Note:** You must write an `if/else` to pass the tests. Use of the ternary operator will not pass here.
|
||||
|
||||
# --hints--
|
||||
|
||||
`MyComponent` should exist and render.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
return mockedComponent.find('MyComponent').length === 1;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
When `display` is set to `true`, a `div`, `button`, and `h1` should render.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const waitForIt = (fn) =>
|
||||
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
const state_1 = () => {
|
||||
mockedComponent.setState({ display: true });
|
||||
return waitForIt(() => mockedComponent);
|
||||
};
|
||||
const updated = await state_1();
|
||||
assert(
|
||||
mockedComponent.find('div').length === 1 &&
|
||||
mockedComponent.find('div').children().length === 2 &&
|
||||
mockedComponent.find('button').length === 1 &&
|
||||
mockedComponent.find('h1').length === 1
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
When `display` is set to `false`, only a `div` and `button` should render.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const waitForIt = (fn) =>
|
||||
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
const state_1 = () => {
|
||||
mockedComponent.setState({ display: false });
|
||||
return waitForIt(() => mockedComponent);
|
||||
};
|
||||
const updated = await state_1();
|
||||
assert(
|
||||
mockedComponent.find('div').length === 1 &&
|
||||
mockedComponent.find('div').children().length === 1 &&
|
||||
mockedComponent.find('button').length === 1 &&
|
||||
mockedComponent.find('h1').length === 0
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
The render method should use an `if/else` statement to check the condition of `this.state.display`.
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
assert(
|
||||
getUserInput('index').includes('if') &&
|
||||
getUserInput('index').includes('else')
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<MyComponent />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
display: true
|
||||
}
|
||||
this.toggleDisplay = this.toggleDisplay.bind(this);
|
||||
}
|
||||
toggleDisplay() {
|
||||
this.setState((state) => ({
|
||||
display: !state.display
|
||||
}));
|
||||
}
|
||||
render() {
|
||||
// Change code below this line
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.toggleDisplay}>Toggle Display</button>
|
||||
<h1>Displayed!</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
display: true
|
||||
}
|
||||
this.toggleDisplay = this.toggleDisplay.bind(this);
|
||||
}
|
||||
toggleDisplay() {
|
||||
this.setState((state) => ({
|
||||
display: !state.display
|
||||
}));
|
||||
}
|
||||
render() {
|
||||
// Change code below this line
|
||||
if (this.state.display) {
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.toggleDisplay}>Toggle Display</button>
|
||||
<h1>Displayed!</h1>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.toggleDisplay}>Toggle Display</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,145 @@
|
||||
---
|
||||
id: 5a24c314108439a4d403616f
|
||||
title: Review Using Props with Stateless Functional Components
|
||||
challengeType: 6
|
||||
forumTopicId: 301411
|
||||
dashedName: review-using-props-with-stateless-functional-components
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Except for the last challenge, you've been passing props to stateless functional components. These components act like pure functions. They accept props as input and return the same view every time they are passed the same props. You may be wondering what state is, and the next challenge will cover it in more detail. Before that, here's a review of the terminology for components.
|
||||
|
||||
A *stateless functional component* is any function you write which accepts props and returns JSX. A *stateless component*, on the other hand, is a class that extends `React.Component`, but does not use internal state (covered in the next challenge). Finally, a *stateful component* is a class component that does maintain its own internal state. You may see stateful components referred to simply as components or React components.
|
||||
|
||||
A common pattern is to try to minimize statefulness and to create stateless functional components wherever possible. This helps contain your state management to a specific area of your application. In turn, this improves development and maintenance of your app by making it easier to follow how changes to state affect its behavior.
|
||||
|
||||
# --instructions--
|
||||
|
||||
The code editor has a `CampSite` component that renders a `Camper` component as a child. Define the `Camper` component and assign it default props of `{ name: 'CamperBot' }`. Inside the `Camper` component, render any code that you want, but make sure to have one `p` element that includes only the `name` value that is passed in as a `prop`. Finally, define `propTypes` on the `Camper` component to require `name` to be provided as a prop and verify that it is of type `string`.
|
||||
|
||||
# --hints--
|
||||
|
||||
The `CampSite` component should render.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(CampSite));
|
||||
return mockedComponent.find('CampSite').length === 1;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `Camper` component should render.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(CampSite));
|
||||
return mockedComponent.find('Camper').length === 1;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `Camper` component should include default props which assign the string `CamperBot` to the key `name`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
/Camper.defaultProps={name:(['"`])CamperBot\1,?}/.test(
|
||||
__helpers.removeWhiteSpace(code)
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
The `Camper` component should include prop types which require the `name` prop to be of type `string`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
/Camper.propTypes={name:PropTypes.string.isRequired,?}/.test(
|
||||
__helpers.removeWhiteSpace(code)
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
The `Camper` component should contain a `p` element with only the text from the `name` prop.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(CampSite));
|
||||
return (
|
||||
mockedComponent.find('p').text() ===
|
||||
mockedComponent.find('Camper').props().name
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --before-user-code--
|
||||
|
||||
```jsx
|
||||
var PropTypes = {
|
||||
string: { isRequired: true }
|
||||
};
|
||||
```
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<CampSite />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class CampSite extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Camper/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
// Change code below this line
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class CampSite extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Camper/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
// Change code below this line
|
||||
|
||||
const Camper = (props) => {
|
||||
return (
|
||||
<div>
|
||||
<p>{props.name}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Camper.propTypes = {
|
||||
name: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
Camper.defaultProps = {
|
||||
name: 'CamperBot'
|
||||
};
|
||||
```
|
@ -0,0 +1,143 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036173
|
||||
title: Set State with this.setState
|
||||
challengeType: 6
|
||||
forumTopicId: 301412
|
||||
dashedName: set-state-with-this-setstate
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The previous challenges covered component `state` and how to initialize state in the `constructor`. There is also a way to change the component's `state`. React provides a method for updating component `state` called `setState`. You call the `setState` method within your component class like so: `this.setState()`, passing in an object with key-value pairs. The keys are your state properties and the values are the updated state data. For instance, if we were storing a `username` in state and wanted to update it, it would look like this:
|
||||
|
||||
```jsx
|
||||
this.setState({
|
||||
username: 'Lewis'
|
||||
});
|
||||
```
|
||||
|
||||
React expects you to never modify `state` directly, instead always use `this.setState()` when state changes occur. Also, you should note that React may batch multiple state updates in order to improve performance. What this means is that state updates through the `setState` method can be asynchronous. There is an alternative syntax for the `setState` method which provides a way around this problem. This is rarely needed but it's good to keep it in mind! Please consult the [React documentation](https://facebook.github.io/react/docs/state-and-lifecycle.html) for further details.
|
||||
|
||||
# --instructions--
|
||||
|
||||
There is a `button` element in the code editor which has an `onClick()` handler. This handler is triggered when the `button` receives a click event in the browser, and runs the `handleClick` method defined on `MyComponent`. Within the `handleClick` method, update the component `state` using `this.setState()`. Set the `name` property in `state` to equal the string `React Rocks!`.
|
||||
|
||||
Click the button and watch the rendered state update. Don't worry if you don't fully understand how the click handler code works at this point. It's covered in upcoming challenges.
|
||||
|
||||
# --hints--
|
||||
|
||||
The state of `MyComponent` should initialize with the key value pair `{ name: Initial State }`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
Enzyme.mount(React.createElement(MyComponent)).state('name') ===
|
||||
'Initial State'
|
||||
);
|
||||
```
|
||||
|
||||
`MyComponent` should render an `h1` header.
|
||||
|
||||
```js
|
||||
assert(Enzyme.mount(React.createElement(MyComponent)).find('h1').length === 1);
|
||||
```
|
||||
|
||||
The rendered `h1` header should contain text rendered from the component's state.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const waitForIt = (fn) =>
|
||||
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
const first = () => {
|
||||
mockedComponent.setState({ name: 'TestName' });
|
||||
return waitForIt(() => mockedComponent.html());
|
||||
};
|
||||
const firstValue = await first();
|
||||
assert(/<h1>TestName<\/h1>/.test(firstValue));
|
||||
};
|
||||
```
|
||||
|
||||
Calling the `handleClick` method on `MyComponent` should set the name property in state to equal `React Rocks!`.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const waitForIt = (fn) =>
|
||||
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
const first = () => {
|
||||
mockedComponent.setState({ name: 'Before' });
|
||||
return waitForIt(() => mockedComponent.state('name'));
|
||||
};
|
||||
const second = () => {
|
||||
mockedComponent.instance().handleClick();
|
||||
return waitForIt(() => mockedComponent.state('name'));
|
||||
};
|
||||
const firstValue = await first();
|
||||
const secondValue = await second();
|
||||
assert(firstValue === 'Before' && secondValue === 'React Rocks!');
|
||||
};
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<MyComponent />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
name: 'Initial State'
|
||||
};
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
}
|
||||
handleClick() {
|
||||
// Change code below this line
|
||||
|
||||
// Change code above this line
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.handleClick}>Click Me</button>
|
||||
<h1>{this.state.name}</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
name: 'Initial State'
|
||||
};
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
}
|
||||
handleClick() {
|
||||
// Change code below this line
|
||||
this.setState({
|
||||
name: 'React Rocks!'
|
||||
});
|
||||
// Change code above this line
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<button onClick = {this.handleClick}>Click Me</button>
|
||||
<h1>{this.state.name}</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,146 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036185
|
||||
title: Use && for a More Concise Conditional
|
||||
challengeType: 6
|
||||
forumTopicId: 301413
|
||||
dashedName: use--for-a-more-concise-conditional
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The `if/else` statements worked in the last challenge, but there's a more concise way to achieve the same result. Imagine that you are tracking several conditions in a component and you want different elements to render depending on each of these conditions. If you write a lot of `else if` statements to return slightly different UIs, you may repeat code which leaves room for error. Instead, you can use the `&&` logical operator to perform conditional logic in a more concise way. This is possible because you want to check if a condition is `true`, and if it is, return some markup. Here's an example:
|
||||
|
||||
```jsx
|
||||
{condition && <p>markup</p>}
|
||||
```
|
||||
|
||||
If the `condition` is `true`, the markup will be returned. If the condition is `false`, the operation will immediately return `false` after evaluating the `condition` and return nothing. You can include these statements directly in your JSX and string multiple conditions together by writing `&&` after each one. This allows you to handle more complex conditional logic in your `render()` method without repeating a lot of code.
|
||||
|
||||
# --instructions--
|
||||
|
||||
Solve the previous example again, so the `h1` only renders if `display` is `true`, but use the `&&` logical operator instead of an `if/else` statement.
|
||||
|
||||
# --hints--
|
||||
|
||||
`MyComponent` should exist and render.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
return mockedComponent.find('MyComponent').length;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
When `display` is set to `true`, a `div`, `button`, and `h1` should render.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const waitForIt = (fn) =>
|
||||
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
const state_1 = () => {
|
||||
mockedComponent.setState({ display: true });
|
||||
return waitForIt(() => mockedComponent);
|
||||
};
|
||||
const updated = await state_1();
|
||||
assert(
|
||||
updated.find('div').length === 1 &&
|
||||
updated.find('div').children().length === 2 &&
|
||||
updated.find('button').length === 1 &&
|
||||
updated.find('h1').length === 1
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
When `display` is set to `false`, only a `div` and `button` should render.
|
||||
|
||||
```js
|
||||
async () => {
|
||||
const waitForIt = (fn) =>
|
||||
new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250));
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
const state_1 = () => {
|
||||
mockedComponent.setState({ display: false });
|
||||
return waitForIt(() => mockedComponent);
|
||||
};
|
||||
const updated = await state_1();
|
||||
assert(
|
||||
updated.find('div').length === 1 &&
|
||||
updated.find('div').children().length === 1 &&
|
||||
updated.find('button').length === 1 &&
|
||||
updated.find('h1').length === 0
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
The render method should use the `&&` logical operator to check the condition of `this.state.display`.
|
||||
|
||||
```js
|
||||
(getUserInput) => assert(getUserInput('index').includes('&&'));
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<MyComponent />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
display: true
|
||||
}
|
||||
this.toggleDisplay = this.toggleDisplay.bind(this);
|
||||
}
|
||||
toggleDisplay() {
|
||||
this.setState(state => ({
|
||||
display: !state.display
|
||||
}));
|
||||
}
|
||||
render() {
|
||||
// Change code below this line
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.toggleDisplay}>Toggle Display</button>
|
||||
<h1>Displayed!</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
display: true
|
||||
}
|
||||
this.toggleDisplay = this.toggleDisplay.bind(this);
|
||||
}
|
||||
toggleDisplay() {
|
||||
this.setState(state => ({
|
||||
display: !state.display
|
||||
}));
|
||||
}
|
||||
render() {
|
||||
// Change code below this line
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.toggleDisplay}>Toggle Display</button>
|
||||
{this.state.display && <h1>Displayed!</h1>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,280 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036187
|
||||
title: Use a Ternary Expression for Conditional Rendering
|
||||
challengeType: 6
|
||||
forumTopicId: 301414
|
||||
dashedName: use-a-ternary-expression-for-conditional-rendering
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Before moving on to dynamic rendering techniques, there's one last way to use built-in JavaScript conditionals to render what you want: the <dfn>ternary operator</dfn>. The ternary operator is often utilized as a shortcut for `if/else` statements in JavaScript. They're not quite as robust as traditional `if/else` statements, but they are very popular among React developers. One reason for this is because of how JSX is compiled, `if/else` statements can't be inserted directly into JSX code. You might have noticed this a couple challenges ago — when an `if/else` statement was required, it was always *outside* the `return` statement. Ternary expressions can be an excellent alternative if you want to implement conditional logic within your JSX. Recall that a ternary operator has three parts, but you can combine several ternary expressions together. Here's the basic syntax:
|
||||
|
||||
```jsx
|
||||
condition ? expressionIfTrue : expressionIfFalse;
|
||||
```
|
||||
|
||||
# --instructions--
|
||||
|
||||
The code editor has three constants defined within the `CheckUserAge` component's `render()` method. They are called `buttonOne`, `buttonTwo`, and `buttonThree`. Each of these is assigned a simple JSX expression representing a button element. First, initialize the state of `CheckUserAge` with `input` and `userAge` both set to values of an empty string.
|
||||
|
||||
Once the component is rendering information to the page, users should have a way to interact with it. Within the component's `return` statement, set up a ternary expression that implements the following logic: when the page first loads, render the submit button, `buttonOne`, to the page. Then, when a user enters their age and clicks the button, render a different button based on the age. If a user enters a number less than `18`, render `buttonThree`. If a user enters a number greater than or equal to `18`, render `buttonTwo`.
|
||||
|
||||
# --hints--
|
||||
|
||||
The `CheckUserAge` component should render with a single `input` element and a single `button` element.
|
||||
|
||||
```js
|
||||
assert(
|
||||
Enzyme.mount(React.createElement(CheckUserAge)).find('div').find('input')
|
||||
.length === 1 &&
|
||||
Enzyme.mount(React.createElement(CheckUserAge)).find('div').find('button')
|
||||
.length === 1
|
||||
);
|
||||
```
|
||||
|
||||
The `CheckUserAge` component's state should be initialized with a property of `userAge` and a property of `input`, both set to a value of an empty string.
|
||||
|
||||
```js
|
||||
assert(
|
||||
Enzyme.mount(React.createElement(CheckUserAge)).state().input === '' &&
|
||||
Enzyme.mount(React.createElement(CheckUserAge)).state().userAge === ''
|
||||
);
|
||||
```
|
||||
|
||||
When the `CheckUserAge` component is first rendered to the DOM, the `button`'s inner text should be Submit.
|
||||
|
||||
```js
|
||||
assert(
|
||||
Enzyme.mount(React.createElement(CheckUserAge)).find('button').text() ===
|
||||
'Submit'
|
||||
);
|
||||
```
|
||||
|
||||
When a number of less than 18 is entered into the `input` element and the `button` is clicked, the `button`'s inner text should read `You Shall Not Pass`.
|
||||
|
||||
```js
|
||||
(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(CheckUserAge));
|
||||
const initialButton = mockedComponent.find('button').text();
|
||||
const enter3AndClickButton = () => {
|
||||
mockedComponent
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: '3' } });
|
||||
mockedComponent.find('button').simulate('click');
|
||||
mockedComponent.update();
|
||||
return mockedComponent.find('button').text();
|
||||
};
|
||||
const enter17AndClickButton = () => {
|
||||
mockedComponent
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: '17' } });
|
||||
mockedComponent.find('button').simulate('click');
|
||||
mockedComponent.update();
|
||||
return mockedComponent.find('button').text();
|
||||
};
|
||||
const userAge3 = enter3AndClickButton();
|
||||
const userAge17 = enter17AndClickButton();
|
||||
assert(
|
||||
initialButton === 'Submit' &&
|
||||
userAge3 === 'You Shall Not Pass' &&
|
||||
userAge17 === 'You Shall Not Pass'
|
||||
);
|
||||
})();
|
||||
```
|
||||
|
||||
When a number greater than or equal to 18 is entered into the `input` element and the `button` is clicked, the `button`'s inner text should read `You May Enter`.
|
||||
|
||||
```js
|
||||
(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(CheckUserAge));
|
||||
const initialButton = mockedComponent.find('button').text();
|
||||
const enter18AndClickButton = () => {
|
||||
mockedComponent
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: '18' } });
|
||||
mockedComponent.find('button').simulate('click');
|
||||
mockedComponent.update();
|
||||
return mockedComponent.find('button').text();
|
||||
};
|
||||
const enter35AndClickButton = () => {
|
||||
mockedComponent
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: '35' } });
|
||||
mockedComponent.find('button').simulate('click');
|
||||
mockedComponent.update();
|
||||
return mockedComponent.find('button').text();
|
||||
};
|
||||
const userAge18 = enter18AndClickButton();
|
||||
const userAge35 = enter35AndClickButton();
|
||||
assert(
|
||||
initialButton === 'Submit' &&
|
||||
userAge18 === 'You May Enter' &&
|
||||
userAge35 === 'You May Enter'
|
||||
);
|
||||
})();
|
||||
```
|
||||
|
||||
Once a number has been submitted, and the value of the `input` is once again changed, the `button` should return to reading `Submit`.
|
||||
|
||||
```js
|
||||
(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(CheckUserAge));
|
||||
const enter18AndClickButton = () => {
|
||||
mockedComponent
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: '18' } });
|
||||
mockedComponent.find('button').simulate('click');
|
||||
mockedComponent.update();
|
||||
return mockedComponent.find('button').text();
|
||||
};
|
||||
const changeInputDontClickButton = () => {
|
||||
mockedComponent
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: '5' } });
|
||||
mockedComponent.update();
|
||||
return mockedComponent.find('button').text();
|
||||
};
|
||||
const enter10AndClickButton = () => {
|
||||
mockedComponent
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: '10' } });
|
||||
mockedComponent.find('button').simulate('click');
|
||||
mockedComponent.update();
|
||||
return mockedComponent.find('button').text();
|
||||
};
|
||||
const userAge18 = enter18AndClickButton();
|
||||
const changeInput1 = changeInputDontClickButton();
|
||||
const userAge10 = enter10AndClickButton();
|
||||
const changeInput2 = changeInputDontClickButton();
|
||||
assert(
|
||||
userAge18 === 'You May Enter' &&
|
||||
changeInput1 === 'Submit' &&
|
||||
userAge10 === 'You Shall Not Pass' &&
|
||||
changeInput2 === 'Submit'
|
||||
);
|
||||
})();
|
||||
```
|
||||
|
||||
Your code should not contain any `if/else` statements.
|
||||
|
||||
```js
|
||||
assert(
|
||||
new RegExp(/(\s|;)if(\s|\()/).test(
|
||||
Enzyme.mount(React.createElement(CheckUserAge)).instance().render.toString()
|
||||
) === false
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<CheckUserAge />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
const inputStyle = {
|
||||
width: 235,
|
||||
margin: 5
|
||||
};
|
||||
|
||||
class CheckUserAge extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
// Change code below this line
|
||||
|
||||
// Change code above this line
|
||||
this.submit = this.submit.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
}
|
||||
handleChange(e) {
|
||||
this.setState({
|
||||
input: e.target.value,
|
||||
userAge: ''
|
||||
});
|
||||
}
|
||||
submit() {
|
||||
this.setState(state => ({
|
||||
userAge: state.input
|
||||
}));
|
||||
}
|
||||
render() {
|
||||
const buttonOne = <button onClick={this.submit}>Submit</button>;
|
||||
const buttonTwo = <button>You May Enter</button>;
|
||||
const buttonThree = <button>You Shall Not Pass</button>;
|
||||
return (
|
||||
<div>
|
||||
<h3>Enter Your Age to Continue</h3>
|
||||
<input
|
||||
style={inputStyle}
|
||||
type='number'
|
||||
value={this.state.input}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<br />
|
||||
{/* Change code below this line */}
|
||||
|
||||
{/* Change code above this line */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
const inputStyle = {
|
||||
width: 235,
|
||||
margin: 5
|
||||
};
|
||||
|
||||
class CheckUserAge extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
userAge: '',
|
||||
input: ''
|
||||
};
|
||||
this.submit = this.submit.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
}
|
||||
handleChange(e) {
|
||||
this.setState({
|
||||
input: e.target.value,
|
||||
userAge: ''
|
||||
});
|
||||
}
|
||||
submit() {
|
||||
this.setState(state => ({
|
||||
userAge: state.input
|
||||
}));
|
||||
}
|
||||
render() {
|
||||
const buttonOne = <button onClick={this.submit}>Submit</button>;
|
||||
const buttonTwo = <button>You May Enter</button>;
|
||||
const buttonThree = <button>You Shall Not Pass</button>;
|
||||
return (
|
||||
<div>
|
||||
<h3>Enter Your Age to Continue</h3>
|
||||
<input
|
||||
style={inputStyle}
|
||||
type='number'
|
||||
value={this.state.input}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<br />
|
||||
{this.state.userAge === ''
|
||||
? buttonOne
|
||||
: this.state.userAge >= 18
|
||||
? buttonTwo
|
||||
: buttonThree}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
@ -0,0 +1,334 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036183
|
||||
title: Use Advanced JavaScript in React Render Method
|
||||
challengeType: 6
|
||||
forumTopicId: 301415
|
||||
dashedName: use-advanced-javascript-in-react-render-method
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
In previous challenges, you learned how to inject JavaScript code into JSX code using curly braces, `{ }`, for tasks like accessing props, passing props, accessing state, inserting comments into your code, and most recently, styling your components. These are all common use cases to put JavaScript in JSX, but they aren't the only way that you can utilize JavaScript code in your React components.
|
||||
|
||||
You can also write JavaScript directly in your `render` methods, before the `return` statement, ***without*** inserting it inside of curly braces. This is because it is not yet within the JSX code. When you want to use a variable later in the JSX code *inside* the `return` statement, you place the variable name inside curly braces.
|
||||
|
||||
# --instructions--
|
||||
|
||||
In the code provided, the `render` method has an array that contains 20 phrases to represent the answers found in the classic 1980's Magic Eight Ball toy. The button click event is bound to the `ask` method, so each time the button is clicked a random number will be generated and stored as the `randomIndex` in state. On line 52, delete the string `change me!` and reassign the `answer` const so your code randomly accesses a different index of the `possibleAnswers` array each time the component updates. Finally, insert the `answer` const inside the `p` tags.
|
||||
|
||||
# --hints--
|
||||
|
||||
The `MagicEightBall` component should exist and should render to the page.
|
||||
|
||||
```js
|
||||
assert.strictEqual(
|
||||
Enzyme.mount(React.createElement(MagicEightBall)).find('MagicEightBall')
|
||||
.length,
|
||||
1
|
||||
);
|
||||
```
|
||||
|
||||
`MagicEightBall`'s first child should be an `input` element.
|
||||
|
||||
```js
|
||||
assert.strictEqual(
|
||||
Enzyme.mount(React.createElement(MagicEightBall))
|
||||
.children()
|
||||
.childAt(0)
|
||||
.name(),
|
||||
'input'
|
||||
);
|
||||
```
|
||||
|
||||
`MagicEightBall`'s third child should be a `button` element.
|
||||
|
||||
```js
|
||||
assert.strictEqual(
|
||||
Enzyme.mount(React.createElement(MagicEightBall))
|
||||
.children()
|
||||
.childAt(2)
|
||||
.name(),
|
||||
'button'
|
||||
);
|
||||
```
|
||||
|
||||
`MagicEightBall`'s state should be initialized with a property of `userInput` and a property of `randomIndex` both set to a value of an empty string.
|
||||
|
||||
```js
|
||||
assert(
|
||||
Enzyme.mount(React.createElement(MagicEightBall)).state('randomIndex') ===
|
||||
'' &&
|
||||
Enzyme.mount(React.createElement(MagicEightBall)).state('userInput') === ''
|
||||
);
|
||||
```
|
||||
|
||||
When `MagicEightBall` is first mounted to the DOM, it should return an empty `p` element.
|
||||
|
||||
```js
|
||||
assert(
|
||||
Enzyme.mount(React.createElement(MagicEightBall)).find('p').length === 1 &&
|
||||
Enzyme.mount(React.createElement(MagicEightBall)).find('p').text() === ''
|
||||
);
|
||||
```
|
||||
|
||||
When text is entered into the `input` element and the button is clicked, the `MagicEightBall` component should return a `p` element that contains a random element from the `possibleAnswers` array.
|
||||
|
||||
```js
|
||||
(() => {
|
||||
const comp = Enzyme.mount(React.createElement(MagicEightBall));
|
||||
const simulate = () => {
|
||||
comp.find('input').simulate('change', { target: { value: 'test?' } });
|
||||
comp.find('button').simulate('click');
|
||||
};
|
||||
const result = () => comp.find('p').text();
|
||||
const _1 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _2 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _3 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _4 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _5 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _6 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _7 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _8 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _9 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _10 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _1_val = _1();
|
||||
const _2_val = _2();
|
||||
const _3_val = _3();
|
||||
const _4_val = _4();
|
||||
const _5_val = _5();
|
||||
const _6_val = _6();
|
||||
const _7_val = _7();
|
||||
const _8_val = _8();
|
||||
const _9_val = _9();
|
||||
const _10_val = _10();
|
||||
const actualAnswers = [
|
||||
_1_val,
|
||||
_2_val,
|
||||
_3_val,
|
||||
_4_val,
|
||||
_5_val,
|
||||
_6_val,
|
||||
_7_val,
|
||||
_8_val,
|
||||
_9_val,
|
||||
_10_val
|
||||
];
|
||||
const hasIndex = actualAnswers.filter(
|
||||
(answer, i) => possibleAnswers.indexOf(answer) !== -1
|
||||
);
|
||||
const notAllEqual = new Set(actualAnswers);
|
||||
assert(notAllEqual.size > 1 && hasIndex.length === 10);
|
||||
})();
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
var possibleAnswers = [
|
||||
'It is certain',
|
||||
'It is decidedly so',
|
||||
'Without a doubt',
|
||||
'Yes, definitely',
|
||||
'You may rely on it',
|
||||
'As I see it, yes',
|
||||
'Outlook good',
|
||||
'Yes',
|
||||
'Signs point to yes',
|
||||
'Reply hazy try again',
|
||||
'Ask again later',
|
||||
'Better not tell you now',
|
||||
'Cannot predict now',
|
||||
'Concentrate and ask again',
|
||||
"Don't count on it",
|
||||
'My reply is no',
|
||||
'My sources say no',
|
||||
'Outlook not so good',
|
||||
'Very doubtful',
|
||||
'Most likely'
|
||||
];
|
||||
ReactDOM.render(<MagicEightBall />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
const inputStyle = {
|
||||
width: 235,
|
||||
margin: 5
|
||||
};
|
||||
|
||||
class MagicEightBall extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
userInput: '',
|
||||
randomIndex: ''
|
||||
};
|
||||
this.ask = this.ask.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
}
|
||||
ask() {
|
||||
if (this.state.userInput) {
|
||||
this.setState({
|
||||
randomIndex: Math.floor(Math.random() * 20),
|
||||
userInput: ''
|
||||
});
|
||||
}
|
||||
}
|
||||
handleChange(event) {
|
||||
this.setState({
|
||||
userInput: event.target.value
|
||||
});
|
||||
}
|
||||
render() {
|
||||
const possibleAnswers = [
|
||||
'It is certain',
|
||||
'It is decidedly so',
|
||||
'Without a doubt',
|
||||
'Yes, definitely',
|
||||
'You may rely on it',
|
||||
'As I see it, yes',
|
||||
'Outlook good',
|
||||
'Yes',
|
||||
'Signs point to yes',
|
||||
'Reply hazy try again',
|
||||
'Ask again later',
|
||||
'Better not tell you now',
|
||||
'Cannot predict now',
|
||||
'Concentrate and ask again',
|
||||
"Don't count on it",
|
||||
'My reply is no',
|
||||
'My sources say no',
|
||||
'Most likely',
|
||||
'Outlook not so good',
|
||||
'Very doubtful'
|
||||
];
|
||||
const answer = 'change me!'; // Change this line
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
type='text'
|
||||
value={this.state.userInput}
|
||||
onChange={this.handleChange}
|
||||
style={inputStyle}
|
||||
/>
|
||||
<br />
|
||||
<button onClick={this.ask}>Ask the Magic Eight Ball!</button>
|
||||
<br />
|
||||
<h3>Answer:</h3>
|
||||
<p>
|
||||
{/* Change code below this line */}
|
||||
|
||||
{/* Change code above this line */}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
const inputStyle = {
|
||||
width: 235,
|
||||
margin: 5
|
||||
};
|
||||
|
||||
class MagicEightBall extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
userInput: '',
|
||||
randomIndex: ''
|
||||
};
|
||||
this.ask = this.ask.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
}
|
||||
ask() {
|
||||
if (this.state.userInput) {
|
||||
this.setState({
|
||||
randomIndex: Math.floor(Math.random() * 20),
|
||||
userInput: ''
|
||||
});
|
||||
}
|
||||
}
|
||||
handleChange(event) {
|
||||
this.setState({
|
||||
userInput: event.target.value
|
||||
});
|
||||
}
|
||||
render() {
|
||||
const possibleAnswers = [
|
||||
'It is certain',
|
||||
'It is decidedly so',
|
||||
'Without a doubt',
|
||||
'Yes, definitely',
|
||||
'You may rely on it',
|
||||
'As I see it, yes',
|
||||
'Outlook good',
|
||||
'Yes',
|
||||
'Signs point to yes',
|
||||
'Reply hazy try again',
|
||||
'Ask again later',
|
||||
'Better not tell you now',
|
||||
'Cannot predict now',
|
||||
'Concentrate and ask again',
|
||||
"Don't count on it",
|
||||
'My reply is no',
|
||||
'My sources say no',
|
||||
'Outlook not so good',
|
||||
'Very doubtful',
|
||||
'Most likely'
|
||||
];
|
||||
const answer = possibleAnswers[this.state.randomIndex];
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
type='text'
|
||||
value={this.state.userInput}
|
||||
onChange={this.handleChange}
|
||||
style={inputStyle}
|
||||
/>
|
||||
<br />
|
||||
<button onClick={this.ask}>Ask the Magic Eight Ball!</button>
|
||||
<br />
|
||||
<h3>Answer:</h3>
|
||||
<p>{answer}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
@ -0,0 +1,236 @@
|
||||
---
|
||||
id: 5a24c314108439a4d403618c
|
||||
title: Use Array.filter() to Dynamically Filter an Array
|
||||
challengeType: 6
|
||||
forumTopicId: 301416
|
||||
dashedName: use-array-filter-to-dynamically-filter-an-array
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The `map` array method is a powerful tool that you will use often when working with React. Another method related to `map` is `filter`, which filters the contents of an array based on a condition, then returns a new array. For example, if you have an array of users that all have a property `online` which can be set to `true` or `false`, you can filter only those users that are online by writing:
|
||||
|
||||
```js
|
||||
let onlineUsers = users.filter(user => user.online);
|
||||
```
|
||||
|
||||
# --instructions--
|
||||
|
||||
In the code editor, `MyComponent`'s `state` is initialized with an array of users. Some users are online and some aren't. Filter the array so you see only the users who are online. To do this, first use `filter` to return a new array containing only the users whose `online` property is `true`. Then, in the `renderOnline` variable, map over the filtered array, and return a `li` element for each user that contains the text of their `username`. Be sure to include a unique `key` as well, like in the last challenges.
|
||||
|
||||
# --hints--
|
||||
|
||||
`MyComponent` should exist and render to the page.
|
||||
|
||||
```js
|
||||
assert.strictEqual(
|
||||
Enzyme.mount(React.createElement(MyComponent)).find('MyComponent').length,
|
||||
1
|
||||
);
|
||||
```
|
||||
|
||||
`MyComponent`'s state should be initialized to an array of six users.
|
||||
|
||||
```js
|
||||
assert(
|
||||
Array.isArray(
|
||||
Enzyme.mount(React.createElement(MyComponent)).state('users')
|
||||
) === true &&
|
||||
Enzyme.mount(React.createElement(MyComponent)).state('users').length === 6
|
||||
);
|
||||
```
|
||||
|
||||
`MyComponent` should return a `div`, an `h1`, and then an unordered list containing `li` elements for every user whose online status is set to `true`.
|
||||
|
||||
```js
|
||||
(() => {
|
||||
const comp = Enzyme.mount(React.createElement(MyComponent));
|
||||
const users = (bool) => ({
|
||||
users: [
|
||||
{ username: 'Jeff', online: bool },
|
||||
{ username: 'Alan', online: bool },
|
||||
{ username: 'Mary', online: bool },
|
||||
{ username: 'Jim', online: bool },
|
||||
{ username: 'Laura', online: bool }
|
||||
]
|
||||
});
|
||||
const result = () => comp.find('li').length;
|
||||
const _1 = result();
|
||||
const _2 = () => {
|
||||
comp.setState(users(true));
|
||||
return result();
|
||||
};
|
||||
const _3 = () => {
|
||||
comp.setState(users(false));
|
||||
return result();
|
||||
};
|
||||
const _4 = () => {
|
||||
comp.setState({ users: [] });
|
||||
return result();
|
||||
};
|
||||
const _2_val = _2();
|
||||
const _3_val = _3();
|
||||
const _4_val = _4();
|
||||
assert(
|
||||
comp.find('div').length === 1 &&
|
||||
comp.find('h1').length === 1 &&
|
||||
comp.find('ul').length === 1 &&
|
||||
_1 === 4 &&
|
||||
_2_val === 5 &&
|
||||
_3_val === 0 &&
|
||||
_4_val === 0
|
||||
);
|
||||
})();
|
||||
```
|
||||
|
||||
`MyComponent` should render `li` elements that contain the `username` of each online user.
|
||||
|
||||
```js
|
||||
(() => {
|
||||
const comp = Enzyme.mount(React.createElement(MyComponent));
|
||||
const users = (bool) => ({
|
||||
users: [
|
||||
{ username: 'Jeff', online: bool },
|
||||
{ username: 'Alan', online: bool },
|
||||
{ username: 'Mary', online: bool },
|
||||
{ username: 'Jim', online: bool },
|
||||
{ username: 'Laura', online: bool }
|
||||
]
|
||||
});
|
||||
const ul = () => {
|
||||
comp.setState(users(true));
|
||||
return comp.find('ul').html();
|
||||
};
|
||||
const html = ul();
|
||||
assert(
|
||||
html ===
|
||||
'<ul><li>Jeff</li><li>Alan</li><li>Mary</li><li>Jim</li><li>Laura</li></ul>'
|
||||
);
|
||||
})();
|
||||
```
|
||||
|
||||
Each list item element should have a unique `key` attribute.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(() => {
|
||||
const ul = Enzyme.mount(React.createElement(MyComponent)).find('ul');
|
||||
console.log(ul.debug());
|
||||
const keys = new Set([
|
||||
ul.childAt(0).key(),
|
||||
ul.childAt(1).key(),
|
||||
ul.childAt(2).key(),
|
||||
ul.childAt(3).key()
|
||||
]);
|
||||
return keys.size === 4;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<MyComponent />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
users: [
|
||||
{
|
||||
username: 'Jeff',
|
||||
online: true
|
||||
},
|
||||
{
|
||||
username: 'Alan',
|
||||
online: false
|
||||
},
|
||||
{
|
||||
username: 'Mary',
|
||||
online: true
|
||||
},
|
||||
{
|
||||
username: 'Jim',
|
||||
online: false
|
||||
},
|
||||
{
|
||||
username: 'Sara',
|
||||
online: true
|
||||
},
|
||||
{
|
||||
username: 'Laura',
|
||||
online: true
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
render() {
|
||||
const usersOnline = null; // Change this line
|
||||
const renderOnline = null; // Change this line
|
||||
return (
|
||||
<div>
|
||||
<h1>Current Online Users:</h1>
|
||||
<ul>{renderOnline}</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
users: [
|
||||
{
|
||||
username: 'Jeff',
|
||||
online: true
|
||||
},
|
||||
{
|
||||
username: 'Alan',
|
||||
online: false
|
||||
},
|
||||
{
|
||||
username: 'Mary',
|
||||
online: true
|
||||
},
|
||||
{
|
||||
username: 'Jim',
|
||||
online: false
|
||||
},
|
||||
{
|
||||
username: 'Sara',
|
||||
online: true
|
||||
},
|
||||
{
|
||||
username: 'Laura',
|
||||
online: true
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
render() {
|
||||
const usersOnline = this.state.users.filter(user => {
|
||||
return user.online;
|
||||
});
|
||||
const renderOnline = usersOnline.map(user => {
|
||||
return <li key={user.username}>{user.username}</li>;
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
<h1>Current Online Users:</h1>
|
||||
<ul>{renderOnline}</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
@ -0,0 +1,265 @@
|
||||
---
|
||||
id: 5a24c314108439a4d403618a
|
||||
title: Use Array.map() to Dynamically Render Elements
|
||||
challengeType: 6
|
||||
forumTopicId: 301417
|
||||
dashedName: use-array-map-to-dynamically-render-elements
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Conditional rendering is useful, but you may need your components to render an unknown number of elements. Often in reactive programming, a programmer has no way to know what the state of an application is until runtime, because so much depends on a user's interaction with that program. Programmers need to write their code to correctly handle that unknown state ahead of time. Using `Array.map()` in React illustrates this concept.
|
||||
|
||||
For example, you create a simple "To Do List" app. As the programmer, you have no way of knowing how many items a user might have on their list. You need to set up your component to dynamically render the correct number of list elements long before someone using the program decides that today is laundry day.
|
||||
|
||||
# --instructions--
|
||||
|
||||
The code editor has most of the `MyToDoList` component set up. Some of this code should look familiar if you completed the controlled form challenge. You'll notice a `textarea` and a `button`, along with a couple of methods that track their states, but nothing is rendered to the page yet.
|
||||
|
||||
Inside the `constructor`, create a `this.state` object and define two states: `userInput` should be initialized as an empty string, and `toDoList` should be initialized as an empty array. Next, delete the comment in the `render()` method next to the `items` variable. In its place, map over the `toDoList` array stored in the component's internal state and dynamically render a `li` for each item. Try entering the string `eat, code, sleep, repeat` into the `textarea`, then click the button and see what happens.
|
||||
|
||||
**Note:** You may know that all sibling child elements created by a mapping operation like this do need to be supplied with a unique `key` attribute. Don't worry, this is the topic of the next challenge.
|
||||
|
||||
# --hints--
|
||||
|
||||
The MyToDoList component should exist and render to the page.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyToDoList));
|
||||
return mockedComponent.find('MyToDoList').length === 1;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The first child of `MyToDoList` should be a `textarea` element.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyToDoList));
|
||||
return (
|
||||
mockedComponent.find('MyToDoList').children().childAt(0).type() ===
|
||||
'textarea'
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The second child of `MyToDoList` should be a `br` element.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyToDoList));
|
||||
return (
|
||||
mockedComponent.find('MyToDoList').children().childAt(1).type() === 'br'
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The third child of `MyToDoList` should be a `button` element.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyToDoList));
|
||||
return (
|
||||
mockedComponent.find('MyToDoList').children().childAt(2).type() ===
|
||||
'button'
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The state of `MyToDoList` should be initialized with `toDoList` as an empty array.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyToDoList));
|
||||
const initialState = mockedComponent.state();
|
||||
return (
|
||||
Array.isArray(initialState.toDoList) === true &&
|
||||
initialState.toDoList.length === 0
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The state of `MyToDoList` should be initialized with `userInput` as an empty string.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyToDoList));
|
||||
const initialState = mockedComponent.state();
|
||||
return (
|
||||
typeof initialState.userInput === 'string' &&
|
||||
initialState.userInput.length === 0
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
When the `Create List` button is clicked, the `MyToDoList` component should dynamically return an unordered list that contains a list item element for every item of a comma-separated list entered into the `textarea` element.
|
||||
|
||||
```js
|
||||
(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyToDoList));
|
||||
const simulateChange = (el, value) =>
|
||||
el.simulate('change', { target: { value } });
|
||||
const state_1 = () => {
|
||||
return mockedComponent.find('ul').find('li');
|
||||
};
|
||||
const setInput = () => {
|
||||
return simulateChange(
|
||||
mockedComponent.find('textarea'),
|
||||
'testA, testB, testC'
|
||||
);
|
||||
};
|
||||
const click = () => {
|
||||
return mockedComponent.find('button').simulate('click');
|
||||
};
|
||||
const state_2 = () => {
|
||||
const nodes = mockedComponent.find('ul').find('li');
|
||||
return { nodes, text: nodes.reduce((t, n) => t + n.text().trim(), '') };
|
||||
};
|
||||
const setInput_2 = () => {
|
||||
return simulateChange(
|
||||
mockedComponent.find('textarea'),
|
||||
't1, t2, t3, t4, t5, t6'
|
||||
);
|
||||
};
|
||||
const click_1 = () => {
|
||||
return mockedComponent.find('button').simulate('click');
|
||||
};
|
||||
const state_3 = () => {
|
||||
const nodes = mockedComponent.find('ul').find('li');
|
||||
return { nodes, text: nodes.reduce((t, n) => t + n.text().trim(), '') };
|
||||
};
|
||||
const awaited_state_1 = state_1();
|
||||
const awaited_setInput = setInput();
|
||||
const awaited_click = click();
|
||||
const awaited_state_2 = state_2();
|
||||
const awaited_setInput_2 = setInput_2();
|
||||
const awaited_click_1 = click_1();
|
||||
const awaited_state_3 = state_3();
|
||||
assert(
|
||||
awaited_state_1.length === 0 &&
|
||||
awaited_state_2.nodes.length === 3 &&
|
||||
awaited_state_3.nodes.length === 6 &&
|
||||
awaited_state_2.text === 'testAtestBtestC' &&
|
||||
awaited_state_3.text === 't1t2t3t4t5t6'
|
||||
);
|
||||
})();
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<MyToDoList />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
const textAreaStyles = {
|
||||
width: 235,
|
||||
margin: 5
|
||||
};
|
||||
|
||||
class MyToDoList extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
// Change code below this line
|
||||
|
||||
// Change code above this line
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
}
|
||||
handleSubmit() {
|
||||
const itemsArray = this.state.userInput.split(',');
|
||||
this.setState({
|
||||
toDoList: itemsArray
|
||||
});
|
||||
}
|
||||
handleChange(e) {
|
||||
this.setState({
|
||||
userInput: e.target.value
|
||||
});
|
||||
}
|
||||
render() {
|
||||
const items = null; // Change this line
|
||||
return (
|
||||
<div>
|
||||
<textarea
|
||||
onChange={this.handleChange}
|
||||
value={this.state.userInput}
|
||||
style={textAreaStyles}
|
||||
placeholder='Separate Items With Commas'
|
||||
/>
|
||||
<br />
|
||||
<button onClick={this.handleSubmit}>Create List</button>
|
||||
<h1>My "To Do" List:</h1>
|
||||
<ul>{items}</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
const textAreaStyles = {
|
||||
width: 235,
|
||||
margin: 5
|
||||
};
|
||||
|
||||
class MyToDoList extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
toDoList: [],
|
||||
userInput: ''
|
||||
};
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
}
|
||||
handleSubmit() {
|
||||
const itemsArray = this.state.userInput.split(',');
|
||||
this.setState({
|
||||
toDoList: itemsArray
|
||||
});
|
||||
}
|
||||
handleChange(e) {
|
||||
this.setState({
|
||||
userInput: e.target.value
|
||||
});
|
||||
}
|
||||
render() {
|
||||
const items = this.state.toDoList.map((item, i) => {
|
||||
return <li key={i}>{item}</li>;
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
<textarea
|
||||
onChange={this.handleChange}
|
||||
value={this.state.userInput}
|
||||
style={textAreaStyles}
|
||||
placeholder='Separate Items With Commas'
|
||||
/>
|
||||
<br />
|
||||
<button onClick={this.handleSubmit}>Create List</button>
|
||||
<h1>My "To Do" List:</h1>
|
||||
<ul>{items}</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
@ -0,0 +1,78 @@
|
||||
---
|
||||
id: 5a24c314108439a4d403616b
|
||||
title: Use Default Props
|
||||
challengeType: 6
|
||||
forumTopicId: 301418
|
||||
dashedName: use-default-props
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
React also has an option to set default props. You can assign default props to a component as a property on the component itself and React assigns the default prop if necessary. This allows you to specify what a prop value should be if no value is explicitly provided. For example, if you declare `MyComponent.defaultProps = { location: 'San Francisco' }`, you have defined a location prop that's set to the string `San Francisco`, unless you specify otherwise. React assigns default props if props are undefined, but if you pass `null` as the value for a prop, it will remain `null`.
|
||||
|
||||
# --instructions--
|
||||
|
||||
The code editor shows a `ShoppingCart` component. Define default props on this component which specify a prop `items` with a value of `0`.
|
||||
|
||||
# --hints--
|
||||
|
||||
The `ShoppingCart` component should render.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
|
||||
return mockedComponent.find('ShoppingCart').length === 1;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `ShoppingCart` component should have a default prop of `{ items: 0 }`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
|
||||
mockedComponent.setProps({ items: undefined });
|
||||
return mockedComponent.find('ShoppingCart').props().items === 0;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<ShoppingCart />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
const ShoppingCart = (props) => {
|
||||
return (
|
||||
<div>
|
||||
<h1>Shopping Cart Component</h1>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
// Change code below this line
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
const ShoppingCart = (props) => {
|
||||
return (
|
||||
<div>
|
||||
<h1>Shopping Cart Component</h1>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
// Change code below this line
|
||||
ShoppingCart.defaultProps = {
|
||||
items: 0
|
||||
}
|
||||
```
|
@ -0,0 +1,132 @@
|
||||
---
|
||||
id: 5a24c314108439a4d403616d
|
||||
title: Use PropTypes to Define the Props You Expect
|
||||
challengeType: 6
|
||||
forumTopicId: 301419
|
||||
dashedName: use-proptypes-to-define-the-props-you-expect
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
React provides useful type-checking features to verify that components receive props of the correct type. For example, your application makes an API call to retrieve data that you expect to be in an array, which is then passed to a component as a prop. You can set `propTypes` on your component to require the data to be of type `array`. This will throw a useful warning when the data is of any other type.
|
||||
|
||||
It's considered a best practice to set `propTypes` when you know the type of a prop ahead of time. You can define a `propTypes` property for a component in the same way you defined `defaultProps`. Doing this will check that props of a given key are present with a given type. Here's an example to require the type `function` for a prop called `handleClick`:
|
||||
|
||||
```js
|
||||
MyComponent.propTypes = { handleClick: PropTypes.func.isRequired }
|
||||
```
|
||||
|
||||
In the example above, the `PropTypes.func` part checks that `handleClick` is a function. Adding `isRequired` tells React that `handleClick` is a required property for that component. You will see a warning if that prop isn't provided. Also notice that `func` represents `function`. Among the seven JavaScript primitive types, `function` and `boolean` (written as `bool`) are the only two that use unusual spelling. In addition to the primitive types, there are other types available. For example, you can check that a prop is a React element. Please refer to the [documentation](https://reactjs.org/docs/jsx-in-depth.html#specifying-the-react-element-type) for all of the options.
|
||||
|
||||
**Note:** As of React v15.5.0, `PropTypes` is imported independently from React, like this: `import PropTypes from 'prop-types';`
|
||||
|
||||
# --instructions--
|
||||
|
||||
Define `propTypes` for the `Items` component to require `quantity` as a prop and verify that it is of type `number`.
|
||||
|
||||
# --hints--
|
||||
|
||||
The `ShoppingCart` component should render.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
|
||||
return mockedComponent.find('ShoppingCart').length === 1;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `Items` component should render.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart));
|
||||
return mockedComponent.find('Items').length === 1;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `Items` component should include a `propTypes` check to require a value for `quantity` and ensure that its value is a number.
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
assert(
|
||||
(function () {
|
||||
const noWhiteSpace = __helpers.removeWhiteSpace(getUserInput('index'));
|
||||
return (
|
||||
noWhiteSpace.includes('quantity:PropTypes.number.isRequired') &&
|
||||
noWhiteSpace.includes('Items.propTypes=')
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --before-user-code--
|
||||
|
||||
```jsx
|
||||
var PropTypes = {
|
||||
number: { isRequired: true }
|
||||
};
|
||||
```
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<ShoppingCart />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
const Items = (props) => {
|
||||
return <h1>Current Quantity of Items in Cart: {props.quantity}</h1>
|
||||
};
|
||||
|
||||
// Change code below this line
|
||||
|
||||
// Change code above this line
|
||||
|
||||
Items.defaultProps = {
|
||||
quantity: 0
|
||||
};
|
||||
|
||||
class ShoppingCart extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return <Items />
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
const Items = (props) => {
|
||||
return <h1>Current Quantity of Items in Cart: {props.quantity}</h1>
|
||||
};
|
||||
|
||||
// Change code below this line
|
||||
Items.propTypes = {
|
||||
quantity: PropTypes.number.isRequired
|
||||
};
|
||||
// Change code above this line
|
||||
|
||||
Items.defaultProps = {
|
||||
quantity: 0
|
||||
};
|
||||
|
||||
class ShoppingCart extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return <Items />
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,150 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036165
|
||||
title: Use React to Render Nested Components
|
||||
challengeType: 6
|
||||
forumTopicId: 301420
|
||||
dashedName: use-react-to-render-nested-components
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The last challenge showed a simple way to compose two components, but there are many different ways you can compose components with React.
|
||||
|
||||
Component composition is one of React's powerful features. When you work with React, it is important to start thinking about your user interface in terms of components like the App example in the last challenge. You break down your UI into its basic building blocks, and those pieces become the components. This helps to separate the code responsible for the UI from the code responsible for handling your application logic. It can greatly simplify the development and maintenance of complex projects.
|
||||
|
||||
# --instructions--
|
||||
|
||||
There are two functional components defined in the code editor, called `TypesOfFruit` and `Fruits`. Take the `TypesOfFruit` component and compose it, or *nest* it, within the `Fruits` component. Then take the `Fruits` component and nest it within the `TypesOfFood` component. The result should be a child component, nested within a parent component, which is nested within a parent component of its own!
|
||||
|
||||
# --hints--
|
||||
|
||||
The `TypesOfFood` component should return a single `div` element.
|
||||
|
||||
```js
|
||||
assert(Enzyme.shallow(React.createElement(TypesOfFood)).type() === 'div');
|
||||
```
|
||||
|
||||
The `TypesOfFood` component should return the `Fruits` component.
|
||||
|
||||
```js
|
||||
assert(
|
||||
Enzyme.shallow(React.createElement(TypesOfFood)).props().children[1].type
|
||||
.name === 'Fruits'
|
||||
);
|
||||
```
|
||||
|
||||
The `Fruits` component should return the `TypesOfFruit` component.
|
||||
|
||||
```js
|
||||
assert(
|
||||
Enzyme.mount(React.createElement(TypesOfFood)).find('h2').html() ===
|
||||
'<h2>Fruits:</h2>'
|
||||
);
|
||||
```
|
||||
|
||||
The `TypesOfFruit` component should return the `h2` and `ul` elements.
|
||||
|
||||
```js
|
||||
assert(
|
||||
Enzyme.mount(React.createElement(TypesOfFood)).find('ul').text() ===
|
||||
'ApplesBlueberriesStrawberriesBananas'
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<TypesOfFood />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
const TypesOfFruit = () => {
|
||||
return (
|
||||
<div>
|
||||
<h2>Fruits:</h2>
|
||||
<ul>
|
||||
<li>Apples</li>
|
||||
<li>Blueberries</li>
|
||||
<li>Strawberries</li>
|
||||
<li>Bananas</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Fruits = () => {
|
||||
return (
|
||||
<div>
|
||||
{ /* Change code below this line */ }
|
||||
|
||||
{ /* Change code above this line */ }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
class TypesOfFood extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Types of Food:</h1>
|
||||
{ /* Change code below this line */ }
|
||||
|
||||
{ /* Change code above this line */ }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
const TypesOfFruit = () => {
|
||||
return (
|
||||
<div>
|
||||
<h2>Fruits:</h2>
|
||||
<ul>
|
||||
<li>Apples</li>
|
||||
<li>Blueberries</li>
|
||||
<li>Strawberries</li>
|
||||
<li>Bananas</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Fruits = () => {
|
||||
return (
|
||||
<div>
|
||||
{ /* Change code below this line */ }
|
||||
<TypesOfFruit />
|
||||
{ /* Change code above this line */ }
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
class TypesOfFood extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Types of Food:</h1>
|
||||
{ /* Change code below this line */ }
|
||||
<Fruits />
|
||||
{ /* Change code above this line */ }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,189 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036176
|
||||
title: Use State to Toggle an Element
|
||||
challengeType: 6
|
||||
forumTopicId: 301421
|
||||
dashedName: use-state-to-toggle-an-element
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Sometimes you might need to know the previous state when updating the state. However, state updates may be asynchronous - this means React may batch multiple `setState()` calls into a single update. This means you can't rely on the previous value of `this.state` or `this.props` when calculating the next value. So, you should not use code like this:
|
||||
|
||||
```jsx
|
||||
this.setState({
|
||||
counter: this.state.counter + this.props.increment
|
||||
});
|
||||
```
|
||||
|
||||
Instead, you should pass `setState` a function that allows you to access state and props. Using a function with `setState` guarantees you are working with the most current values of state and props. This means that the above should be rewritten as:
|
||||
|
||||
```jsx
|
||||
this.setState((state, props) => ({
|
||||
counter: state.counter + props.increment
|
||||
}));
|
||||
```
|
||||
|
||||
You can also use a form without `props` if you need only the `state`:
|
||||
|
||||
```jsx
|
||||
this.setState(state => ({
|
||||
counter: state.counter + 1
|
||||
}));
|
||||
```
|
||||
|
||||
Note that you have to wrap the object literal in parentheses, otherwise JavaScript thinks it's a block of code.
|
||||
|
||||
# --instructions--
|
||||
|
||||
`MyComponent` has a `visibility` property which is initialized to `false`. The render method returns one view if the value of `visibility` is true, and a different view if it is false.
|
||||
|
||||
Currently, there is no way of updating the `visibility` property in the component's `state`. The value should toggle back and forth between true and false. There is a click handler on the button which triggers a class method called `toggleVisibility()`. Pass a function to `setState` to define this method so that the `state` of `visibility` toggles to the opposite value when the method is called. If `visibility` is `false`, the method sets it to `true`, and vice versa.
|
||||
|
||||
Finally, click the button to see the conditional rendering of the component based on its `state`.
|
||||
|
||||
**Hint:** Don't forget to bind the `this` keyword to the method in the `constructor`!
|
||||
|
||||
# --hints--
|
||||
|
||||
`MyComponent` should return a `div` element which contains a `button`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(
|
||||
Enzyme.mount(React.createElement(MyComponent)).find('div').find('button')
|
||||
.length,
|
||||
1
|
||||
);
|
||||
```
|
||||
|
||||
The state of `MyComponent` should initialize with a `visibility` property set to `false`.
|
||||
|
||||
```js
|
||||
assert.strictEqual(
|
||||
Enzyme.mount(React.createElement(MyComponent)).state('visibility'),
|
||||
false
|
||||
);
|
||||
```
|
||||
|
||||
Clicking the button element should toggle the `visibility` property in state between `true` and `false`.
|
||||
|
||||
```js
|
||||
(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
const first = () => {
|
||||
mockedComponent.setState({ visibility: false });
|
||||
return mockedComponent.state('visibility');
|
||||
};
|
||||
const second = () => {
|
||||
mockedComponent.find('button').simulate('click');
|
||||
return mockedComponent.state('visibility');
|
||||
};
|
||||
const third = () => {
|
||||
mockedComponent.find('button').simulate('click');
|
||||
return mockedComponent.state('visibility');
|
||||
};
|
||||
const firstValue = first();
|
||||
const secondValue = second();
|
||||
const thirdValue = third();
|
||||
assert(!firstValue && secondValue && !thirdValue);
|
||||
})();
|
||||
```
|
||||
|
||||
An anonymous function should be passed to `setState`.
|
||||
|
||||
```js
|
||||
const paramRegex = '[a-zA-Z$_]\\w*(,[a-zA-Z$_]\\w*)?';
|
||||
assert(
|
||||
new RegExp(
|
||||
'this\\.setState\\((function\\(' +
|
||||
paramRegex +
|
||||
'\\){|([a-zA-Z$_]\\w*|\\(' +
|
||||
paramRegex +
|
||||
'\\))=>)'
|
||||
).test(__helpers.removeWhiteSpace(code))
|
||||
);
|
||||
```
|
||||
|
||||
`this` should not be used inside `setState`
|
||||
|
||||
```js
|
||||
assert(!/this\.setState\([^}]*this/.test(code));
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<MyComponent />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
visibility: false
|
||||
};
|
||||
// Change code below this line
|
||||
|
||||
// Change code above this line
|
||||
}
|
||||
// Change code below this line
|
||||
|
||||
// Change code above this line
|
||||
render() {
|
||||
if (this.state.visibility) {
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.toggleVisibility}>Click Me</button>
|
||||
<h1>Now you see me!</h1>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.toggleVisibility}>Click Me</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
visibility: false
|
||||
};
|
||||
this.toggleVisibility = this.toggleVisibility.bind(this);
|
||||
}
|
||||
toggleVisibility() {
|
||||
this.setState(state => ({
|
||||
visibility: !state.visibility
|
||||
}));
|
||||
}
|
||||
render() {
|
||||
if (this.state.visibility) {
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.toggleVisibility}>Click Me</button>
|
||||
<h1>Now you see me!</h1>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.toggleVisibility}>Click Me</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
@ -0,0 +1,127 @@
|
||||
---
|
||||
id: 5a24c314108439a4d403617d
|
||||
title: Use the Lifecycle Method componentDidMount
|
||||
challengeType: 6
|
||||
forumTopicId: 301422
|
||||
dashedName: use-the-lifecycle-method-componentdidmount
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Most web developers, at some point, need to call an API endpoint to retrieve data. If you're working with React, it's important to know where to perform this action.
|
||||
|
||||
The best practice with React is to place API calls or any calls to your server in the lifecycle method `componentDidMount()`. This method is called after a component is mounted to the DOM. Any calls to `setState()` here will trigger a re-rendering of your component. When you call an API in this method, and set your state with the data that the API returns, it will automatically trigger an update once you receive the data.
|
||||
|
||||
# --instructions--
|
||||
|
||||
There is a mock API call in `componentDidMount()`. It sets state after 2.5 seconds to simulate calling a server to retrieve data. This example requests the current total active users for a site. In the render method, render the value of `activeUsers` in the `h1` after the text `Active Users:`. Watch what happens in the preview, and feel free to change the timeout to see the different effects.
|
||||
|
||||
# --hints--
|
||||
|
||||
`MyComponent` should render a `div` element which wraps an `h1` tag.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
return (
|
||||
mockedComponent.find('div').length === 1 &&
|
||||
mockedComponent.find('h1').length === 1
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
Component state should be updated with a timeout function in `componentDidMount`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
return new RegExp('setTimeout(.|\n)+setState(.|\n)+activeUsers').test(
|
||||
String(mockedComponent.instance().componentDidMount)
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The `h1` tag should render the `activeUsers` value from `MyComponent`'s state.
|
||||
|
||||
```js
|
||||
(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
const first = () => {
|
||||
mockedComponent.setState({ activeUsers: 1237 });
|
||||
return mockedComponent.find('h1').text();
|
||||
};
|
||||
const second = () => {
|
||||
mockedComponent.setState({ activeUsers: 1000 });
|
||||
return mockedComponent.find('h1').text();
|
||||
};
|
||||
assert(new RegExp('1237').test(first()) && new RegExp('1000').test(second()));
|
||||
})();
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<MyComponent />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
activeUsers: null
|
||||
};
|
||||
}
|
||||
componentDidMount() {
|
||||
setTimeout(() => {
|
||||
this.setState({
|
||||
activeUsers: 1273
|
||||
});
|
||||
}, 2500);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{/* Change code below this line */}
|
||||
<h1>Active Users: </h1>
|
||||
{/* Change code above this line */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
activeUsers: null
|
||||
};
|
||||
}
|
||||
componentDidMount() {
|
||||
setTimeout(() => {
|
||||
this.setState({
|
||||
activeUsers: 1273
|
||||
});
|
||||
}, 2500);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Active Users: {this.state.activeUsers}</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
@ -0,0 +1,87 @@
|
||||
---
|
||||
id: 5a24c314108439a4d403617c
|
||||
title: Use the Lifecycle Method componentWillMount
|
||||
challengeType: 6
|
||||
forumTopicId: 301423
|
||||
dashedName: use-the-lifecycle-method-componentwillmount
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
React components have several special methods that provide opportunities to perform actions at specific points in the lifecycle of a component. These are called lifecycle methods, or lifecycle hooks, and allow you to catch components at certain points in time. This can be before they are rendered, before they update, before they receive props, before they unmount, and so on. Here is a list of some of the main lifecycle methods: `componentWillMount()` `componentDidMount()` `shouldComponentUpdate()` `componentDidUpdate()` `componentWillUnmount()` The next several lessons will cover some of the basic use cases for these lifecycle methods.
|
||||
|
||||
**Note:** The `componentWillMount` Lifecycle method will be deprecated in a future version of 16.X and removed in version 17. [(Source)](https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html)
|
||||
|
||||
# --instructions--
|
||||
|
||||
The `componentWillMount()` method is called before the `render()` method when a component is being mounted to the DOM. Log something to the console within `componentWillMount()` - you may want to have your browser console open to see the output.
|
||||
|
||||
# --hints--
|
||||
|
||||
`MyComponent` should render a `div` element.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
return mockedComponent.find('div').length === 1;
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
`console.log` should be called in `componentWillMount`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const lifecycle = React.createElement(MyComponent)
|
||||
.type.prototype.componentWillMount.toString()
|
||||
.replace(/ /g, '');
|
||||
return lifecycle.includes('console.log(');
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<MyComponent />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
componentWillMount() {
|
||||
// Change code below this line
|
||||
|
||||
// Change code above this line
|
||||
}
|
||||
render() {
|
||||
return <div />
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
componentWillMount() {
|
||||
// Change code below this line
|
||||
console.log('Component is mounting...');
|
||||
// Change code above this line
|
||||
}
|
||||
render() {
|
||||
return <div />
|
||||
}
|
||||
};
|
||||
```
|
@ -0,0 +1,84 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036168
|
||||
title: Write a React Component from Scratch
|
||||
challengeType: 6
|
||||
forumTopicId: 301424
|
||||
dashedName: write-a-react-component-from-scratch
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now that you've learned the basics of JSX and React components, it's time to write a component on your own. React components are the core building blocks of React applications so it's important to become very familiar with writing them. Remember, a typical React component is an ES6 `class` which extends `React.Component`. It has a render method that returns HTML (from JSX) or `null`. This is the basic form of a React component. Once you understand this well, you will be prepared to start building more complex React projects.
|
||||
|
||||
# --instructions--
|
||||
|
||||
Define a class `MyComponent` that extends `React.Component`. Its render method should return a `div` that contains an `h1` tag with the text: `My First React Component!` in it. Use this text exactly, the case and punctuation matter. Make sure to call the constructor for your component, too.
|
||||
|
||||
Render this component to the DOM using `ReactDOM.render()`. There is a `div` with `id='challenge-node'` available for you to use.
|
||||
|
||||
# --hints--
|
||||
|
||||
There should be a React component called `MyComponent`.
|
||||
|
||||
```js
|
||||
(getUserInput) =>
|
||||
assert(
|
||||
__helpers
|
||||
.removeWhiteSpace(getUserInput('index'))
|
||||
.includes('classMyComponentextendsReact.Component{')
|
||||
);
|
||||
```
|
||||
|
||||
`MyComponent` should contain an `h1` tag with text `My First React Component!` Case and punctuation matter.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(function () {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
return mockedComponent.find('h1').text() === 'My First React Component!';
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
`MyComponent` should render to the DOM.
|
||||
|
||||
```js
|
||||
assert(document.getElementById('challenge-node').childNodes.length === 1);
|
||||
```
|
||||
|
||||
`MyComponent` should have a constructor calling `super` with `props`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
MyComponent.toString().includes('MyComponent(props)') &&
|
||||
MyComponent.toString().includes('_super.call(this, props)')
|
||||
);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
// Change code below this line
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
// Change code below this line
|
||||
class MyComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>My First React Component!</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
ReactDOM.render(<MyComponent />, document.getElementById('challenge-node'));
|
||||
```
|
@ -0,0 +1,146 @@
|
||||
---
|
||||
id: 5a24c314108439a4d4036177
|
||||
title: Write a Simple Counter
|
||||
challengeType: 6
|
||||
forumTopicId: 301425
|
||||
dashedName: write-a-simple-counter
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
You can design a more complex stateful component by combining the concepts covered so far. These include initializing `state`, writing methods that set `state`, and assigning click handlers to trigger these methods.
|
||||
|
||||
# --instructions--
|
||||
|
||||
The `Counter` component keeps track of a `count` value in `state`. There are two buttons which call methods `increment()` and `decrement()`. Write these methods so the counter value is incremented or decremented by 1 when the appropriate button is clicked. Also, create a `reset()` method so when the reset button is clicked, the count is set to 0.
|
||||
|
||||
**Note:** Make sure you don't modify the `className`s of the buttons. Also, remember to add the necessary bindings for the newly-created methods in the constructor.
|
||||
|
||||
# --hints--
|
||||
|
||||
`Counter` should return a `div` element which contains three buttons with text content in this order `Increment!`, `Decrement!`, `Reset`.
|
||||
|
||||
```js
|
||||
assert(
|
||||
(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(Counter));
|
||||
return (
|
||||
mockedComponent.find('.inc').text() === 'Increment!' &&
|
||||
mockedComponent.find('.dec').text() === 'Decrement!' &&
|
||||
mockedComponent.find('.reset').text() === 'Reset'
|
||||
);
|
||||
})()
|
||||
);
|
||||
```
|
||||
|
||||
The state of `Counter` should initialize with a `count` property set to `0`.
|
||||
|
||||
```js
|
||||
const mockedComponent = Enzyme.mount(React.createElement(Counter));
|
||||
assert(mockedComponent.find('h1').text() === 'Current Count: 0');
|
||||
```
|
||||
|
||||
Clicking the increment button should increment the count by `1`.
|
||||
|
||||
```js
|
||||
const mockedComponent = Enzyme.mount(React.createElement(Counter));
|
||||
mockedComponent.find('.inc').simulate('click');
|
||||
assert(mockedComponent.find('h1').text() === 'Current Count: 1');
|
||||
```
|
||||
|
||||
Clicking the decrement button should decrement the count by `1`.
|
||||
|
||||
```js
|
||||
const mockedComponent = Enzyme.mount(React.createElement(Counter));
|
||||
mockedComponent.find('.dec').simulate('click');
|
||||
assert(mockedComponent.find('h1').text() === 'Current Count: -1');
|
||||
```
|
||||
|
||||
Clicking the reset button should reset the count to `0`.
|
||||
|
||||
```js
|
||||
const mockedComponent = Enzyme.mount(React.createElement(Counter));
|
||||
mockedComponent.setState({ count: 5 });
|
||||
const currentCountElement = mockedComponent.find('h1');
|
||||
assert(currentCountElement.text() === 'Current Count: 5');
|
||||
mockedComponent.find('.reset').simulate('click');
|
||||
assert(currentCountElement.text() === 'Current Count: 0');
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --after-user-code--
|
||||
|
||||
```jsx
|
||||
ReactDOM.render(<Counter />, document.getElementById('root'))
|
||||
```
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```jsx
|
||||
class Counter extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
count: 0
|
||||
};
|
||||
// Change code below this line
|
||||
|
||||
// Change code above this line
|
||||
}
|
||||
// Change code below this line
|
||||
|
||||
// Change code above this line
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<button className='inc' onClick={this.increment}>Increment!</button>
|
||||
<button className='dec' onClick={this.decrement}>Decrement!</button>
|
||||
<button className='reset' onClick={this.reset}>Reset</button>
|
||||
<h1>Current Count: {this.state.count}</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
# --solutions--
|
||||
|
||||
```jsx
|
||||
class Counter extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
count: 0
|
||||
};
|
||||
this.increment = this.increment.bind(this);
|
||||
this.decrement = this.decrement.bind(this);
|
||||
this.reset = this.reset.bind(this);
|
||||
}
|
||||
reset() {
|
||||
this.setState({
|
||||
count: 0
|
||||
});
|
||||
}
|
||||
increment() {
|
||||
this.setState(state => ({
|
||||
count: state.count + 1
|
||||
}));
|
||||
}
|
||||
decrement() {
|
||||
this.setState(state => ({
|
||||
count: state.count - 1
|
||||
}));
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<button className='inc' onClick={this.increment}>Increment!</button>
|
||||
<button className='dec' onClick={this.decrement}>Decrement!</button>
|
||||
<button className='reset' onClick={this.reset}>Reset</button>
|
||||
<h1>Current Count: {this.state.count}</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
Reference in New Issue
Block a user