{ "name": "React", "order": 5, "time": "5 hours", "helpRoom": "Help", "required": [ { "src": "https://unpkg.com/react@16.4.0/umd/react.production.min.js" }, { "src": "https://unpkg.com/react-dom@16.4.0/umd/react-dom.production.min.js" } ], "template": "
${ source || '' }", "challenges": [ { "id": "587d7dbc367417b2b2512bb1", "title": "Create a Simple JSX Element", "releasedOn": "December 25, 2017", "description": [ "Intro: 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.",
"div
element to the constant JSX
. Replace the div
with an h1
element and add the text Hello JSX!
inside it."
],
"files": {
"indexjsx": {
"key": "indexjsx",
"ext": "jsx",
"name": "index",
"contents": ["", "const JSX = ;", ""],
"tail": ["ReactDOM.render(JSX, document.getElementById('root'))"],
"head": []
}
},
"tests": [
{
"text":
"The constant JSX
should return an h1
element.",
"testString":
"assert(JSX.type === 'h1', 'The constant JSX
should return an h1
element.');"
},
{
"text":
"The h1
tag should include the text Hello JSX!
",
"testString":
"assert(Enzyme.shallow(JSX).contains('Hello JSX!'), 'The h1
tag should include the text Hello JSX!
');"
}
],
"solutions": ["const JSX = <div>", "Invalid JSX:", "
<p>Paragraph One</p>
<p>Paragraph Two</p>
<p>Paragraph Three</p>
</div>
<p>Paragraph One</p>", "
<p>Paragraph Two</p>
<p>Paragraph Three</p>
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."
],
"files": {
"indexjsx": {
"key": "indexjsx",
"ext": "jsx",
"name": "index",
"contents": ["// write your code here", ""],
"tail": ["ReactDOM.render(JSX, document.getElementById('root'))"],
"head": []
}
},
"tests": [
{
"text":
"The constant JSX
should return a div
element.",
"testString":
"assert(JSX.type === 'div', 'The constant JSX
should return a div
element.');"
},
{
"text":
"The div
should contain a p
tag as the second element.",
"testString":
"assert(JSX.props.children[1].type === 'p', 'The div
should contain a p
tag as the second element.');"
},
{
"text":
"The div
should contain a ul
tag as the third element.",
"testString":
"assert(JSX.props.children[2].type === 'ul', 'The div
should contain a ul
tag as the third element.');"
},
{
"text":
"The div
should contain an h1
tag as the first element.",
"testString":
"assert(JSX.props.children[0].type === 'h1', 'The div
should contain an h1
tag as the first element.');"
},
{
"text":
"The ul
should contain three li
elements.",
"testString":
"assert(JSX.props.children[2].props.children.length === 3, 'The ul
should contain three li
elements.');"
}
],
"solutions": [
"const JSX = (\nSome info
\n{/* */}
to wrap around the comment text.",
"div
element, without modifying the existing h1
or p
elements."
],
"files": {
"indexjsx": {
"key": "indexjsx",
"ext": "jsx",
"name": "index",
"contents": [
"const JSX = (",
" Here's a subtitle
", "JSX
should return a div
element.",
"testString":
"assert(JSX.type === 'div', 'The constant JSX
should return a div
element.');"
},
{
"text":
"The div
should contain an h1
tag as the first element.",
"testString":
"assert(JSX.props.children[0].type === 'h1', 'The div
should contain an h1
tag as the first element.');"
},
{
"text":
"The div
should contain a p
tag as the second element.",
"testString":
"assert(JSX.props.children[1].type === 'p', 'The div
should contain a p
tag as the second element.');"
},
{
"text": "The JSX
should include a comment.",
"testString":
"getUserInput => assert(getUserInput('index').includes('/*') && getUserInput('index').includes('*/'), 'The JSX
should include a comment.');"
}
],
"solutions": [
"const JSX = (\nHere's a subtitle
\nReactDOM.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.",
"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."
],
"files": {
"indexjsx": {
"key": "indexjsx",
"ext": "jsx",
"name": "index",
"contents": [
"const JSX = (",
" Lets render this to the DOM
", "JSX
should return a div
element.",
"testString":
"assert(JSX.type === 'div', 'The constant JSX
should return a div
element.');"
},
{
"text":
"The div
should contain an h1
tag as the first element.",
"testString":
"assert(JSX.props.children[0].type === 'h1', 'The div
should contain an h1
tag as the first element.');"
},
{
"text":
"The div
should contain a p
tag as the second element.",
"testString":
"assert(JSX.props.children[1].type === 'p', 'The div
should contain a p
tag as the second element.');"
},
{
"text":
"The provided JSX element should render to the DOM node with id challenge-node
.",
"testString":
"assert(document.getElementById('challenge-node').childNodes[0].innerHTML === 'Lets render this to the DOM
', 'The provided JSX element should render to the DOM node with idchallenge-node
.');"
}
],
"solutions": [
"const JSX = (\nLets render this to the DOM
\nclass
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.",
"myDiv
to the div
provided in the JSX code."
],
"files": {
"indexjsx": {
"key": "indexjsx",
"ext": "jsx",
"name": "index",
"contents": [
"const JSX = (",
" JSX
should return a div
element.",
"testString":
"assert.strictEqual(JSX.type, 'div', 'The constant JSX
should return a div
element.');"
},
{
"text": "The div
has a class of myDiv
.",
"testString":
"assert.strictEqual(JSX.props.className, 'myDiv', 'The div
has a class of myDiv
.');"
}
],
"solutions": [
"const JSX = (\nclassName
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.",
"Be sure to close all tags!
", "JSX
should return a div
element.",
"testString":
"assert.strictEqual(JSX.type, 'div', 'The constant JSX
should return a div
element.');"
},
{
"text": "The div
should contain a br
tag.",
"testString":
"assert(Enzyme.shallow(JSX).find('br').length === 1, 'The div
should contain a br
tag.');"
},
{
"text": "The div
should contain an hr
tag.",
"testString":
"assert(Enzyme.shallow(JSX).find('hr').length === 1, 'The div
should contain an hr
tag.');"
}
],
"solutions": [
"const JSX = (\nBe sure to close all tags!
\nnull
. 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:",
"// 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.", "
const DemoComponent = function() {
return (
<div className='customClass' />
);
};
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."
],
"files": {
"indexjsx": {
"key": "indexjsx",
"ext": "jsx",
"name": "index",
"contents": [
"const MyComponent = function() {",
" // change code below this line",
"",
"",
"",
" // change code above this line",
"}"
],
"tail": [
"ReactDOM.render(MyComponent
should return JSX.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return mockedComponent.length === 1; })(), 'MyComponent
should return JSX.');"
},
{
"text":
"MyComponent
should return a div
element.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return mockedComponent.children().type() === 'div' })(), 'MyComponent
should return a div
element.');"
},
{
"text":
"The div
element should contain a string of text.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return mockedComponent.find('div').text() !== ''; })(), 'The div
element should contain a string of text.');"
}
],
"solutions": [
"const MyComponent = function() {\n // change code below this line\n return (\n class
syntax. In the following example, Kitten
extends React.Component
:",
"class Kitten extends React.Component {", "This creates an ES6 class
constructor(props) {
super(props);
}
render() {
return (
<h1>Hi</h1>
);
}
}
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
.",
"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!
."
],
"files": {
"indexjsx": {
"key": "indexjsx",
"ext": "jsx",
"name": "index",
"contents": [
"",
"class MyComponent extends React.Component {",
" constructor(props) {",
" super(props);",
" }",
" render() {",
" // change code below this line",
"",
"",
"",
" // change code above this line",
" }",
"};"
],
"tail": [
"ReactDOM.render(div
element.",
"testString":
"assert(Enzyme.shallow(React.createElement(MyComponent)).type() === 'div', 'The React component should return a div
element.');"
},
{
"text":
"The returned div
should render an h1
header within it.",
"testString":
"assert(/div
should render an h1
header within it.');"
},
{
"text":
"The h1
header should contain the string Hello React!
.",
"testString":
"assert(Enzyme.shallow(React.createElement(MyComponent)).html() === 'h1
header should contain the string Hello React!
.');"
}
],
"solutions": [
"class MyComponent extends React.Component {\n constructor(props) {\n super(props);\n }\n render() {\n // change code below this line\n return (\n 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:",
"return (", "When React encounters a custom HTML tag that references another component (a component name wrapped in
<App>
<Navbar />
<Dashboard />
<Footer />
</App>
)
< />
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
.",
"ChildComponent
and a React 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."
],
"files": {
"indexjsx": {
"key": "indexjsx",
"ext": "jsx",
"name": "index",
"contents": [
"const ChildComponent = () => {",
" return (",
" I am the child
", "div
element.",
"testString":
"assert((function() { var shallowRender = Enzyme.shallow(React.createElement(ParentComponent)); return shallowRender.type() === 'div'; })(), 'The React component should return a single div
element.');"
},
{
"text": "The component should return two nested elements.",
"testString":
"assert((function() { var shallowRender = Enzyme.shallow(React.createElement(ParentComponent)); return shallowRender.children().length === 2; })(), 'The component should return two nested elements.');"
},
{
"text":
"The component should return the ChildComponent as its second child.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ParentComponent)); return mockedComponent.find('ParentComponent').find('ChildComponent').length === 1; })(), 'The component should return the ChildComponent as its second child.');"
}
],
"solutions": [
"const ChildComponent = () => {\n return (\n I am the child
\nTypesOfFruit
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!"
],
"files": {
"indexjsx": {
"key": "indexjsx",
"ext": "jsx",
"name": "index",
"contents": [
"const TypesOfFruit = () => {",
" return (",
" TypesOfFood
component should return a single div
element.",
"testString":
"assert(Enzyme.shallow(React.createElement(TypesOfFood)).type() === 'div', 'The TypesOfFood
component should return a single div
element.');"
},
{
"text":
"The TypesOfFood
component should return the Fruits
component.",
"testString":
"assert(Enzyme.shallow(React.createElement(TypesOfFood)).props().children[1].type.name === 'Fruits', 'The TypesOfFood
component should return the Fruits
component.');"
},
{
"text":
"The Fruits
component should return the TypesOfFruit
component.",
"testString":
"assert(Enzyme.mount(React.createElement(TypesOfFood)).find('h2').html() === 'Fruits
component should return the TypesOfFruit
component.');"
},
{
"text":
"The TypesOfFruit
component should return the h2
and ul
elements.",
"testString":
"assert(Enzyme.mount(React.createElement(TypesOfFood)).find('ul').text() === 'ApplesBlueberriesStrawberriesBananas', 'The TypesOfFruit
component should return the h2
and ul
elements.');"
}
],
"solutions": [
"const TypesOfFruit = () => {\n return (\n 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 in the background. 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."
],
"files": {
"indexjsx": {
"key": "indexjsx",
"ext": "jsx",
"name": "index",
"head": [
"class NonCitrus extends React.Component {",
" render() {",
" return (",
" TypesOfFood
component should return a single div
element.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood)); return mockedComponent.children().type() === 'div'; })(), 'The TypesOfFood
component should return a single div
element.');"
},
{
"text":
"The TypesOfFood
component should return the Fruits
component.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood)); return mockedComponent.children().childAt(1).name() === 'Fruits'; })(), 'The TypesOfFood
component should return the Fruits
component.');"
},
{
"text":
"The Fruits
component should return the NonCitrus
component and the Citrus
component.",
"testString":
"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 Fruits
component should return the NonCitrus
component and the Citrus
component.');"
},
{
"text":
"The TypesOfFood
component should return the Vegetables
component below the Fruits
component.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood)); return mockedComponent.children().childAt(2).name() === 'Vegetables'; })(), 'The TypesOfFood
component should return the Vegetables
component below the Fruits
component.');"
}
],
"solutions": [
"class Fruits extends React.Component {\n constructor(props) {\n super(props);\n }\n render() {\n return (\n 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.",
"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."
],
"files": {
"indexjsx": {
"key": "indexjsx",
"ext": "jsx",
"name": "index",
"head": [
"",
"const Fruits = () => {",
" return (",
" TypesOfFood
component should return a single div
element.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood)); return mockedComponent.children().type() === 'div'; })(), 'The TypesOfFood
component should return a single div
element.');"
},
{
"text":
"The TypesOfFood
component should render the Fruits
component after the h1
element.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood)); return mockedComponent.children().childAt(1).name() === 'Fruits'; })(), 'The TypesOfFood
component should render the Fruits
component after the h1
element.');"
},
{
"text":
"The TypesOfFood
component should render the Vegetables
component after Fruits
.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood)); return mockedComponent.children().childAt(2).name() === 'Vegetables'; })(), 'The TypesOfFood
component should render the Vegetables
component after Fruits
.');"
},
{
"text":
"The TypesOfFood
component should render to the DOM within the div
with the id challenge-node
.",
"testString":
"assert((function() { const html = document.getElementById('challenge-node').childNodes[0].innerHTML; return (html === 'TypesOfFood
component should render to the DOM within the div
with the id challenge-node
.');"
}
],
"solutions": [
"class TypesOfFood extends React.Component {\n constructor(props) {\n super(props);\n }\n render() {\n return (\n 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.",
"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."
],
"files": {
"indexjsx": {
"key": "indexjsx",
"ext": "jsx",
"name": "index",
"contents": ["// change code below this line", ""],
"head": [],
"tail": []
}
},
"tests": [
{
"text":
"There should be a React component called MyComponent
.",
"testString":
"getUserInput => assert(getUserInput('index').replace(/\\s/g, '').includes('classMyComponentextendsReact.Component{'), 'There should be a React component called MyComponent
.');"
},
{
"text":
"MyComponent
should contain an h1
tag with text My First React Component!
Case and punctuation matter.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return mockedComponent.find('h1').text() === 'My First React Component!'; })(), 'MyComponent
should contain an h1
tag with text My First React Component!
Case and punctuation matter.');"
},
{
"text": "MyComponent
should render to the DOM.",
"testString":
"assert(document.getElementById('challenge-node').childNodes.length === 1, 'MyComponent
should render to the DOM.');"
}
],
"solutions": [
"// change code below this line\nclass MyComponent extends React.Component {\n constructor(props) {\n super(props);\n }\n render() {\n return (\n App
component which renders a child component called Welcome
that is a stateless functional component. You can pass Welcome
a user
property by writing:",
"<App>", "You use custom HTML attributes that React provides support for to pass the property
<Welcome user='Mark' />
</App>
user
to the component Welcome
. Since Welcome
is a stateless functional component, it has access to this value like so:",
"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.",
"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()}
."
],
"files": {
"indexjsx": {
"key": "indexjsx",
"ext": "jsx",
"name": "index",
"contents": [
"",
"const CurrentDate = (props) => {",
" return (",
" The current date is:
", " { /* change code above this line */ }", "Calendar
component should return a single div
element.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(Calendar)); return mockedComponent.children().type() === 'div'; })(), 'The Calendar
component should return a single div
element.');"
},
{
"text":
"The second child of the Calendar
component should be the CurrentDate
component.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(Calendar)); return mockedComponent.children().childAt(1).name() === 'CurrentDate'; })(), 'The second child of the Calendar
component should be the CurrentDate
component.');"
},
{
"text":
"The CurrentDate
component should have a prop called date
.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(Calendar)); return mockedComponent.children().childAt(1).props().date })(), 'The CurrentDate
component should have a prop called date
.');"
},
{
"text":
"The date
prop of the CurrentDate
should contain a string of text.",
"testString":
"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 of the CurrentDate
should contain a string of text.');"
},
{
"text":
"The CurrentDate
component should render the value from the date
prop in the p
tag.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(Calendar)); return mockedComponent.find('p').html().includes(Date().substr(3)); })(), 'The CurrentDate
component should render the value from the date
prop in the p
tag.');"
}
],
"solutions": [
"const CurrentDate = (props) => {\n return (\n The current date is: {props.date}
\n { /* change code above this line */ }\nprops
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.",
"<ParentComponent>", "The child component then has access to the array property
<ChildComponent colors={[\"green\", \"blue\", \"red\"]} />
</ParentComponent>
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.",
"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."
],
"files": {
"indexjsx": {
"key": "indexjsx",
"ext": "jsx",
"name": "index",
"contents": [
"const List= (props) => {",
" { /* change code below this line */ }",
" return {}
", " { /* change code above this line */ }", "};", "", "class ToDo extends React.Component {", " constructor(props) {", " super(props);", " }", " render() {", " return (", "ToDo
component should return a single outer div
.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ToDo)); return mockedComponent.children().first().type() === 'div'; })(), 'The ToDo
component should return a single outer div
.');"
},
{
"text":
"The third child of the ToDo
component should be an instance of the List
component.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ToDo)); return mockedComponent.children().first().childAt(2).name() === 'List'; })(), 'The third child of the ToDo
component should be an instance of the List
component.');"
},
{
"text":
"The fifth child of the ToDo
component should be an instance of the List
component.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ToDo)); return mockedComponent.children().first().childAt(4).name() === 'List'; })(), 'The fifth child of the ToDo
component should be an instance of the List
component.');"
},
{
"text":
"Both instances of the List
component should have a property called tasks
and tasks
should be of type array.",
"testString":
"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); })(), 'Both instances of the List
component should have a property called tasks
and tasks
should be of type array.');"
},
{
"text":
"The first List
component representing the tasks for today should have 2 or more items.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ToDo)); return mockedComponent.find('List').get(0).props.tasks.length >= 2; })(), 'The first List
component representing the tasks for today should have 2 or more items.');"
},
{
"text":
"The second List
component representing the tasks for tomorrow should have 3 or more items.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ToDo)); return mockedComponent.find('List').get(1).props.tasks.length >= 3; })(), 'The second List
component representing the tasks for tomorrow should have 3 or more items.');"
},
{
"text":
"The List
component should render the value from the tasks
prop in the p
tag.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ToDo)); return mockedComponent.find('p').get(0).props.children === mockedComponent.find('List').get(0).props.tasks.join(', ') && mockedComponent.find('p').get(1).props.children === mockedComponent.find('List').get(1).props.tasks.join(', '); })(), 'The List
component should render the value from the tasks
prop in the p
tag.');"
}
],
"solutions": [
"const List= (props) => {\n return {props.tasks.join(', ')}
\n};\n\nclass ToDo extends React.Component {\n constructor(props) {\n super(props);\n }\n render() {\n return (\nMyComponent.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
.",
"ShoppingCart
component. Define default props on this component which specify a prop items
with a value of 0
."
],
"files": {
"indexjsx": {
"key": "indexjsx",
"ext": "jsx",
"name": "index",
"contents": [
"const ShoppingCart = (props) => {",
" return (",
" ShoppingCart
component should render.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart)); return mockedComponent.find('ShoppingCart').length === 1; })(), 'The ShoppingCart
component should render.');"
},
{
"text":
"The ShoppingCart
component should have a default prop of { items: 0 }
.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart)); mockedComponent.setProps({items: undefined}); return mockedComponent.find('ShoppingCart').props().items === 0; })(), 'The ShoppingCart
component should have a default prop of { items: 0 }
.');"
}
],
"solutions": [
"const ShoppingCart = (props) => {\n return (\n 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."
],
"files": {
"indexjsx": {
"key": "indexjsx",
"ext": "jsx",
"name": "index",
"contents": [
"const Items = (props) => {",
" return ShoppingCart
should render.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart)); return mockedComponent.find('ShoppingCart').length === 1; })(), 'The component ShoppingCart
should render.');"
},
{
"text": "The component Items
should render.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart)); return mockedComponent.find('Items').length === 1; })(), 'The component Items
should render.');"
},
{
"text":
"The Items
component should have a prop of { quantity: 10 }
passed from the ShoppingCart
component.",
"testString":
"getUserInput => assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart)); return mockedComponent.find('Items').props().quantity == 10 && getUserInput('index').replace(/ /g,'').includes('Items
component should have a prop of { quantity: 10 }
passed from the ShoppingCart
component.');"
}
],
"solutions": [
"const Items = (props) => {\n return 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
:",
"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 for all of the options.",
"Note: As of React v15.5.0, PropTypes
is imported independently from React, like this:",
"import React, { PropTypes } from 'react';
",
"propTypes
for the Items
component to require quantity
as a prop and verify that it is of type number
."
],
"files": {
"indexjsx": {
"key": "indexjsx",
"ext": "jsx",
"name": "index",
"head": [
"var PropTypes = {",
" number: { isRequired: true }",
"};",
""
],
"contents": [
"const Items = (props) => {",
" return ShoppingCart
component should render.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart)); return mockedComponent.find('ShoppingCart').length === 1; })(), 'The ShoppingCart
component should render.');"
},
{
"text": "The Items
component should render.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart)); return mockedComponent.find('Items').length === 1; })(), 'The Items
component should render.');"
},
{
"text":
"The Items
component should include a propTypes
check that requires quantity
to be a number
.",
"testString":
"getUserInput => assert((function() { const noWhiteSpace = getUserInput('index').replace(/ /g, ''); return noWhiteSpace.includes('quantity:PropTypes.number.isRequired') && noWhiteSpace.includes('Items.propTypes='); })(), 'The Items
component should include a propTypes
check that requires quantity
to be a number
.');"
}
],
"solutions": [
"const Items = (props) => {\n return 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.",
"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."
],
"files": {
"indexjsx": {
"key": "indexjsx",
"ext": "jsx",
"name": "index",
"contents": [
"class ReturnTempPassword extends React.Component {",
" constructor(props) {",
" super(props);",
"",
" }",
" render() {",
" return (",
" Your temporary password is:
", " { /* change code above this line */ }", "ResetPassword
component should return a single div
element.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ResetPassword)); return mockedComponent.children().type() === 'div'; })(), 'The ResetPassword
component should return a single div
element.');"
},
{
"text":
"The fourth child of ResetPassword
should be the ReturnTempPassword
component.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ResetPassword)); return mockedComponent.children().childAt(3).name() === 'ReturnTempPassword'; })(), 'The fourth child of ResetPassword
should be the ReturnTempPassword
component.');"
},
{
"text":
"The ReturnTempPassword
component should have a prop called tempPassword
.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ResetPassword)); return mockedComponent.find('ReturnTempPassword').props().tempPassword; })(), 'The ReturnTempPassword
component should have a prop called tempPassword
.');"
},
{
"text":
"The tempPassword
prop of ReturnTempPassword
should be equal to a string of at least 8
characters.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ResetPassword)); const temp = mockedComponent.find('ReturnTempPassword').props().tempPassword; return typeof temp === 'string' && temp.length >= 8; })(), 'The tempPassword
prop of ReturnTempPassword
should be equal to a string of at least 8
characters.');"
},
{
"text":
"The ReturnTempPassword
component should display the password you create as the tempPassword
prop within strong
tags.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ResetPassword)); return mockedComponent.find('strong').text() === mockedComponent.find('ReturnTempPassword').props().tempPassword; })(), 'The ReturnTempPassword
component should display the password you create as the tempPassword
prop within strong
tags.');"
}
],
"solutions": [
"class ReturnTempPassword extends React.Component {\n constructor(props) {\n super(props);\n\n }\n render() {\n return (\n Your temporary password is: {this.props.tempPassword}
\nReact.Component
, but does not use internal state (covered in the next challenge). Finally, a stateful component is any 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.",
"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
."
],
"files": {
"indexjsx": {
"key": "indexjsx",
"ext": "jsx",
"name": "index",
"head": [
"var PropTypes = {",
" string: { isRequired: true }",
"};"
],
"contents": [
"class CampSite extends React.Component {",
" constructor(props) {",
" super(props);",
" }",
" render() {",
" return (",
" CampSite
component should render.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(CampSite)); return mockedComponent.find('CampSite').length === 1; })(), 'The CampSite
component should render.');"
},
{
"text": "The Camper
component should render.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(CampSite)); return mockedComponent.find('Camper').length === 1; })(), 'The Camper
component should render.');"
},
{
"text":
"The Camper
component should include default props which assign the string CamperBot
to the key name
.",
"testString":
"getUserInput => assert((function() { const noWhiteSpace = getUserInput('index').replace(/\\s/g, ''); const verify1 = 'Camper.defaultProps={name:\\'CamperBot\\'}'; const verify2 = 'Camper.defaultProps={name:\"CamperBot\"}'; return (noWhiteSpace.includes(verify1) || noWhiteSpace.includes(verify2)); })(), 'The Camper
component should include default props which assign the string CamperBot
to the key name
.');"
},
{
"text":
"The Camper
component should include prop types which require the name
prop to be of type string
.",
"testString":
"getUserInput => assert((function() { const mockedComponent = Enzyme.mount(React.createElement(CampSite)); const noWhiteSpace = getUserInput('index').replace(/\\s/g, ''); const verifyDefaultProps = 'Camper.propTypes={name:PropTypes.string.isRequired}'; return noWhiteSpace.includes(verifyDefaultProps); })(), 'The Camper
component should include prop types which require the name
prop to be of type string
.');"
},
{
"text":
"The Camper
component should contain a p
element with only the text from the name
prop.",
"testString":
"assert((function() { const mockedComponent = Enzyme.mount(React.createElement(CampSite)); return mockedComponent.find('p').text() === mockedComponent.find('Camper').props().name; })(), 'The Camper
component should contain a p
element with only the text from the name
prop.');"
}
],
"solutions": [
"class CampSite extends React.Component {\n constructor(props) {\n super(props);\n }\n render() {\n return (\n {props.name}
\nstate
. 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:",
"this.state = {
// describe your state here
}", "You have access to thestate
object throughout the life of your component. You can update it, render it in your UI, and pass it as props to child components. Thestate
object can be as complex or as simple as you need it to be. Note that you must create a class component by extendingReact.Component
in order to createstate
like this.", "
", "There is a component in the code editor that is trying to render aname
property from itsstate
. However, there is nostate
defined. Initialize the component withstate
in theconstructor
and assign your name to a property ofname
." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "", "class StatefulComponent extends React.Component {", " constructor(props) {", " super(props);", " // initialize state here", "", " }", " render() {", " return (", "", "", " );", " }", "};" ], "tail": [ "ReactDOM.render({this.state.name}
", ", document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": " StatefulComponent
should exist and render.", "testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(StatefulComponent)); return mockedComponent.find('StatefulComponent').length === 1; })(), 'StatefulComponent
should exist and render.');" }, { "text": "StatefulComponent
should render adiv
and anh1
element.", "testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(StatefulComponent)); return mockedComponent.find('div').length === 1 && mockedComponent.find('h1').length === 1; })(), 'StatefulComponent
should render adiv
and anh1
element.');" }, { "text": "The state ofStatefulComponent
should be initialized with a propertyname
set to a string.", "testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(StatefulComponent)); const initialState = mockedComponent.state(); return ( typeof initialState === 'object' && typeof initialState.name === 'string'); })(), 'The state ofStatefulComponent
should be initialized with a propertyname
set to a string.');" }, { "text": "The propertyname
in the state ofStatefulComponent
should render in theh1
element.", "testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(StatefulComponent)); const initialState = mockedComponent.state(); return mockedComponent.find('h1').text() === initialState.name; })(), 'The propertyname
in the state ofStatefulComponent
should render in theh1
element.');" } ], "solutions": [ "class StatefulComponent extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n name: 'freeCodeCamp!'\n }\n }\n render() {\n return (\n\n\n );\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d4036171", "title": "Render State in the User Interface", "releasedOn": "December 25, 2017", "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{this.state.name}
\nstate
in itsrender()
method. You can access the data withthis.state
.", "If you want to access a state value within thereturn
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 itsstate
. Itsstate
is completely encapsulated, or local to that component, unless you pass state data to a child component asprops
. This notion of encapsulatedstate
is very important because it allows you to write certain logic, then have that logic contained and isolated in one place in your code.", "
", "In the code editor,MyComponent
is already stateful. Define anh1
tag in the component's render method which renders the value ofname
from the component's state.", "Note: Theh1
should only render the value fromstate
and nothing else. In JSX, any code you write with curly braces{ }
will be treated as JavaScript. So to access the value fromstate
just enclose the reference in curly braces." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "class MyComponent extends React.Component {", " constructor(props) {", " super(props);", " this.state = {", " name: 'freeCodeCamp'", " }", " }", " render() {", " return (", "", " { /* change code below this line */ }", "", " { /* change code above this line */ }", "", " );", " }", "};" ], "tail": [ "ReactDOM.render(, document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": " MyComponent
should have a keyname
with valuefreeCodeCamp
stored in its state.", "testString": "assert(Enzyme.mount(React.createElement(MyComponent)).state('name') === 'freeCodeCamp', 'MyComponent
should have a keyname
with valuefreeCodeCamp
stored in its state.');" }, { "text": "MyComponent
should render anh1
header enclosed in a singlediv
.", "testString": "assert(/.*<\\/h1><\\/div>/.test(Enzyme.mount(React.createElement(MyComponent)).html()), '
MyComponent
should render anh1
header enclosed in a singlediv
.');" }, { "text": "The renderedh1
header should contain text rendered from the component's state.", "testString": "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 === '', 'The renderedTestName
h1
header should contain text rendered from the component's state.');};" } ], "solutions": [ "class MyComponent extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n name: 'freeCodeCamp'\n }\n }\n render() {\n return (\n\n { /* change code below this line */ }\n\n );\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d4036172", "title": "Render State in the User Interface Another Way", "releasedOn": "December 25, 2017", "description": [ "There is another way to access{this.state.name}
\n { /* change code above this line */ }\nstate
in a component. In therender()
method, before thereturn
statement, you can write JavaScript directly. For example, you could declare functions, access data fromstate
orprops
, perform computations on this data, and so on. Then, you can assign any data to variables, which you have access to in thereturn
statement.", "
", "In theMyComponent
render method, define aconst
calledname
and set it equal to the name value in the component'sstate
. 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 anh1
tag using the variablename
. Remember, you need to use the JSX syntax (curly braces for JavaScript) in the return statement." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "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 (", "", " { /* change code below this line */ }", "", " { /* change code above this line */ }", "", " );", " }", "};" ], "tail": [ "ReactDOM.render(, document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": " MyComponent
should have a keyname
with valuefreeCodeCamp
stored in its state.", "testString": "assert(Enzyme.mount(React.createElement(MyComponent)).state('name') === 'freeCodeCamp', 'MyComponent
should have a keyname
with valuefreeCodeCamp
stored in its state.');" }, { "text": "MyComponent
should render anh1
header enclosed in a singlediv
.", "testString": "assert(/.*<\\/h1><\\/div>/.test(Enzyme.mount(React.createElement(MyComponent)).html()), '
MyComponent
should render anh1
header enclosed in a singlediv
.');" }, { "text": "The renderedh1
tag should include a reference to{name}
.", "testString": "getUserInput => assert(/\\n*\\s*\\{\\s*name\\s*\\}\\s*\\n*<\\/h1>/.test(getUserInput('index')), 'The rendered
h1
tag should include a reference to{name}
.');" }, { "text": "The renderedh1
header should contain text rendered from the component's state.", "testString": "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 === '', 'The renderedTestName
h1
header should contain text rendered from the component's state.'); };" } ], "solutions": [ "class MyComponent extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n name: 'freeCodeCamp'\n }\n }\n render() {\n // change code below this line\n const name = this.state.name;\n // change code above this line\n return (\n\n { /* change code below this line */ }\n\n );\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d4036173", "title": "Set State with this.setState", "releasedOn": "December 25, 2017", "description": [ "The previous challenges covered component{name}
\n { /* change code above this line */ }\nstate
and how to initialize state in theconstructor
. There is also a way to change the component'sstate
. React provides a method for updating componentstate
calledsetState
. You call thesetState
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 ausername
in state and wanted to update it, it would look like this:", "this.setState({", "React expects you to never modify
username: 'Lewis'
});state
directly, instead always usethis.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 thesetState
method can be asynchronous. There is an alternative syntax for thesetState
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 for further details.", "
", "There is abutton
element in the code editor which has anonClick()
handler. This handler is triggered when thebutton
receives a click event in the browser, and runs thehandleClick
method defined onMyComponent
. Within thehandleClick
method, update the componentstate
usingthis.setState()
. Set thename
property instate
to equal the stringReact 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." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "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 (", "", " ", "", " );", " }", "};" ], "tail": [ "ReactDOM.render({this.state.name}
", ", document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": "The state of MyComponent
should initialize with the key value pair{ name: Initial State }
.", "testString": "assert(Enzyme.mount(React.createElement(MyComponent)).state('name') === 'Initial State', 'The state ofMyComponent
should initialize with the key value pair{ name: Initial State }
.');" }, { "text": "MyComponent
should render anh1
header.", "testString": "assert(Enzyme.mount(React.createElement(MyComponent)).find('h1').length === 1, 'MyComponent
should render anh1
header.');" }, { "text": "The renderedh1
header should contain text rendered from the component's state.", "testString": "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(/TestName<\\/h1>/.test(firstValue), 'The rendered
h1
header should contain text rendered from the component's state.'); };" }, { "text": "Calling thehandleClick
method onMyComponent
should set the name property in state to equalReact Rocks!
.", "testString": "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!', 'Calling thehandleClick
method onMyComponent
should set the name property in state to equalReact Rocks!
.'); };" } ], "solutions": [ "class MyComponent extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n name: 'Initial State'\n };\n this.handleClick = this.handleClick.bind(this);\n }\n handleClick() {\n // change code below this line\n this.setState({\n name: 'React Rocks!'\n });\n // change code above this line\n }\n render() {\n return (\n\n \n\n );\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d4036174", "title": "Bind 'this' to a Class Method", "releasedOn": "December 25, 2017", "description": [ "In addition to setting and updating{this.state.name}
\nstate
, you can also define methods for your component class. A class method typically needs to use thethis
keyword so it can access properties on the class (such asstate
andprops
) inside the scope of the method. There are a few ways to allow your class methods to accessthis
.", "One common way is to explicitly bindthis
in the constructor sothis
becomes bound to the class methods when the component is initialized. You may have noticed the last challenge usedthis.handleClick = this.handleClick.bind(this)
for itshandleClick
method in the constructor. Then, when you call a function likethis.setState()
within your class method,this
refers to the class and will not beundefined
.", "Note: Thethis
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 ofthis
so please refer to other lessons if the above is confusing!", "
", "The code editor has a component with astate
that keeps track of an item count. It also has a method which allows you to increment this item count. However, the method doesn't work because it's using thethis
keyword that is undefined. Fix it by explicitly bindingthis
to theaddItem()
method in the component's constructor.", "Next, add a click handler to thebutton
element in the render method. It should trigger theaddItem()
method when the button receives a click event. Remember that the method you pass to theonClick
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 the item count increment in the HTML." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "class MyComponent extends React.Component {", " constructor(props) {", " super(props);", " this.state = {", " itemCount: 0", " };", " // change code below this line", "", " // change code above this line", " }", " addItem() {", " this.setState({", " itemCount: this.state.itemCount + 1", " });", " }", " render() {", " return (", "", " { /* change code below this line */ }", " ", " { /* change code above this line */ }", "", " );", " }", "};" ], "tail": [ "ReactDOM.render(Current Item Count: {this.state.itemCount}
", ", document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": " MyComponent
should return adiv
element which wraps two elements, a button and anh1
element, in that order.", "testString": "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', 'MyComponent
should return adiv
element which wraps two elements, a button and anh1
element, in that order.');" }, { "text": "The state ofMyComponent
should initialize with the key value pair{ itemCount: 0 }
.", "testString": "assert(Enzyme.mount(React.createElement(MyComponent)).state('itemCount') === 0, 'The state ofMyComponent
should initialize with the key value pair{ itemCount: 0 }
.');" }, { "text": "Clicking thebutton
element should run theaddItem
method and increment the stateitemCount
by1
.", "testString": "async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); const first = () => { mockedComponent.setState({ itemCount: 0 }); return waitForIt(() => mockedComponent.state('itemCount')); }; const second = () => { mockedComponent.find('button').simulate('click'); return waitForIt(() => mockedComponent.state('itemCount')); }; const firstValue = await first(); const secondValue = await second(); assert(firstValue === 0 && secondValue === 1, 'Clicking thebutton
element should run theaddItem
method and increment the stateitemCount
by1
.'); };" } ], "solutions": [ "class MyComponent extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n itemCount: 0\n };\n this.addItem = this.addItem.bind(this);\n }\n addItem() {\n this.setState({\n itemCount: this.state.itemCount + 1\n });\n }\n render() {\n return (\n\n \n\n );\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d4036176", "title": "Use State to Toggle an Element", "releasedOn": "December 25, 2017", "description": [ "You can useCurrent Item Count: {this.state.itemCount}
\nstate
in React applications in more complex ways than what you've seen so far. One example is to monitor the status of a value, then render the UI conditionally based on this value. There are several different ways to accomplish this, and the code editor shows one method.", "
", "MyComponent
has avisibility
property which is initialized tofalse
. The render method returns one view if the value ofvisibility
is true, and a different view if it is false.", "Currently, there is no way of updating thevisibility
property in the component'sstate
. The value should toggle back and forth between true and false. There is a click handler on the button which triggers a class method calledtoggleVisibility()
. Define this method so thestate
ofvisibility
toggles to the opposite value when the method is called. Ifvisibility
isfalse
, the method sets it totrue
, and vice versa.", "Finally, click the button to see the conditional rendering of the component based on itsstate
.", "Hint: Don't forget to bind thethis
keyword to the method in the constructor!" ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "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 (", "", " ", "", " );", " } else {", " return (", "Now you see me!
", "", " ", "", " );", " }", " }", "};" ], "tail": [ "ReactDOM.render(, document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": " MyComponent
should return adiv
element which contains abutton
.", "testString": "assert.strictEqual(Enzyme.mount(React.createElement(MyComponent)).find('div').find('button').length, 1, 'MyComponent
should return adiv
element which contains abutton
.');" }, { "text": "The state ofMyComponent
should initialize with avisibility
property set tofalse
.", "testString": "assert.strictEqual(Enzyme.mount(React.createElement(MyComponent)).state('visibility'), false, 'The state ofMyComponent
should initialize with avisibility
property set tofalse
.');" }, { "text": "Clicking the button element should toggle thevisibility
property in state betweentrue
andfalse
.", "testString": "async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); const first = () => { mockedComponent.setState({ visibility: false }); return waitForIt(() => mockedComponent.state('visibility')); }; const second = () => { mockedComponent.find('button').simulate('click'); return waitForIt(() => mockedComponent.state('visibility')); }; const third = () => { mockedComponent.find('button').simulate('click'); return waitForIt(() => mockedComponent.state('visibility')); }; const firstValue = await first(); const secondValue = await second(); const thirdValue = await third(); assert(!firstValue && secondValue && !thirdValue, 'Clicking the button element should toggle thevisibility
property in state betweentrue
andfalse
.'); }; " } ], "solutions": [ "class MyComponent extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n visibility: false\n };\n this.toggleVisibility = this.toggleVisibility.bind(this);\n }\n toggleVisibility() {\n this.setState({\n visibility: !this.state.visibility\n });\n }\n render() {\n if (this.state.visibility) {\n return (\n\n \n\n );\n } else {\n return (\nNow you see me!
\n\n \n\n );\n }\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d4036177", "title": "Write a Simple Counter", "releasedOn": "December 25, 2017", "description": [ "You can design a more complex stateful component by combining the concepts covered so far. These include initializingstate
, writing methods that setstate
, and assigning click handlers to trigger these methods.", "
", "TheCounter
component keeps track of acount
value instate
. There are two buttons which call methodsincrement()
anddecrement()
. Write these methods so the counter value is incremented or decremented by 1 when the appropriate button is clicked. Also, create areset()
method so when the reset button is clicked, the count is set to 0.", "Note: Make sure you don't modify theclassNames
of the buttons. Also, remember to add the necessary bindings for the newly-created methods in the constructor." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "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 (", "", " ", " ", " ", "", " );", " }", "};" ], "tail": [ "ReactDOM.render(Current Count: {this.state.count}
", ", document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": " Counter
should return adiv
element which contains three buttons with text content in this orderIncrement!
,Decrement!
,Reset
.", "testString": "assert((() => { const mockedComponent = Enzyme.mount(React.createElement(Counter)); return (mockedComponent.find('.inc').text() === 'Increment!' && mockedComponent.find('.dec').text() === 'Decrement!' && mockedComponent.find('.reset').text() === 'Reset'); })(), 'Counter
should return adiv
element which contains three buttons with text content in this orderIncrement!
,Decrement!
,Reset
.');" }, { "text": "The state ofCounter
should initialize with acount
property set to0
.", "testString": "assert.strictEqual(Enzyme.mount(React.createElement(Counter)).state('count'), 0, 'The state ofCounter
should initialize with acount
property set to0
.');" }, { "text": "Clicking the increment button should increment the count by1
.", "testString": "async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(Counter)); const first = () => { mockedComponent.setState({ count: 0 }); return waitForIt(() => mockedComponent.state('count')); }; const second = () => { mockedComponent.find('.inc').simulate('click'); return waitForIt(() => mockedComponent.state('count')); }; const firstValue = await first(); const secondValue = await second(); assert(firstValue === 0 && secondValue === 1, 'Clicking the increment button should increment the count by1
.'); }; " }, { "text": "Clicking the decrement button should decrement the count by1
.", "testString": "async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(Counter)); const first = () => { mockedComponent.setState({ count: 0 }); return waitForIt(() => mockedComponent.state('count')); }; const second = () => { mockedComponent.find('.dec').simulate('click'); return waitForIt(() => mockedComponent.state('count')); }; const firstValue = await first(); const secondValue = await second(); assert(firstValue === 0 && secondValue === -1, 'Clicking the decrement button should decrement the count by1
.'); }; " }, { "text": "Clicking the reset button should reset the count to0
.", "testString": "async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(Counter)); const init = () => { mockedComponent.setState({ count: 0 }); return waitForIt(() => mockedComponent.state('count')); }; const increment = () => { mockedComponent.find('.inc').simulate('click'); mockedComponent.find('.inc').simulate('click'); return waitForIt(() => mockedComponent.state('count')); }; const decrement = () => { mockedComponent.find('.dec').simulate('click'); return waitForIt(() => mockedComponent.state('count')); }; const reset = () => { mockedComponent.find('.reset').simulate('click'); return waitForIt(() => mockedComponent.state('count')); }; const firstValue = await init(); const secondValue = await increment(); const thirdValue = await decrement(); const fourthValue = await reset(); assert(firstValue === 0 && secondValue === 2 && thirdValue === 1 && fourthValue === 0, 'Clicking the reset button should reset the count to0
.'); }; " } ], "solutions": [ "class Counter extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n count: 0\n };\n this.increment = this.increment.bind(this);\n this.decrement = this.decrement.bind(this);\n this.reset = this.reset.bind(this);\n }\n reset() {\n this.setState({\n count: 0\n });\n }\n increment() {\n this.setState({\n count: this.state.count + 1\n });\n }\n decrement() {\n this.setState({\n count: this.state.count - 1\n });\n }\n render() {\n return (\n\n \n \n \n\n );\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d4036178", "title": "Create a Controlled Input", "releasedOn": "December 25, 2017", "description": [ "Your application may have more complex interactions betweenCurrent Count: {this.state.count}
\nstate
and the rendered UI. For example, form control elements for text input, such asinput
andtextarea
, maintain their own state in the DOM as the user types. With React, you can move this mutatable state into a React component'sstate
. The user's input becomes part of the applicationstate
, 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.", "
", "The code editor has the skeleton of a component calledControlledInput
to create a controlledinput
element. The component'sstate
is already initialized with aninput
property that holds an empty string. This value represents the text a user types into theinput
field.", "First, create a method calledhandleChange()
that has a parameter calledevent
. When the method is called, it receives anevent
object that contains a string of text from theinput
element. You can access this string withevent.target.value
inside the method. Update theinput
property of the component'sstate
with this new string.", "In the render method, create theinput
element above theh4
tag. Add avalue
attribute which is equal to theinput
property of the component'sstate
. Then add anonChange()
event handler set to thehandleChange()
method.", "When you type in the input box, that text is processed by thehandleChange()
method, set as theinput
property in the localstate
, and rendered as the value in theinput
box on the page. The componentstate
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." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "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 (", "", " { /* change code below this line */}", "", " { /* change code above this line */}", "", " );", " }", "};" ], "tail": [ "ReactDOM.render(Controlled Input:
", "{this.state.input}
", ", document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": " ControlledInput
should return adiv
element which contains aninput
and ap
tag.", "testString": "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, 'ControlledInput
should return adiv
element which contains aninput
and ap
tag.');" }, { "text": "The state ofControlledInput
should initialize with aninput
property set to an empty string.", "testString": "assert.strictEqual(Enzyme.mount(React.createElement(ControlledInput)).state('input'), '', 'The state ofControlledInput
should initialize with aninput
property set to an empty string.');" }, { "text": "Typing in the input element should update the state and the value of the input, and thep
element should render this state as you type.", "testString": "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', 'Typing in the input element should update the state and the value of the input, and thep
element should render this state as you type.'); }; " } ], "solutions": [ "class ControlledInput extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n input: ''\n };\n this.handleChange = this.handleChange.bind(this);\n }\n handleChange(event) {\n this.setState({\n input: event.target.value\n });\n }\n render() {\n return (\n\n \n\n );\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d4036179", "title": "Create a Controlled Form", "releasedOn": "December 25, 2017", "description": [ "The last challenge showed that React can control the internal state for certain elements likeControlled Input:
\n\n{this.state.input}
\ninput
andtextarea
, which makes them controlled components. This applies to other form elements as well, including the regular HTMLform
element.", "
", "TheMyForm
component is set up with an emptyform
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 thetype
set tosubmit
indicating it is the button controlling the form. Add theinput
element in theform
and set itsvalue
andonChange()
attributes like the last challenge. You should then complete thehandleSubmit
method so that it sets the component state propertysubmit
to the current input value in the localstate
.", "Note: You also must callevent.preventDefault()
in the submit handler, to prevent the default form submit behavior which will refresh the web page.", "Finally, create anh1
tag after theform
which renders thesubmit
value from the component'sstate
. You can then type in the form and click the button (or press enter), and you should see your input rendered to the page." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "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 (", "", " ", " { /* change code below this line */ }", "", " { /* change code above this line */ }", "", " );", " }", "};" ], "tail": [ "ReactDOM.render(, document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": " MyForm
should return adiv
element which contains aform
and anh1
tag. The form should include aninput
and abutton
.", "testString": "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) })(), 'MyForm
should return adiv
element which contains aform
and anh1
tag. The form should include aninput
and abutton
.');" }, { "text": "The state ofMyForm
should initialize withinput
andsubmit
properties, both set to empty strings.", "testString": "assert(Enzyme.mount(React.createElement(MyForm)).state('input') === '' && Enzyme.mount(React.createElement(MyForm)).state('submit') === '', 'The state ofMyForm
should initialize withinput
andsubmit
properties, both set to empty strings.');" }, { "text": "Typing in theinput
element should update theinput
property of the component's state.", "testString": "async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(MyForm)); 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'), inputVal: mockedComponent.find('input').props().value }))}; const before = await _1(); const after = await _2(); assert(before === '' && after.state === 'TestInput' && after.inputVal === 'TestInput', 'Typing in theinput
element should update theinput
property of the component's state.'); }; " }, { "text": "Submitting the form should runhandleSubmit
which should set thesubmit
property in state equal to the current input.", "testString": "async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(MyForm)); const _1 = () => { mockedComponent.setState({ input: '' }); mockedComponent.setState({submit: ''}); mockedComponent.find('input').simulate('change', {target: {value: 'SubmitInput'}}); return waitForIt(() => mockedComponent.state('submit'))}; const _2 = () => { mockedComponent.find('form').simulate('submit'); return waitForIt(() => mockedComponent.state('submit'))}; const before = await _1(); const after = await _2(); assert(before === '' && after === 'SubmitInput', 'Submitting the form should runhandleSubmit
which should set thesubmit
property in state equal to the current input.'); };" }, { "text": "Theh1
header should render the value of thesubmit
field from the component's state.", "testString": "async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(MyForm)); const _1 = () => { mockedComponent.setState({ input: '' }); mockedComponent.setState({submit: ''}); mockedComponent.find('input').simulate('change', {target: {value: 'TestInput'}}); return waitForIt(() => mockedComponent.find('h1').text())}; const _2 = () => { mockedComponent.find('form').simulate('submit'); return waitForIt(() => mockedComponent.find('h1').text())}; const before = await _1(); const after = await _2(); assert(before === '' && after === 'TestInput', 'Theh1
header should render the value of thesubmit
field from the component's state.'); }; " } ], "solutions": [ "class MyForm extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n input: '',\n submit: ''\n };\n this.handleChange = this.handleChange.bind(this);\n this.handleSubmit = this.handleSubmit.bind(this);\n }\n handleChange(event) {\n this.setState({\n input: event.target.value\n });\n }\n handleSubmit(event) {\n event.preventDefault()\n this.setState({\n submit: this.state.input\n });\n }\n render() {\n return (\n\n \n\n );\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d403617a", "title": "Pass State as Props to Child Components", "releasedOn": "December 25, 2017", "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{this.state.submit}
\nstate
important to your app, that then renders child components. You want these components to have access to some pieces of thatstate
, which are passed in as props.", "For example, maybe you have anApp
component that renders aNavbar
, among other components. In yourApp
, you havestate
that contains a lot of user information, but theNavbar
only needs access to the user's username so it can display it. You pass that piece ofstate
to theNavbar
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.", "
", "TheMyApp
component is stateful and renders aNavbar
component as a child. Pass thename
property in itsstate
down to the child component, then show thename
in theh1
tag that's part of theNavbar
render method." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "class MyApp extends React.Component {", " constructor(props) {", " super(props);", " this.state = {", " name: 'CamperBot'", " }", " }", " render() {", " return (", "", "", " );", " }", "};", "", "class Navbar extends React.Component {", " constructor(props) {", " super(props);", " }", " render() {", " return (", "", " ", "", " );", " }", "};" ], "tail": [ "ReactDOM.render(Hello, my name is: /* your code here */
", ", document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": "The MyApp
component should render with aNavbar
component inside.", "testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyApp)); return mockedComponent.find('MyApp').length === 1 && mockedComponent.find('Navbar').length === 1; })(), 'TheMyApp
component should render with aNavbar
component inside.');" }, { "text": "TheNavbar
component should receive theMyApp
state propertyname
as props.", "testString": "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', 'TheNavbar
component should receive theMyApp
state propertyname
as props.'); }; " }, { "text": "Theh1
element inNavbar
should render thename
prop.", "testString": "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, 'Theh1
element inNavbar
should render thename
prop.'); }; " } ], "solutions": [ "class MyApp extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n name: 'CamperBot'\n }\n }\n render() {\n return (\n\n\n );\n }\n};\nclass Navbar extends React.Component {\n constructor(props) {\n super(props);\n }\n render() {\n return (\n\n \n\n );\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d403617b", "title": "Pass a Callback as Props", "releasedOn": "December 25, 2017", "description": [ "You can passHello, my name is: {this.props.name}
\nstate
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 underthis.props
in the child component.", "
", "There are three components outlined in the code editor. TheMyApp
component is the parent that will render theGetInput
andRenderInput
child components. Add theGetInput
component to the render method inMyApp
, then pass it a prop calledinput
assigned toinputValue
fromMyApp
'sstate
. Also create a prop calledhandleChange
and pass the input handlerhandleChange
to it.", "Next, addRenderInput
to the render method inMyApp
, then create a prop calledinput
and pass theinputValue
fromstate
to it. Once you are finished you will be able to type in theinput
field in theGetInput
component, which then calls the handler method in its parent via props. This updates the input in thestate
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 thestate
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." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "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 (", "", " { /* change code below this line */ }", "", " { /* change code above this line */ }", "", " );", " }", "};", "", "class GetInput extends React.Component {", " constructor(props) {", " super(props);", " }", " render() {", " return (", "", "", " );", " }", "};", "", "class RenderInput extends React.Component {", " constructor(props) {", " super(props);", " }", " render() {", " return (", "Get Input:
", " ", "", "", " );", " }", "};" ], "tail": [ "ReactDOM.render(Input Render:
", "{this.props.input}
", ", document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": "The MyApp
component should render.", "testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyApp)); return mockedComponent.find('MyApp').length === 1; })(), 'TheMyApp
component should render.');" }, { "text": "TheGetInput
component should render.", "testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyApp)); return mockedComponent.find('GetInput').length === 1; })(), 'TheGetInput
component should render.');" }, { "text": "TheRenderInput
component should render.", "testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyApp)); return mockedComponent.find('RenderInput').length === 1; })(), 'TheRenderInput
component should render.');" }, { "text": "TheGetInput
component should receive theMyApp
state propertyinputValue
as props and contain aninput
element which modifiesMyApp
state.", "testString": "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', 'TheGetInput
component should receive theMyApp
state propertyinputValue
as props and contain aninput
element which modifiesMyApp
state.'); }; " }, { "text": "TheRenderInput
component should receive theMyApp
state propertyinputValue
as props.", "testString": "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'), 'TheRenderInput
component should receive theMyApp
state propertyinputValue
as props.'); }; " } ], "solutions": [ "class MyApp extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n inputValue: ''\n }\n this.handleChange = this.handleChange.bind(this); \n }\n handleChange(event) {\n this.setState({\n inputValue: event.target.value\n });\n }\n render() {\n return (\n\n\n );\n }\n};\n\nclass GetInput extends React.Component {\n constructor(props) {\n super(props);\n }\n render() {\n return (\n\n \n \n\n );\n }\n};\n\nclass RenderInput extends React.Component {\n constructor(props) {\n super(props);\n }\n render() {\n return (\nGet Input:
\n \n\n\n );\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d403617c", "title": "Use the Lifecycle Method componentWillMount", "releasedOn": "December 25, 2017", "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:", "Input Render:
\n{this.props.input}
\ncomponentWillMount()
", "componentDidMount()
", "componentWillReceiveProps()
", "shouldComponentUpdate()
", "componentWillUpdate()
", "componentDidUpdate()
", "componentWillUnmount()
", "The next several lessons will cover some of the basic use cases for these lifecycle methods.", "
", "ThecomponentWillMount()
method is called before therender()
method when a component is being mounted to the DOM. Log something to the console withincomponentWillMount()
- you may want to have your browser console open to see the output." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "class MyComponent extends React.Component {", " constructor(props) {", " super(props);", " }", " componentWillMount() {", " // change code below this line", "", " // change code above this line", " }", " render() {", " return ", " }", "};" ], "tail": [ "ReactDOM.render(, document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": " MyComponent
should render adiv
element.", "testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return mockedComponent.find('div').length === 1; })(), 'MyComponent
should render adiv
element.');" }, { "text": "console.log
should be called incomponentWillMount
.", "testString": "assert((function() { const lifecycle = React.createElement(MyComponent).type.prototype.componentWillMount.toString().replace(/ /g,''); return lifecycle.includes('console.log('); })(), 'console.log
should be called incomponentWillMount
.');" } ], "solutions": [ "class MyComponent extends React.Component {\n constructor(props) {\n super(props);\n }\n componentWillMount() {\n // change code below this line\n console.log('Component is mounting...');\n // change code above this line\n }\n render() {\n return \n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d403617d", "title": "Use the Lifecycle Method componentDidMount", "releasedOn": "December 25, 2017", "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 methodcomponentDidMount()
. This method is called after a component is mounted to the DOM. Any calls tosetState()
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.", "
", "There is a mock API call incomponentDidMount()
. 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 ofactiveUsers
in theh1
. Watch what happens in the preview, and feel free to change the timeout to see the different effects." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "class MyComponent extends React.Component {", " constructor(props) {", " super(props);", " this.state = {", " activeUsers: null", " };", " }", " componentDidMount() {", " setTimeout( () => {", " this.setState({", " activeUsers: 1273", " });", " }, 2500);", " }", " render() {", " return (", "", "", " );", " }", "};" ], "tail": [ "ReactDOM.render(Active Users: { /* change code here */ }
", ", document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": " MyComponent
should render adiv
element which wraps anh1
tag.", "testString": "assert((() => { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return (mockedComponent.find('div').length === 1 && mockedComponent.find('h1').length === 1); })(), 'MyComponent
should render adiv
element which wraps anh1
tag.');" }, { "text": "Component state should be updated with a timeout function incomponentDidMount
.", "testString": "assert((() => { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return new RegExp('setTimeout(.|\\n)+setState(.|\\n)+activeUsers').test(String(mockedComponent.instance().componentDidMount)); })(), 'Component state should be updated with a timeout function incomponentDidMount
.');" }, { "text": "Theh1
tag should render theactiveUsers
value fromMyComponent
's state.", "testString": "async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); const first = () => { mockedComponent.setState({ activeUsers: 1237 }); return waitForIt(() => mockedComponent.find('h1').text()); }; const second = () => { mockedComponent.setState({ activeUsers: 1000 }); return waitForIt(() => mockedComponent.find('h1').text()); }; const firstValue = await first(); const secondValue = await second(); assert(new RegExp('1237').test(firstValue) && new RegExp('1000').test(secondValue), 'Theh1
tag should render theactiveUsers
value fromMyComponent
's state.'); }; " } ], "solutions": [ "class MyComponent extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n activeUsers: null\n };\n }\n componentDidMount() {\n setTimeout( () => {\n this.setState({\n activeUsers: 1273\n });\n }, 2500);\n }\n render() {\n return (\n\n\n );\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d403617e", "title": "Add Event Listeners", "releasedOn": "December 25, 2017", "description": [ "TheActive Users: {this.state.activeUsers}
\ncomponentDidMount()
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 asonClick()
. 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.", "
", "Attach an event listener in thecomponentDidMount()
method forkeydown
events and have these events trigger the callbackhandleKeyPress()
. You can usedocument.addEventListener()
which takes the event (in quotes) as the first argument and the callback as the second argument.", "Then, incomponentWillUnmount()
, remove this same event listener. You can pass the same arguments todocument.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." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "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({", " message: this.state.message + 'You pressed the enter key! '", " });", " }", " handleKeyPress(event) {", " if (event.keyCode === 13) {", " this.handleEnter();", " }", " }", " render() {", " return (", "", "", " );", " }", "};" ], "tail": [ "ReactDOM.render({this.state.message}
", ", document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": " MyComponent
should render adiv
element which wraps anh1
tag.", "testString": "assert((() => { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return mockedComponent.find('div').children().find('h1').length === 1; })(), 'MyComponent
should render adiv
element which wraps anh1
tag.');" }, { "text": "A keydown listener should be attached to the document incomponentDidMount
.", "testString": "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); })(), 'A keydown listener should be attached to the document incomponentDidMount
.');" }, { "text": "The keydown listener should be removed from the document incomponentWillUnmount
.", "testString": "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); })(), 'The keydown listener should be removed from the document incomponentWillUnmount
.');" }, { "text": "Once the component has mounted, pressingenter
should update its state and the renderedh1
tag.", "testString": "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, 'Once the component has mounted, pressingenter
should update its state and the renderedh1
tag.'); }; " } ], "solutions": [ "class MyComponent extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n message: ''\n };\n this.handleKeyPress = this.handleKeyPress.bind(this);\n this.handleEnter = this.handleEnter.bind(this); }\n componentDidMount() {\n // change code below this line\n document.addEventListener('keydown', this.handleKeyPress);\n // change code above this line\n }\n componentWillUnmount() {\n // change code below this line\n document.removeEventListener('keydown', this.handleKeyPress);\n // change code above this line\n }\n handleEnter() {\n this.setState({\n message: this.state.message + 'You pressed the enter key! '\n });\n }\n handleKeyPress(event) {\n if (event.keyCode === 13) {\n this.handleEnter();\n }\n }\n render() {\n return (\n\n\n );\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d403617f", "title": "Manage Updates with Lifecycle Methods", "releasedOn": "December 25, 2017", "description": [ "Another lifecycle method is{this.state.message}
\ncomponentWillReceiveProps()
which is called whenever a component is receiving new props. This method receives the new props as an argument, which is usually written asnextProps
. You can use this argument and compare withthis.props
and perform actions before the component updates. For example, you may callsetState()
locally before the update is processed.", "Another method iscomponentDidUpdate()
, and is called immediately after a component re-renders. Note that rendering and mounting are considered different things in the component lifecycle. When a page first loads, all components are mounted and this is where methods likecomponentWillMount()
andcomponentDidMount()
are called. After this, as state changes, components re-render themselves. The next challenge covers this in more detail.", "
", "The child componentDialog
receivesmessage
props from its parent, theController
component. Write thecomponentWillReceiveProps()
method in theDialog
component and have it logthis.props
andnextProps
to the console. You'll need to passnextProps
as an argument to this method and although it's possible to name it anything, name itnextProps
here.", "Next, addcomponentDidUpdate()
in theDialog
component, and log a statement that says the component has updated. This method works similar tocomponentWillUpdate()
, which is provided for you. Now click the button to change the message and watch your browser console. The order of the console statements show the order the methods are called.", "Note: You'll need to write the lifecycle methods as normal functions and not as arrow functions to pass the tests (there is also no advantage to writing lifecycle methods as arrow functions)." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "class Dialog extends React.Component {", " constructor(props) {", " super(props);", " }", " componentWillUpdate() {", " console.log('Component is about to update...');", " }", " // change code below this line", "", " // change code above this line", " render() {", " return{this.props.message}
", " }", "};", "", "class Controller extends React.Component {", " constructor(props) {", " super(props);", " this.state = {", " message: 'First Message'", " };", " this.changeMessage = this.changeMessage.bind(this);", " }", " changeMessage() {", " this.setState({", " message: 'Second Message'", " });", " }", " render() {", " return (", "", " ", "", " );", " }", "};" ], "tail": [ "ReactDOM.render(, document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": "The Controller
component should render theDialog
component as a child.", "testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(Controller)); return mockedComponent.find('Controller').length === 1 && mockedComponent.find('Dialog').length === 1; })(), 'TheController
component should render theDialog
component as a child.');" }, { "text": "ThecomponentWillReceiveProps
method in theDialog
component should logthis.props
to the console.", "testString": "assert((function() { const lifecycleChild = React.createElement(Dialog).type.prototype.componentWillReceiveProps.toString().replace(/ /g,''); return lifecycleChild.includes('console.log') && lifecycleChild.includes('this.props') })(), 'ThecomponentWillReceiveProps
method in theDialog
component should logthis.props
to the console.');" }, { "text": "ThecomponentWillReceiveProps
method in theDialog
component should lognextProps
to the console.", "testString": "assert((function() { const lifecycleChild = React.createElement(Dialog).type.prototype.componentWillReceiveProps.toString().replace(/ /g,''); const nextPropsAsParameterTest = /componentWillReceiveProps(| *?= *?)(\\(|)nextProps(\\)|)( *?=> *?{| *?{|{)/; const nextPropsInConsoleLogTest = /console\\.log\\(.*?nextProps\\b.*?\\)/; return ( lifecycleChild.includes('console.log') && nextPropsInConsoleLogTest.test(lifecycleChild) && nextPropsAsParameterTest.test(lifecycleChild) ); })(), 'ThecomponentWillReceiveProps
method in theDialog
component should lognextProps
to the console.');" }, { "text": "TheDialog
component should call thecomponentDidUpdate
method and log a message to the console.", "testString": "assert((function() { const lifecycleChild = React.createElement(Dialog).type.prototype.componentDidUpdate.toString().replace(/ /g,''); return lifecycleChild.length !== 'undefined' && lifecycleChild.includes('console.log'); })(), 'TheDialog
component should call thecomponentDidUpdate
method and log a message to the console.');" } ], "solutions": [ "class Dialog extends React.Component {\n constructor(props) {\n super(props);\n }\n componentWillUpdate() {\n console.log('Component is about to update...');\n }\n // change code below this line\n componentWillReceiveProps(nextProps) {\n console.log(this.props, nextProps);\n }\n componentDidUpdate() {\n console.log('Component re-rendered');\n }\n // change code above this line\n render() {\n return{this.props.message}
\n }\n};\n\nclass Controller extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n message: 'First Message'\n };\n this.changeMessage = this.changeMessage.bind(this); \n }\n changeMessage() {\n this.setState({\n message: 'Second Message'\n });\n }\n render() {\n return (\n\n \n\n );\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d4036180", "title": "Optimize Re-Renders with shouldComponentUpdate", "releasedOn": "December 25, 2017", "description": [ "So far, if any component receives newstate
or newprops
, 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 newstate
orprops
, and declare specifically if the components should update or not. The method isshouldComponentUpdate()
, and it takesnextProps
andnextState
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 newprops
, even if theprops
haven't changed. You can useshouldComponentUpdate()
to prevent this by comparing theprops
. The method must return aboolean
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 returntrue
orfalse
accordingly.", "
", "TheshouldComponentUpdate()
method is added in a component calledOnlyEvens
. Currently, this method returnstrue
soOnlyEvens
re-renders every time it receives newprops
. Modify the method soOnlyEvens
updates only if thevalue
of its new props is even. Click theAdd
button and watch the order of events in your browser's console as the other lifecycle hooks are triggered." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "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", " }", " componentWillReceiveProps(nextProps) {", " console.log('Receiving new props...');", " }", " componentDidUpdate() {", " console.log('Component re-rendered.');", " }", " render() {", " return{this.props.value}
", " }", "};", "", "class Controller extends React.Component {", " constructor(props) {", " super(props);", " this.state = {", " value: 0", " };", " this.addValue = this.addValue.bind(this);", " }", " addValue() {", " this.setState({", " value: this.state.value + 1", " });", " }", " render() {", " return (", "", " ", "", " );", " }", "};" ], "tail": [ "ReactDOM.render(", " , document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": "The Controller
component should render theOnlyEvens
component as a child.", "testString": "assert((() => { const mockedComponent = Enzyme.mount(React.createElement(Controller)); return mockedComponent.find('Controller').length === 1 && mockedComponent.find('OnlyEvens').length === 1; })(), 'TheController
component should render theOnlyEvens
component as a child.');" }, { "text": "TheshouldComponentUpdate
method should be defined on theOnlyEvens
component.", "testString": "assert((() => { const child = React.createElement(OnlyEvens).type.prototype.shouldComponentUpdate.toString().replace(/s/g,''); return child !== 'undefined'; })(), 'TheshouldComponentUpdate
method should be defined on theOnlyEvens
component.');" }, { "text": "TheOnlyEvens
component should return anh1
tag which renders the value ofthis.props.value
.", "testString": "async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(Controller)); const first = () => { mockedComponent.setState({ value: 1000 }); return waitForIt(() => mockedComponent.find('h1').html()); }; const second = () => { mockedComponent.setState({ value: 10 }); return waitForIt(() => mockedComponent.find('h1').html()); }; const firstValue = await first(); const secondValue = await second(); assert(firstValue === '1000
' && secondValue === '10
', 'TheOnlyEvens
component should return anh1
tag which renders the value ofthis.props.value
.'); }; " }, { "text": "OnlyEvens
should re-render only whennextProps.value
is even.", "testString": "async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(Controller)); const first = () => { mockedComponent.setState({ value: 8 }); return waitForIt(() => mockedComponent.find('h1').text()); }; const second = () => { mockedComponent.setState({ value: 7 }); return waitForIt(() => mockedComponent.find('h1').text()); }; const third = () => { mockedComponent.setState({ value: 42 }); return waitForIt(() => mockedComponent.find('h1').text()); }; const firstValue = await first(); const secondValue = await second(); const thirdValue = await third(); assert(firstValue === '8' && secondValue === '8' && thirdValue === '42', 'OnlyEvens
should re-render only whennextProps.value
is even.'); }; " } ], "solutions": [ "class OnlyEvens extends React.Component {\n constructor(props) {\n super(props);\n }\n shouldComponentUpdate(nextProps, nextState) {\n console.log('Should I update?');\n // change code below this line\n return nextProps.value % 2 === 0;\n // change code above this line\n }\n componentWillReceiveProps(nextProps) {\n console.log('Receiving new props...');\n }\n componentDidUpdate() {\n console.log('Component re-rendered.');\n }\n render() {\n return{this.props.value}
\n }\n};\n\nclass Controller extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n value: 0\n };\n this.addValue = this.addValue.bind(this);\n }\n addValue() {\n this.setState({\n value: this.state.value + 1\n });\n }\n render() {\n return (\n\n \n\n );\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d4036181", "title": "Introducing Inline Styles", "releasedOn": "December 25, 2017", "description": [ "There are other complex concepts that add powerful capabilities to your React code. But you may be wondering about the more simple problem of how to style those JSX elements you create in React. You likely know that it won't be exactly the same as working with HTML because of the way you apply classes to JSX elements.", "If you import styles from a stylesheet, it isn't much different at all. You apply a class to your JSX element using the\n className
attribute, and apply styles to the class in your stylesheet. Another option is to apply inline styles, which are very common in ReactJS development.", "You apply inline styles to JSX elements similar to how you do it in HTML, but with a few JSX differences. Here's an example of an inline style in HTML:", "<div style=\"color: yellow; font-size: 16px\">Mellow Yellow</div>
", "JSX elements use thestyle
attribute, but because of the way JSX is transpiled, you can't set the value to astring
. Instead, you set it equal to a JavaScriptobject
. Here's an example:", "<div style={{color: \"yellow\", fontSize: 16}}>Mellow Yellow</div>
", "Notice how we camelCase the \"fontSize\" property? This is because React will not accept kebab-case keys in the style object. React will apply the correct property name for us in the HTML.", "
", "Add astyle
attribute to thediv
in the code editor to give the text a color of red and font size of 72px.", "Note that you can optionally set the font size to be a number, omitting the units \"px\", or write it as \"72px\"." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "", "class Colorful extends React.Component {", " render() {", " return (", "Big Red", " );", " }", "};", "" ], "tail": [ "ReactDOM.render(, document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": "The component should render a div
element.", "testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(Colorful)); return mockedComponent.children().type() === 'div'; })(), 'The component should render adiv
element.');" }, { "text": "Thediv
element should have a color ofred
.", "testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(Colorful)); return mockedComponent.children().props().style.color === 'red'; })(), 'Thediv
element should have a color ofred
.');" }, { "text": "Thediv
element should have a font size of72px
.", "testString": "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'); })(), 'Thediv
element should have a font size of72px
.');" } ], "solutions": [ "class Colorful extends React.Component {\n render() {\n return (\nBig Red\n );\n }\n};\n" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d4036182", "title": "Add Inline Styles in React", "releasedOn": "December 25, 2017", "description": [ "You may have noticed in the last challenge that there were several other syntax differences from HTML inline styles in addition to thestyle
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 withfontSize
instead offont-size
. Hyphenated words likefont-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 (likeheight
,width
, andfontSize
) are assumed to be inpx
unless otherwise specified. If you want to useem
, for example, you wrap the value and the units in quotes, like{fontSize: \"4em\"}
. Other than the length values that default topx
, all other property values should be wrapped in quotes.", "
", "If you have a large set of styles, you can assign a styleobject
to a constant to keep your code organized. Uncomment thestyles
constant and declare anobject
with three style properties and their values. Give thediv
a color of\"purple\"
, a font-size of40
, and a border of\"2px solid purple\"
. Then set thestyle
attribute equal to thestyles
constant." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "", "// const styles =", "// change code above this line", "class Colorful extends React.Component {", " render() {", " // change code below this line", " return (", "Style Me!", " );", " // change code above this line", " }", "};", "" ], "tail": [ "ReactDOM.render(, document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": "The styles
variable should be anobject
with three properties.", "testString": "assert(Object.keys(styles).length === 3, 'Thestyles
variable should be anobject
with three properties.');" }, { "text": "Thestyles
variable should have acolor
property set to a value ofpurple
.", "testString": "assert(styles.color === 'purple', 'Thestyles
variable should have acolor
property set to a value ofpurple
.');" }, { "text": "Thestyles
variable should have afontSize
property set to a value of40
.", "testString": "assert(styles.fontSize === 40, 'Thestyles
variable should have afontSize
property set to a value of40
.');" }, { "text": "Thestyles
variable should have aborder
property set to a value of2px solid purple
.", "testString": "assert(styles.border === \"2px solid purple\", 'Thestyles
variable should have aborder
property set to a value of2px solid purple
.');" }, { "text": "The component should render adiv
element.", "testString": "assert((function() { const mockedComponent = Enzyme.shallow(React.createElement(Colorful)); return mockedComponent.type() === 'div'; })(), 'The component should render adiv
element.');" }, { "text": "Thediv
element should have its styles defined by thestyles
object.", "testString": "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\"); })(), 'Thediv
element should have its styles defined by thestyles
object.');" } ], "solutions": [ "const styles = {\n color: \"purple\",\n fontSize: 40,\n border: \"2px solid purple\"\n};\n// change code above this line\nclass Colorful extends React.Component {\n render() {\n // change code below this line\n return (\nStyle Me!\n // change code above this line\n );\n }\n};\n" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d4036183", "title": "Use Advanced JavaScript in React Render Method", "releasedOn": "December 25, 2017", "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 yourrender
methods, before thereturn
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 thereturn
statement, you place the variable name inside curly braces.", "
", "In the code provided, therender
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 theask
method, so each time the button is clicked a random number will be generated and stored as therandomIndex
in state. On line 52, delete the string\"change me!\"
and reassign theanswer
const so your code randomly accesses a different index of thepossibleAnswers
array each time the component updates. Finally, insert theanswer
const inside thep
tags." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "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 code here", " return (", "", "", " );", " }", "};" ], "tail": [ "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(
", "
", "Answer:
", "", " { /* change code below this line */ }", "", " { /* change code above this line */ }", "
", ", document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": "The MagicEightBall
component should exist and should render to the page.", "testString": "assert.strictEqual(Enzyme.mount(React.createElement(MagicEightBall)).find('MagicEightBall').length, 1, 'TheMagicEightBall
component should exist and should render to the page.');" }, { "text": "MagicEightBall
's first child should be aninput
element.", "testString": "assert.strictEqual(Enzyme.mount(React.createElement(MagicEightBall)).children().childAt(0).name(), 'input', 'MagicEightBall
's first child should be aninput
element.');" }, { "text": "MagicEightBall
's third child should be abutton
element.", "testString": "assert.strictEqual(Enzyme.mount(React.createElement(MagicEightBall)).children().childAt(2).name(), 'button', 'MagicEightBall
's third child should be abutton
element.');" }, { "text": "MagicEightBall
's state should be initialized with a property ofuserInput
and a property ofrandomIndex
both set to a value of an empty string.", "testString": "assert(Enzyme.mount(React.createElement(MagicEightBall)).state('randomIndex') === '' && Enzyme.mount(React.createElement(MagicEightBall)).state('userInput') === '', 'MagicEightBall
's state should be initialized with a property ofuserInput
and a property ofrandomIndex
both set to a value of an empty string.');" }, { "text": "WhenMagicEightBall
is first mounted to the DOM, it should return an emptyp
element.", "testString": "assert(Enzyme.mount(React.createElement(MagicEightBall)).find('p').length === 1 && Enzyme.mount(React.createElement(MagicEightBall)).find('p').text() === '', 'WhenMagicEightBall
is first mounted to the DOM, it should return an emptyp
element.');" }, { "text": "When text is entered into theinput
element and the button is clicked, theMagicEightBall
component should return ap
element that contains a random element from thepossibleAnswers
array.", "testString": "async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); 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 waitForIt(() => result()) }; const _2 = () => { simulate(); return waitForIt(() => result()) }; const _3 = () => { simulate(); return waitForIt(() => result()) }; const _4 = () => { simulate(); return waitForIt(() => result()) }; const _5 = () => { simulate(); return waitForIt(() => result()) }; const _6 = () => { simulate(); return waitForIt(() => result()) }; const _7 = () => { simulate(); return waitForIt(() => result()) }; const _8 = () => { simulate(); return waitForIt(() => result()) }; const _9 = () => { simulate(); return waitForIt(() => result()) }; const _10 = () => { simulate(); return waitForIt(() => result()) }; const _1_val = await _1(); const _2_val = await _2(); const _3_val = await _3(); const _4_val = await _4(); const _5_val = await _5(); const _6_val = await _6(); const _7_val = await _7(); const _8_val = await _8(); const _9_val = await _9(); const _10_val = await _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, 'When text is entered into theinput
element and the button is clicked, theMagicEightBall
component should return ap
element that contains a random element from thepossibleAnswers
array.');}" } ], "solutions": [ "const inputStyle = {\n width: 235,\n margin: 5\n}\n\nclass MagicEightBall extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n userInput: '',\n randomIndex: ''\n }\n this.ask = this.ask.bind(this);\n this.handleChange = this.handleChange.bind(this);\n }\n ask() {\n if (this.state.userInput) {\n this.setState({\n randomIndex: Math.floor(Math.random() * 20),\n userInput: ''\n });\n }\n }\n handleChange(event) {\n this.setState({\n userInput: event.target.value\n });\n }\n render() {\n const possibleAnswers = [\n \"It is certain\", \"It is decidedly so\", \"Without a doubt\",\n \"Yes, definitely\", \"You may rely on it\", \"As I see it, yes\",\n \"Outlook good\", \"Yes\", \"Signs point to yes\", \"Reply hazy try again\",\n \"Ask again later\", \"Better not tell you now\", \"Cannot predict now\",\n \"Concentrate and ask again\", \"Don't count on it\", \"My reply is no\",\n \"My sources say no\", \"Outlook not so good\",\"Very doubtful\", \"Most likely\"\n ];\n const answer = possibleAnswers[this.state.randomIndex];\n return (\n\n\n );\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d4036184", "title": "Render with an If/Else Condition", "releasedOn": "December 25, 2017", "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
\n
\nAnswer:
\n\n {answer}\n
\nif/else
statement in therender()
method of a React component.", "
", "MyComponent contains aboolean
in its state which tracks whether you want to display some element in the UI or not. Thebutton
toggles the state of this value. Currently, it renders the same UI every time. Rewrite therender()
method with anif/else
statement so that ifdisplay
istrue
, you return the current markup. Otherwise, return the markup without theh1
element.", "Note: You must write anif/else
to pass the tests. Use of the ternary operator will not pass here." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "class MyComponent extends React.Component {", " constructor(props) {", " super(props);", " this.state = {", " display: true", " }", " this.toggleDisplay = this.toggleDisplay.bind(this);", " }", " toggleDisplay() {", " this.setState({", " display: !this.state.display", " });", " }", " render() {", " // change code below this line", "", " return (", "", " ", "", " );", " }", "};" ], "tail": [ "ReactDOM.render(Displayed!
", ", document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": " MyComponent
should exist and render.", "testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return mockedComponent.find('MyComponent').length === 1; })(), 'MyComponent
should exist and render.');" }, { "text": "Whendisplay
is set totrue
, adiv
,button
, andh1
should render.", "testString": "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, 'Whendisplay
is set totrue
, adiv
,button
, andh1
should render.'); }; " }, { "text": "Whendisplay
is set tofalse
, only adiv
andbutton
should render.", "testString": "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, 'Whendisplay
is set tofalse
, only adiv
andbutton
should render.'); }; " }, { "text": "The render method should use anif/else
statement to check the condition ofthis.state.display
.", "testString": "getUserInput => assert(getUserInput('index').includes('if') && getUserInput('index').includes('else'), 'The render method should use anif/else
statement to check the condition ofthis.state.display
.');" } ], "solutions": [ "class MyComponent extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n display: true\n }\n this.toggleDisplay = this.toggleDisplay.bind(this); \n }\n toggleDisplay() {\n this.setState({\n display: !this.state.display\n });\n }\n render() {\n // change code below this line\n if (this.state.display) {\n return (\n\n \n\n );\n } else {\n return (\nDisplayed!
\n\n \n\n );\n }\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d4036185", "title": "Use && for a More Concise Conditional", "releasedOn": "December 25, 2017", "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 ofelse 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 istrue
, and if it is, return some markup. Here's an example:", "{condition && <p>markup</p>}
", "If thecondition
istrue
, the markup will be returned. If the condition isfalse
, the operation will immediately returnfalse
after evaluating thecondition
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 yourrender()
method without repeating a lot of code.", "
", "Solve the previous example again, so theh1
only renders ifdisplay
istrue
, but use the&&
logical operator instead of anif/else
statement." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "class MyComponent extends React.Component {", " constructor(props) {", " super(props);", " this.state = {", " display: true", " }", " this.toggleDisplay = this.toggleDisplay.bind(this);", " }", " toggleDisplay() {", " this.setState({", " display: !this.state.display", " });", " }", " render() {", " // change code below this line", " return (", "", " ", "", " );", " }", "};" ], "tail": [ "ReactDOM.render(Displayed!
", ", document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": " MyComponent
should exist and render.", "testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return mockedComponent.find('MyComponent').length; })(), 'MyComponent
should exist and render.');" }, { "text": "Whendisplay
is set totrue
, adiv
,button
, andh1
should render.", "testString": "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, 'Whendisplay
is set totrue
, adiv
,button
, andh1
should render.'); }; " }, { "text": "Whendisplay
is set tofalse
, only adiv
andbutton
should render.", "testString": "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, 'Whendisplay
is set tofalse
, only adiv
andbutton
should render.'); }; " }, { "text": "The render method should use the && logical operator to check the condition of this.state.display.", "testString": "getUserInput => assert(getUserInput('index').includes('&&'), 'The render method should use the && logical operator to check the condition of this.state.display.');" } ], "solutions": [ "class MyComponent extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n display: true\n }\n this.toggleDisplay = this.toggleDisplay.bind(this); \n }\n toggleDisplay() {\n this.setState({\n display: !this.state.display\n });\n }\n render() {\n // change code below this line\n return (\n\n \n {this.state.display &&\n );\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d4036187", "title": "Use a Ternary Expression for Conditional Rendering", "releasedOn": "December 25, 2017", "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 ternary operator. The ternary operator is often utilized as a shortcut forDisplayed!
}\nif/else
statements in JavaScript. They're not quite as robust as traditionalif/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 anif/else
statement was required, it was always outside thereturn
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:", "condition ? expressionIfTrue : expressionIfFalse", "
", "The code editor has three constants defined within theCheckUserAge
component'srender()
method. They are calledbuttonOne
,buttonTwo
, andbuttonThree
. Each of these is assigned a simple JSX expression representing a button element. First, initialize the state ofCheckUserAge
withinput
anduserAge
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'sreturn
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 than18
, renderbuttonThree
. If a user enters a number greater than or equal to18
, renderbuttonTwo
." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "", "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({", " userAge: this.state.input", " });", " }", " render() {", " const buttonOne = ;", " const buttonTwo = ;", " const buttonThree = ;", " return (", "", "", " );", " }", "};" ], "tail": [ "ReactDOM.render(Enter Your Age to Continue
", "
", " {", " /* change code here */", " }", ", document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": "The CheckUserAge
component should render with a singleinput
element and a singlebutton
element.", "testString": "assert(Enzyme.mount(React.createElement(CheckUserAge)).find('div').find('input').length === 1 && Enzyme.mount(React.createElement(CheckUserAge)).find('div').find('button').length === 1, 'TheCheckUserAge
component should render with a singleinput
element and a singlebutton
element.');" }, { "text": "TheCheckUserAge
component's state should be initialized with a property ofuserAge
and a property ofinput
, both set to a value of an empty string.", "testString": "assert(Enzyme.mount(React.createElement(CheckUserAge)).state().input === '' && Enzyme.mount(React.createElement(CheckUserAge)).state().userAge === '', 'TheCheckUserAge
component's state should be initialized with a property ofuserAge
and a property ofinput
, both set to a value of an empty string.');" }, { "text": "When theCheckUserAge
component is first rendered to the DOM, thebutton
's inner text should be Submit.", "testString": "assert(Enzyme.mount(React.createElement(CheckUserAge)).find('button').text() === 'Submit', 'When theCheckUserAge
component is first rendered to the DOM, thebutton
's inner text should be Submit.');" }, { "text": "When a number of less than 18 is entered into theinput
element and thebutton
is clicked, thebutton
's inner text should readYou Shall Not Pass
.", "testString": "async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); 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'); return waitForIt(() => { mockedComponent.update(); return mockedComponent.find('button').text(); }); }; const enter17AndClickButton = () => { mockedComponent.find('input').simulate('change', {target: { value: '17' }}); mockedComponent.find('button').simulate('click'); return waitForIt(() => { mockedComponent.update(); return mockedComponent.find('button').text(); }); }; const userAge3 = await enter3AndClickButton(); const userAge17 = await enter17AndClickButton(); assert(initialButton === 'Submit' && userAge3 === 'You Shall Not Pass' && userAge17 === 'You Shall Not Pass', 'When a number of less than 18 is entered into theinput
element and thebutton
is clicked, thebutton
's inner text should readYou Shall Not Pass
.'); }; " }, { "text": "When a number greater than or equal to 18 is entered into theinput
element and thebutton
is clicked, thebutton
's inner text should readYou May Enter
.", "testString": "async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); 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'); return waitForIt(() => { mockedComponent.update(); return mockedComponent.find('button').text(); }); }; const enter35AndClickButton = () => { mockedComponent.find('input').simulate('change', {target: { value: '35' }}); mockedComponent.find('button').simulate('click'); return waitForIt(() => { mockedComponent.update(); return mockedComponent.find('button').text(); }); }; const userAge18 = await enter18AndClickButton(); const userAge35 = await enter35AndClickButton(); assert(initialButton === 'Submit' && userAge18 === 'You May Enter' && userAge35 === 'You May Enter', 'When a number greater than or equal to 18 is entered into theinput
element and thebutton
is clicked, thebutton
's inner text should readYou May Enter
.'); }; " }, { "text": "Once a number has been submitted, and the value of theinput
is once again changed, thebutton
should return to readingSubmit
.", "testString": "async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(CheckUserAge)); const enter18AndClickButton = () => { mockedComponent.find('input').simulate('change', {target: { value: '18' }}); mockedComponent.find('button').simulate('click'); return waitForIt(() => { mockedComponent.update(); return mockedComponent.find('button').text(); }); }; const changeInputDontClickButton = () => { mockedComponent.find('input').simulate('change', {target: { value: '5' }}); return waitForIt(() => { mockedComponent.update(); return mockedComponent.find('button').text(); }); }; const enter10AndClickButton = () => { mockedComponent.find('input').simulate('change', {target: { value: '10' }}); mockedComponent.find('button').simulate('click'); return waitForIt(() => { mockedComponent.update(); return mockedComponent.find('button').text(); }); }; const userAge18 = await enter18AndClickButton(); const changeInput1 = await changeInputDontClickButton(); const userAge10 = await enter10AndClickButton(); const changeInput2 = await changeInputDontClickButton(); assert(userAge18 === 'You May Enter' && changeInput1 === 'Submit' && userAge10 === 'You Shall Not Pass' && changeInput2 === 'Submit', 'Once a number has been submitted, and the value of theinput
is once again changed, thebutton
should return to readingSubmit
.'); }; " }, { "text": "Your code should not contain anyif/else
statements.", "testString": "assert(new RegExp(/(\\s|;)if(\\s|\\()/).test(Enzyme.mount(React.createElement(CheckUserAge)).instance().render.toString()) === false, 'Your code should not contain anyif/else
statements.');" } ], "solutions": [ "const inputStyle = {\n width: 235,\n margin: 5\n}\n\nclass CheckUserAge extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n userAge: '',\n input: ''\n }\n this.submit = this.submit.bind(this);\n this.handleChange = this.handleChange.bind(this);\n }\n handleChange(e) {\n this.setState({\n input: e.target.value,\n userAge: ''\n });\n }\n submit() {\n this.setState({\n userAge: this.state.input\n });\n }\n render() {\n const buttonOne = ;\n const buttonTwo = ;\n const buttonThree = ;\n return (\n\n\n );\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d4036188", "title": "Render Conditionally from Props", "releasedOn": "December 25, 2017", "description": [ "So far, you've seen how to useEnter Your Age to Continue
\n
\n {\n this.state.userAge === '' ?\n buttonOne :\n this.state.userAge >= 18 ?\n buttonTwo :\n buttonThree\n }\nif/else
,&&,
null
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.", "
", "The code editor has two components that are partially defined for you: a parent calledGameOfChance
, and a child calledResults
. 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 useMath.random()
. This method returns a value between0
(inclusive) and1
(exclusive) each time it is called. So for 50/50 odds, useMath.random() > .5
in your expression. Statistically speaking, this expression will returntrue
50% of the time, andfalse
the other 50%. On line 30, replace the comment with this 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 theResults
component as a child ofGameOfChance
, and pass inexpression
as a prop calledfiftyFifty
. In theResults
component, write a ternary expression to render the text\"You win!\"
or\"You lose!\"
based on thefiftyFifty
prop that's being passed in fromGameOfChance
. Finally, make sure thehandleClick()
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." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "class Results extends React.Component {", " constructor(props) {", " super(props);", " }", " render() {", " return (", "", " {", " /* change code here */", " }", "
", " )", " };", "};", "", "class GameOfChance extends React.Component {", " constructor(props) {", " super(props);", " this.state = {", " counter: 1", " }", " this.handleClick = this.handleClick.bind(this);", " }", " handleClick() {", " this.setState({", " counter: 0 // change code here", " });", " }", " render() {", " let expression = null; // change code here", " return (", "", " ", " { /* change code below this line */ }", "", " { /* change code above this line */ }", "", " );", " }", "};" ], "tail": [ "ReactDOM.render({'Turn: ' + this.state.counter}
", ", document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": "The GameOfChance
component should exist and render to the page.", "testString": "assert.strictEqual(Enzyme.mount(React.createElement(GameOfChance)).find('GameOfChance').length, 1, 'TheGameOfChance
component should exist and render to the page.');" }, { "text": "GameOfChance
should return a singlebutton
element.", "testString": "assert.strictEqual(Enzyme.mount(React.createElement(GameOfChance)).find('button').length, 1, 'GameOfChance
should return a singlebutton
element.');" }, { "text": "GameOfChance
should return a single instance of theResults
component, which has a prop calledfiftyFifty
.", "testString": "assert(Enzyme.mount(React.createElement(GameOfChance)).find('Results').length === 1 && Enzyme.mount(React.createElement(GameOfChance)).find('Results').props().hasOwnProperty('fiftyFifty') === true, 'GameOfChance
should return a single instance of theResults
component, which has a prop calledfiftyFifty
.');" }, { "text": "GameOfChance
state should be initialized with a property ofcounter
set to a value of1
.", "testString": "assert.strictEqual(Enzyme.mount(React.createElement(GameOfChance)).state().counter, 1, 'GameOfChance
state should be initialized with a property ofcounter
set to a value of1
.');" }, { "text": "When theGameOfChance
component is first rendered to the DOM, ap
element should be returned with the inner text ofTurn: 1
.", "testString": "assert.strictEqual(Enzyme.mount(React.createElement(GameOfChance)).find('p').text(), 'Turn: 1', 'When theGameOfChance
component is first rendered to the DOM, ap
element should be returned with the inner text ofTurn: 1
.');" }, { "text": "Each time the button is clicked, the counter state should be incremented by a value of 1, and a singlep
element should be rendered to the DOM that contains the text \"Turn: N\", where N is the value of the counter state.", "testString": "async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); 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 waitForIt(() => result())}; const _2 = () => { simulate(); return waitForIt(() => result())}; const _3 = () => { simulate(); return waitForIt(() => result())}; const _4 = () => { simulate(); return waitForIt(() => result())}; const _5 = () => { simulate(); return waitForIt(() => result())}; const _1_val = await _1(); const _2_val = await _2(); const _3_val = await _3(); const _4_val = await _4(); const _5_val = await _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', 'Each time the button is clicked, the counter state should be incremented by a value of 1, and a singlep
element should be rendered to the DOM that contains the text \"Turn: N\", where N is the value of the counter state.'); }; " }, { "text": "When theGameOfChance
component is first mounted to the DOM and each time the button is clicked thereafter, a singleh1
element should be returned that randomly renders eitherYou Win!
orYou Lose!
.", "testString": "async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); 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 waitForIt(() => result())}; const _3 = () => { simulate(); return waitForIt(() => result())}; const _4 = () => { simulate(); return waitForIt(() => result())}; const _5 = () => { simulate(); return waitForIt(() => result())}; const _6 = () => { simulate(); return waitForIt(() => result())}; const _7 = () => { simulate(); return waitForIt(() => result())}; const _8 = () => { simulate(); return waitForIt(() => result())}; const _9 = () => { simulate(); return waitForIt(() => result())}; const _10 = () => { simulate(); return waitForIt(() => result())}; const _2_val = await _2(); const _3_val = await _3(); const _4_val = await _4(); const _5_val = await _5(); const _6_val = await _6(); const _7_val = await _7(); const _8_val = await _8(); const _9_val = await _9(); const _10_val = await _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, 'When theGameOfChance
component is first mounted to the DOM and each time the button is clicked thereafter, a singleh1
element should be returned that randomly renders eitherYou Win!
orYou Lose!
.'); }; " } ], "solutions": [ "class Results extends React.Component {\n constructor(props) {\n super(props);\n }\n render() {\n return (\n\n {\n this.props.fiftyFifty ?\n 'You Win!' :\n 'You Lose!'\n }\n
\n )\n };\n};\n\nclass GameOfChance extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n counter: 1\n }\n this.handleClick = this.handleClick.bind(this);\n }\n handleClick() {\n this.setState({\n counter: this.state.counter + 1\n });\n }\n render() {\n const expression = Math.random() > .5;\n return (\n\n \n\n );\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d4036189", "title": "Change Inline CSS Conditionally Based on Component State", "releasedOn": "December 25, 2017", "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.", "\n {'Turn: ' + this.state.counter}
\n
", "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 to3px solid red
. You can try it out by entering text in the input." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "", "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 (", "", "", " );", " }", "};" ], "tail": [ "ReactDOM.render(Don't Type Too Much:
", " ", ", document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": "The GateKeeper
component should render adiv
element.", "testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(GateKeeper)); return mockedComponent.find('div').length === 1; })(), 'TheGateKeeper
component should render adiv
element.');" }, { "text": "TheGateKeeper
component should be initialized with a state keyinput
set to an empty string.", "testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(GateKeeper)); return mockedComponent.state().input === ''; })(), 'TheGateKeeper
component should be initialized with a state keyinput
set to an empty string.');" }, { "text": "TheGateKeeper
component should render anh3
tag and aninput
tag.", "testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(GateKeeper)); return mockedComponent.find('h3').length === 1 && mockedComponent.find('input').length === 1; })(), 'TheGateKeeper
component should render anh3
tag and aninput
tag.');" }, { "text": "Theinput
tag should initially have a style of1px solid black
for theborder
property.", "testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(GateKeeper)); return mockedComponent.find('input').props().style.border === '1px solid black'; })(), 'Theinput
tag should initially have a style of1px solid black
for theborder
property.');" }, { "text": "Theinput
tag should be styled with a border of3px solid red
if the input value in state is longer than 15 characters.", "testString": "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', 'Theinput
tag should be styled with a border of3px solid red
if the input value in state is longer than 15 characters.'); }; " } ], "solutions": [ "class GateKeeper extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n input: ''\n };\n this.handleChange = this.handleChange.bind(this);\n }\n handleChange(event) {\n this.setState({ input: event.target.value })\n }\n render() {\n let inputStyle = {\n border: '1px solid black'\n };\n if (this.state.input.length > 15) {\n inputStyle.border = '3px solid red';\n };\n return (\n\n\n );\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d403618a", "title": "Use Array.map() to Dynamically Render Elements", "releasedOn": "December 25, 2017", "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. UsingDon't Type Too Much:
\n \nArray.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. ", "
", "The code editor has most of theMyToDoList
component set up. Some of this code should look familiar if you completed the controlled form challenge. You'll notice atextarea
and abutton
, along with a couple of methods that track their states, but nothing is rendered to the page yet.", "Inside theconstructor
, create athis.state
object and define two states:userInput
should be initialized as an empty string, andtoDoList
should be initialized as an empty array. Next, delete the comment in therender()
method next to theitems
variable. In its place, map over thetoDoList
array stored in the component's internal state and dynamically render ali
for each item. Try entering the stringeat, code, sleep, repeat
into thetextarea
, 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 uniquekey
attribute. Don't worry, this is the topic of the next challenge." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "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 code here", " return (", "", "", " );", " }", "};" ], "tail": [ "ReactDOM.render(
", " ", "My \"To Do\" List:
", "", " {items}", "
", ", document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": "The MyToDoList component should exist and render to the page.", "testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyToDoList)); return mockedComponent.find('MyToDoList').length === 1; })(), 'The MyToDoList component should exist and render to the page.');" }, { "text": "The first child of MyToDoList
should be atextarea
element.", "testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyToDoList)); return mockedComponent.find('MyToDoList').children().childAt(0).type() === 'textarea'; })(), 'The first child ofMyToDoList
should be atextarea
element.');" }, { "text": "The third child ofMyToDoList
should be abutton
element.", "testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyToDoList)); return mockedComponent.find('MyToDoList').children().childAt(2).type() === 'button'; })(), 'The third child ofMyToDoList
should be abutton
element.');" }, { "text": "The state ofMyToDoList
should be initialized withtoDoList
as an empty array.", "testString": "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 ofMyToDoList
should be initialized withtoDoList
as an empty array.');" }, { "text": "The state ofMyToDoList
should be initialized withuserInput
as an empty string.", "testString": "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyToDoList)); const initialState = mockedComponent.state(); return typeof initialState.userInput === 'string' && initialState.userInput.length === 0; })(), 'The state ofMyToDoList
should be initialized withuserInput
as an empty string.');" }, { "text": "When theCreate List
button is clicked, theMyToDoList
component should dynamically return an unordered list that contains a list item element for every item of a comma-separated list entered into thetextarea
element.", "testString": "async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); const mockedComponent = Enzyme.mount(React.createElement(MyToDoList)); const simulateChange = (el, value) => el.simulate('change', {target: {value}}); const state_1 = () => { return waitForIt(() => mockedComponent.find('ul').find('li'))}; const setInput = () => { return waitForIt(() => simulateChange(mockedComponent.find('textarea'), \"testA, testB, testC\"))}; const click = () => { return waitForIt(() => mockedComponent.find('button').simulate('click'))}; const state_2 = () => { return waitForIt(() => { const nodes = mockedComponent.find('ul').find('li'); return { nodes, text: nodes.reduce((t, n) => t + n.text(), '') }; })}; const setInput_2 = () => { return waitForIt(() => simulateChange(mockedComponent.find('textarea'), \"t1, t2, t3, t4, t5, t6\"))}; const click_1 = () => { return waitForIt(() => mockedComponent.find('button').simulate('click'))}; const state_3 = () => { return waitForIt(() => { const nodes = mockedComponent.find('ul').find('li'); return { nodes, text: nodes.reduce((t, n) => t + n.text(), '') }; })}; const awaited_state_1 = await state_1(); const awaited_setInput = await setInput(); const awaited_click = await click(); const awaited_state_2 = await state_2(); const awaited_setInput_2 = await setInput_2(); const awaited_click_1 = await click_1(); const awaited_state_3 = await state_3(); assert(awaited_state_1.length === 0 && awaited_state_2.nodes.length === 3 && awaited_state_3.nodes.length === 6 && awaited_state_2.text === 'testA testB testC' && awaited_state_3.text === 't1 t2 t3 t4 t5 t6', 'When theCreate List
button is clicked, theMyToDoList
component should dynamically return an unordered list that contains a list item element for every item of a comma-separated list entered into thetextarea
element.'); }; " } ], "solutions": [ "const textAreaStyles = {\n width: 235,\n margin: 5\n};\n\nclass MyToDoList extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n toDoList: [],\n userInput: ''\n }\n this.handleSubmit = this.handleSubmit.bind(this);\n this.handleChange = this.handleChange.bind(this);\n }\n handleSubmit() {\n const itemsArray = this.state.userInput.split(',');\n this.setState({\n toDoList: itemsArray\n });\n }\n handleChange(e) {\n this.setState({\n userInput: e.target.value\n });\n }\n render() {\n const items = this.state.toDoList.map( (item, i) => {\n return{item} \n });\n return (\n\n\n );\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d403618b", "title": "Give Sibling Elements a Unique Key Attribute", "releasedOn": "December 25, 2017", "description": [ "The last challenge showed how the
\n \nMy \"To Do\" List:
\n\n {items}\n
\nmap
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 akey
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 that keys only need to be unique between sibling elements, they don't need to be globally unique in your application.", "
", "The code editor has an array with some front end frameworks and a stateless functional component namedFrameworks()
.Frameworks()
needs to map the array to an unordered list, much like in the last challenge. Finish writing themap
callback to return anli
element for each framework in thefrontEndFrameworks
array. This time, make sure to give eachli
akey
attribute, set to a unique value.", "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." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "", "const frontEndFrameworks = [", " 'React',", " 'Angular',", " 'Ember',", " 'Knockout',", " 'Backbone',", " 'Vue'", "];", "", "function Frameworks() {", " const renderFrameworks = null; // change code here", " return (", "", "", " );", "};" ], "tail": [ "ReactDOM.render(Popular Front End JavaScript Frameworks
", "", " {renderFrameworks}", "
", ", document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": "The Frameworks
component should exist and render to the page.", "testString": "assert(Enzyme.mount(React.createElement(Frameworks)).find('Frameworks').length === 1, 'TheFrameworks
component should exist and render to the page.');" }, { "text": "Frameworks
should render anh1
element.", "testString": "assert(Enzyme.mount(React.createElement(Frameworks)).find('h1').length === 1, 'Frameworks
should render anh1
element.');" }, { "text": "Frameworks
should render aul
element.", "testString": "assert(Enzyme.mount(React.createElement(Frameworks)).find('ul').length === 1, 'Frameworks
should render aul
element.');" }, { "text": "Theul
tag should render 6 childli
elements.", "testString": "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, 'Theul
tag should render 6 childli
elements.');" }, { "text": "Each list item element should have a uniquekey
attribute.", "testString": "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 have a uniquekey
attribute.');" } ], "solutions": [ "const frontEndFrameworks = [\n 'React',\n 'Angular',\n 'Ember',\n 'Knockout',\n 'Backbone',\n 'Vue'\n];\n\nfunction Frameworks() {\n const renderFrameworks = frontEndFrameworks.map((fw, i) => {\n return{fw} \n })\n return (\n\n\n );\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d403618c", "title": "Use Array.filter() to Dynamically Filter an Array", "releasedOn": "December 25, 2017", "description": [ "ThePopular Front End JavaScript Frameworks
\n\n {renderFrameworks}\n
\nmap
array method is a powerful tool that you will use often when working with React. Another method related tomap
isfilter
, 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 propertyonline
which can be set totrue
orfalse
, you can filter only those users that are online by writing:", "let onlineUsers = users.filter(user => user.online);
", "
", "In the code editor,MyComponent
'sstate
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 usefilter
to return a new array containing only the users whoseonline
property istrue
. Then, in therenderOnline
variable, map over the filtered array, and return ali
element for each user that contains the text of theirusername
. Be sure to include a uniquekey
as well, like in the last challenges." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "contents": [ "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 code here", " const renderOnline = null; // change code here", " return (", "", "", " );", " }", "};" ], "tail": [ "ReactDOM.render(Current Online Users:
", "", " {renderOnline}", "
", ", document.getElementById('root'))" ], "head": [] } }, "tests": [ { "text": " MyComponent
should exist and render to the page.", "testString": "assert.strictEqual(Enzyme.mount(React.createElement(MyComponent)).find('MyComponent').length, 1, 'MyComponent
should exist and render to the page.');" }, { "text": "MyComponent
's state should be initialized to an array of six users.\")", "testString": "assert(Array.isArray(Enzyme.mount(React.createElement(MyComponent)).state('users')) === true && Enzyme.mount(React.createElement(MyComponent)).state('users').length === 6, \"MyComponent
's state should be initialized to an array of six users.\");" }, { "text": "MyComponent
should return adiv
, anh1
, and then an unordered list containingli
elements for every user whose online status is set totrue
.", "testString": "async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); 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 waitForIt(() => result()) }; const _3 = () => { comp.setState(users(false)); return waitForIt(() => result()) }; const _4 = () => { comp.setState({ users: [] }); return waitForIt(() => result()) }; const _2_val = await _2(); const _3_val = await _3(); const _4_val = await _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 return adiv
, anh1
, and then an unordered list containingli
elements for every user whose online status is set totrue
.'); }; " }, { "text": "MyComponent
should renderli
elements that contain the username of each online user.", "testString": "async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); 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 waitForIt(() => comp.find('ul').html()) }; const html = await ul(); assert(html === '', '
- Jeff
- Alan
- Mary
- Jim
- Laura
MyComponent
should renderli
elements that contain the username of each online user.'); }; " }, { "text": "Each list item element should have a uniquekey
attribute.", "testString": "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; })(), 'Each list item element should have a uniquekey
attribute.');" } ], "solutions": [ "class MyComponent extends React.Component {\n constructor(props) {\n super(props);\n this.state = {\n users: [\n {\n username: 'Jeff',\n online: true\n },\n {\n username: 'Alan',\n online: false\n },\n {\n username: 'Mary',\n online: true\n },\n {\n username: 'Jim',\n online: false\n },\n {\n username: 'Sara',\n online: true\n },\n {\n username: 'Laura',\n online: true\n }\n ]\n }\n }\n render() {\n const usersOnline = this.state.users.filter(user => {\n return user.online;\n });\n const renderOnlineUsers = usersOnline.map(user => {\n return (\n{user.username} \n );\n });\n return (\n\n\n );\n }\n};" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true }, { "id": "5a24c314108439a4d403618d", "title": "Render React on the Server with renderToString", "releasedOn": "December 25, 2017", "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 aCurrent Online Users:
\n\n {renderOnlineUsers}\n
\nrenderToString()
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.", "
", "TherenderToString()
method is provided onReactDOMServer
, which is available here as a global object. The method takes one argument which is a React element. Use this to renderApp
to a string." ], "files": { "indexjsx": { "key": "indexjsx", "ext": "jsx", "name": "index", "head": [ "var ReactDOMServer = { renderToString(x) { return null; } };" ], "contents": [ "", "class App extends React.Component {", " constructor(props) {", " super(props);", " }", " render() {", " return ", " }", "};", "", "// change code below this line", "" ], "tail": ["ReactDOM.render(, document.getElementById('root'))"] } }, "tests": [ { "text": "The App
component should render to a string usingReactDOMServer.renderToString
.", "testString": "getUserInput => assert(getUserInput('index').replace(/ /g,'').includes('ReactDOMServer.renderToString()') && Enzyme.mount(React.createElement(App)).children().name() === 'div', 'The App
component should render to a string usingReactDOMServer.renderToString
.');" } ], "solutions": [ "class App extends React.Component {\n constructor(props) {\n super(props);\n }\n render() {\n return \n }\n};\n\n// change code below this line\nReactDOMServer.renderToString();" ], "challengeType": 6, "isRequired": false, "translations": {}, "react": true } ] }