fix: convert async tests to synchronous (#39478)
* fix: convert async tests to sync * fix: correct formatting changed by prettier
This commit is contained in:
@ -8,11 +8,13 @@ forumTopicId: 301384
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<section id='description'>
|
||||
The last challenge showed that React can control the internal state for certain elements like <code>input</code> and <code>textarea</code>, which makes them controlled components. This applies to other form elements as well, including the regular HTML <code>form</code> element.
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
|
||||
<section id='instructions'>
|
||||
The <code>MyForm</code> component is set up with an empty <code>form</code> with a submit handler. The submit handler will be called when the form is submitted.
|
||||
We've added a button which submits the form. You can see it has the <code>type</code> set to <code>submit</code> indicating it is the button controlling the form. Add the <code>input</code> element in the <code>form</code> and set its <code>value</code> and <code>onChange()</code> attributes like the last challenge. You should then complete the <code>handleSubmit</code> method so that it sets the component state property <code>submit</code> to the current input value in the local <code>state</code>.
|
||||
@ -21,6 +23,7 @@ Finally, create an <code>h1</code> tag after the <code>form</code> which renders
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
@ -30,17 +33,77 @@ tests:
|
||||
- text: The state of <code>MyForm</code> should initialize with <code>input</code> and <code>submit</code> properties, both set to empty strings.
|
||||
testString: assert(Enzyme.mount(React.createElement(MyForm)).state('input') === '' && Enzyme.mount(React.createElement(MyForm)).state('submit') === '');
|
||||
- text: Typing in the <code>input</code> element should update the <code>input</code> 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''); }; '
|
||||
testString: "(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyForm));
|
||||
const _1 = () => {
|
||||
mockedComponent.setState({ input: '' });
|
||||
return mockedComponent.state('input');
|
||||
};
|
||||
const _2 = () => {
|
||||
mockedComponent
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: 'TestInput' } });
|
||||
return {
|
||||
state: mockedComponent.state('input'),
|
||||
inputVal: mockedComponent.find('input').props().value,
|
||||
};
|
||||
};
|
||||
const before = _1();
|
||||
const after = _2();
|
||||
assert(
|
||||
before === '' &&
|
||||
after.state === 'TestInput' &&
|
||||
after.inputVal === 'TestInput'
|
||||
);
|
||||
})();
|
||||
"
|
||||
- text: Submitting the form should run <code>handleSubmit</code> which should set the <code>submit</code> 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''); };'
|
||||
testString: "(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyForm));
|
||||
const _1 = () => {
|
||||
mockedComponent.setState({ input: '' });
|
||||
mockedComponent.setState({ submit: '' });
|
||||
mockedComponent
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: 'SubmitInput' } });
|
||||
return mockedComponent.state('submit');
|
||||
};
|
||||
const _2 = () => {
|
||||
mockedComponent.find('form').simulate('submit');
|
||||
return mockedComponent.state('submit');
|
||||
};
|
||||
const before = _1();
|
||||
const after = _2();
|
||||
assert(before === '' && after === 'SubmitInput');
|
||||
})();
|
||||
"
|
||||
- text: The <code>h1</code> header should render the value of the <code>submit</code> 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''); }; '
|
||||
testString: "(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyForm));
|
||||
const _1 = () => {
|
||||
mockedComponent.setState({ input: '' });
|
||||
mockedComponent.setState({ submit: '' });
|
||||
mockedComponent
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: 'TestInput' } });
|
||||
return mockedComponent.find('h1').text();
|
||||
};
|
||||
const _2 = () => {
|
||||
mockedComponent.find('form').simulate('submit');
|
||||
return mockedComponent.find('h1').text();
|
||||
};
|
||||
const before = _1();
|
||||
const after = _2();
|
||||
assert(before === '' && after === 'TestInput');
|
||||
})();
|
||||
"
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
|
||||
<section id='challengeSeed'>
|
||||
|
||||
<div id='jsx-seed'>
|
||||
@ -63,35 +126,35 @@ class MyForm extends React.Component {
|
||||
}
|
||||
handleSubmit(event) {
|
||||
// change code below this line
|
||||
|
||||
|
||||
// change code above this line
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
{ /* change code below this line */ }
|
||||
{/* change code below this line */}
|
||||
|
||||
{ /* change code above this line */ }
|
||||
{/* change code above this line */}
|
||||
<button type='submit'>Submit!</button>
|
||||
</form>
|
||||
{ /* change code below this line */ }
|
||||
{/* change code below this line */}
|
||||
|
||||
{ /* change code above this line */ }
|
||||
{/* change code above this line */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
### After Test
|
||||
|
||||
<div id='jsx-teardown'>
|
||||
|
||||
```js
|
||||
ReactDOM.render(<MyForm />, document.getElementById('root'))
|
||||
ReactDOM.render(<MyForm />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
</div>
|
||||
@ -99,8 +162,8 @@ ReactDOM.render(<MyForm />, document.getElementById('root'))
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
class MyForm extends React.Component {
|
||||
@ -119,8 +182,8 @@ class MyForm extends React.Component {
|
||||
});
|
||||
}
|
||||
handleSubmit(event) {
|
||||
event.preventDefault()
|
||||
this.setState((state) => ({
|
||||
event.preventDefault();
|
||||
this.setState(state => ({
|
||||
submit: state.input
|
||||
}));
|
||||
}
|
||||
@ -128,16 +191,14 @@ class MyForm extends React.Component {
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<input
|
||||
value={this.state.input}
|
||||
onChange={this.handleChange} />
|
||||
<input value={this.state.input} onChange={this.handleChange} />
|
||||
<button type='submit'>Submit!</button>
|
||||
</form>
|
||||
<h1>{this.state.submit}</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@ -8,17 +8,20 @@ forumTopicId: 301398
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<section id='description'>
|
||||
So far, if any component receives new <code>state</code> or new <code>props</code>, it re-renders itself and all its children. This is usually okay. But React provides a lifecycle method you can call when child components receive new <code>state</code> or <code>props</code>, and declare specifically if the components should update or not. The method is <code>shouldComponentUpdate()</code>, and it takes <code>nextProps</code> and <code>nextState</code> as parameters.
|
||||
This method is a useful way to optimize performance. For example, the default behavior is that your component re-renders when it receives new <code>props</code>, even if the <code>props</code> haven't changed. You can use <code>shouldComponentUpdate()</code> to prevent this by comparing the <code>props</code>. The method must return a <code>boolean</code> value that tells React whether or not to update the component. You can compare the current props (<code>this.props</code>) to the next props (<code>nextProps</code>) to determine if you need to update or not, and return <code>true</code> or <code>false</code> accordingly.
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
|
||||
<section id='instructions'>
|
||||
The <code>shouldComponentUpdate()</code> method is added in a component called <code>OnlyEvens</code>. Currently, this method returns <code>true</code> so <code>OnlyEvens</code> re-renders every time it receives new <code>props</code>. Modify the method so <code>OnlyEvens</code> updates only if the <code>value</code> of its new props is even. Click the <code>Add</code> button and watch the order of events in your browser's console as the lifecycle hooks are triggered.
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
@ -28,15 +31,49 @@ tests:
|
||||
- text: The <code>shouldComponentUpdate</code> method should be defined on the <code>OnlyEvens</code> component.
|
||||
testString: assert((() => { const child = React.createElement(OnlyEvens).type.prototype.shouldComponentUpdate.toString().replace(/s/g,''); return child !== 'undefined'; })());
|
||||
- text: The <code>OnlyEvens</code> component should return an <code>h1</code> tag which renders the value of <code>this.props.value</code>.
|
||||
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 === ''<h1>1000</h1>'' && secondValue === ''<h1>10</h1>''); }; '
|
||||
testString: "(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(Controller));
|
||||
const first = () => {
|
||||
mockedComponent.setState({ value: 1000 });
|
||||
return mockedComponent.find('h1').html();
|
||||
};
|
||||
const second = () => {
|
||||
mockedComponent.setState({ value: 10 });
|
||||
return mockedComponent.find('h1').html();
|
||||
};
|
||||
const firstValue = first();
|
||||
const secondValue = second();
|
||||
assert(firstValue === '<h1>1000</h1>' && secondValue === '<h1>10</h1>');
|
||||
})();
|
||||
"
|
||||
- text: <code>OnlyEvens</code> should re-render only when <code>nextProps.value</code> 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''); }; '
|
||||
testString: "(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(Controller));
|
||||
const first = () => {
|
||||
mockedComponent.setState({ value: 8 });
|
||||
return mockedComponent.find('h1').text();
|
||||
};
|
||||
const second = () => {
|
||||
mockedComponent.setState({ value: 7 });
|
||||
return mockedComponent.find('h1').text();
|
||||
};
|
||||
const third = () => {
|
||||
mockedComponent.setState({ value: 42 });
|
||||
return mockedComponent.find('h1').text();
|
||||
};
|
||||
const firstValue = first();
|
||||
const secondValue = second();
|
||||
const thirdValue = third();
|
||||
assert(firstValue === '8' && secondValue === '8' && thirdValue === '42');
|
||||
})();
|
||||
"
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
|
||||
<section id='challengeSeed'>
|
||||
|
||||
<div id='jsx-seed'>
|
||||
@ -48,17 +85,17 @@ class OnlyEvens extends React.Component {
|
||||
}
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
console.log('Should I update?');
|
||||
// change code below this line
|
||||
// change code below this line
|
||||
return true;
|
||||
// change code above this line
|
||||
// change code above this line
|
||||
}
|
||||
componentDidUpdate() {
|
||||
console.log('Component re-rendered.');
|
||||
}
|
||||
render() {
|
||||
return <h1>{this.props.value}</h1>
|
||||
return <h1>{this.props.value}</h1>;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class Controller extends React.Component {
|
||||
constructor(props) {
|
||||
@ -69,7 +106,7 @@ class Controller extends React.Component {
|
||||
this.addValue = this.addValue.bind(this);
|
||||
}
|
||||
addValue() {
|
||||
this.setState((state) => ({
|
||||
this.setState(state => ({
|
||||
value: state.value + 1
|
||||
}));
|
||||
}
|
||||
@ -77,21 +114,21 @@ class Controller extends React.Component {
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.addValue}>Add</button>
|
||||
<OnlyEvens value={this.state.value}/>
|
||||
<OnlyEvens value={this.state.value} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
### After Test
|
||||
|
||||
<div id='jsx-teardown'>
|
||||
|
||||
```js
|
||||
ReactDOM.render(<Controller />, document.getElementById('root'))
|
||||
ReactDOM.render(<Controller />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
</div>
|
||||
@ -99,8 +136,8 @@ ReactDOM.render(<Controller />, document.getElementById('root'))
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
class OnlyEvens extends React.Component {
|
||||
@ -117,9 +154,9 @@ class OnlyEvens extends React.Component {
|
||||
console.log('Component re-rendered.');
|
||||
}
|
||||
render() {
|
||||
return <h1>{this.props.value}</h1>
|
||||
return <h1>{this.props.value}</h1>;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class Controller extends React.Component {
|
||||
constructor(props) {
|
||||
@ -127,10 +164,10 @@ class Controller extends React.Component {
|
||||
this.state = {
|
||||
value: 0
|
||||
};
|
||||
this.addValue = this.addValue.bind(this);
|
||||
this.addValue = this.addValue.bind(this);
|
||||
}
|
||||
addValue() {
|
||||
this.setState((state) => ({
|
||||
this.setState(state => ({
|
||||
value: state.value + 1
|
||||
}));
|
||||
}
|
||||
@ -138,11 +175,11 @@ class Controller extends React.Component {
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.addValue}>Add</button>
|
||||
<OnlyEvens value={this.state.value}/>
|
||||
<OnlyEvens value={this.state.value} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@ -8,12 +8,14 @@ forumTopicId: 301405
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<section id='description'>
|
||||
So far, you've seen how to use <code>if/else</code>, <code>&&,</code> <code>null</code> and the ternary operator (<code>condition ? expressionIfTrue : expressionIfFalse</code>) 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.
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
|
||||
<section id='instructions'>
|
||||
The code editor has two components that are partially defined for you: a parent called <code>GameOfChance</code>, and a child called <code>Results</code>. They are used to create a simple game where the user presses a button to see if they win or lose.
|
||||
First, you'll need a simple expression that randomly returns a different value every time it is run. You can use <code>Math.random()</code>. This method returns a value between <code>0</code> (inclusive) and <code>1</code> (exclusive) each time it is called. So for 50/50 odds, use <code>Math.random() >= .5</code> in your expression. Statistically speaking, this expression will return <code>true</code> 50% of the time, and <code>false</code> the other 50%. On line 30, replace the comment with this expression to complete the variable declaration.
|
||||
@ -21,6 +23,7 @@ Now you have an expression that you can use to make a randomized decision in the
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
@ -36,15 +39,144 @@ tests:
|
||||
- text: 'When the <code>GameOfChance</code> component is first rendered to the DOM, a <code>p</code> element should be returned with the inner text of <code>Turn: 1</code>.'
|
||||
testString: 'assert.strictEqual(Enzyme.mount(React.createElement(GameOfChance)).find(''p'').text(), ''Turn: 1'');'
|
||||
- text: 'Each time the button is clicked, the counter state should be incremented by a value of 1, and a single <code>p</code> 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''); }; '
|
||||
testString: "(() => {
|
||||
const comp = Enzyme.mount(React.createElement(GameOfChance));
|
||||
const simulate = () => {
|
||||
comp.find('button').simulate('click');
|
||||
};
|
||||
const result = () => ({
|
||||
count: comp.state('counter'),
|
||||
text: comp.find('p').text(),
|
||||
});
|
||||
const _1 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _2 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _3 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _4 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _5 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _1_val = _1();
|
||||
const _2_val = _2();
|
||||
const _3_val = _3();
|
||||
const _4_val = _4();
|
||||
const _5_val = _5();
|
||||
assert(
|
||||
_1_val.count === 2 &&
|
||||
_1_val.text === 'Turn: 2' &&
|
||||
_2_val.count === 3 &&
|
||||
_2_val.text === 'Turn: 3' &&
|
||||
_3_val.count === 4 &&
|
||||
_3_val.text === 'Turn: 4' &&
|
||||
_4_val.count === 5 &&
|
||||
_4_val.text === 'Turn: 5' &&
|
||||
_5_val.count === 6 &&
|
||||
_5_val.text === 'Turn: 6'
|
||||
);
|
||||
})();
|
||||
"
|
||||
- text: When the <code>GameOfChance</code> component is first mounted to the DOM and each time the button is clicked thereafter, a single <code>h1</code> element should be returned that randomly renders either <code>You Win!</code> or <code>You Lose!</code>.
|
||||
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); }; '
|
||||
testString: "(() => {
|
||||
const comp = Enzyme.mount(React.createElement(GameOfChance));
|
||||
const simulate = () => {
|
||||
comp.find('button').simulate('click');
|
||||
};
|
||||
const result = () => ({
|
||||
h1: comp.find('h1').length,
|
||||
text: comp.find('h1').text(),
|
||||
});
|
||||
const _1 = result();
|
||||
const _2 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _3 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _4 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _5 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _6 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _7 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _8 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _9 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _10 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _2_val = _2();
|
||||
const _3_val = _3();
|
||||
const _4_val = _4();
|
||||
const _5_val = _5();
|
||||
const _6_val = _6();
|
||||
const _7_val = _7();
|
||||
const _8_val = _8();
|
||||
const _9_val = _9();
|
||||
const _10_val = _10();
|
||||
const __text = new Set([
|
||||
_1.text,
|
||||
_2_val.text,
|
||||
_3_val.text,
|
||||
_4_val.text,
|
||||
_5_val.text,
|
||||
_6_val.text,
|
||||
_7_val.text,
|
||||
_8_val.text,
|
||||
_9_val.text,
|
||||
_10_val.text,
|
||||
]);
|
||||
const __h1 = new Set([
|
||||
_1.h1,
|
||||
_2_val.h1,
|
||||
_3_val.h1,
|
||||
_4_val.h1,
|
||||
_5_val.h1,
|
||||
_6_val.h1,
|
||||
_7_val.h1,
|
||||
_8_val.h1,
|
||||
_9_val.h1,
|
||||
_10_val.h1,
|
||||
]);
|
||||
assert(__text.size === 2 && __h1.size === 1);
|
||||
})();
|
||||
"
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
|
||||
<section id='challengeSeed'>
|
||||
|
||||
<div id='jsx-seed'>
|
||||
@ -55,22 +187,16 @@ class Results extends React.Component {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<h1>
|
||||
{
|
||||
/* change code here */
|
||||
}
|
||||
</h1>
|
||||
)
|
||||
};
|
||||
};
|
||||
return <h1>{/* change code here */}</h1>;
|
||||
}
|
||||
}
|
||||
|
||||
class GameOfChance extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
counter: 1
|
||||
}
|
||||
};
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
}
|
||||
handleClick() {
|
||||
@ -83,24 +209,24 @@ class GameOfChance extends React.Component {
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.handleClick}>Play Again</button>
|
||||
{ /* change code below this line */ }
|
||||
{/* change code below this line */}
|
||||
|
||||
{ /* change code above this line */ }
|
||||
{/* change code above this line */}
|
||||
<p>{'Turn: ' + this.state.counter}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
### After Test
|
||||
|
||||
<div id='jsx-teardown'>
|
||||
|
||||
```js
|
||||
ReactDOM.render(<GameOfChance />, document.getElementById('root'))
|
||||
ReactDOM.render(<GameOfChance />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
</div>
|
||||
@ -108,8 +234,8 @@ ReactDOM.render(<GameOfChance />, document.getElementById('root'))
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
class Results extends React.Component {
|
||||
@ -117,24 +243,16 @@ class Results extends React.Component {
|
||||
super(props);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<h1>
|
||||
{
|
||||
this.props.fiftyFifty ?
|
||||
'You Win!' :
|
||||
'You Lose!'
|
||||
}
|
||||
</h1>
|
||||
)
|
||||
};
|
||||
};
|
||||
return <h1>{this.props.fiftyFifty ? 'You Win!' : 'You Lose!'}</h1>;
|
||||
}
|
||||
}
|
||||
|
||||
class GameOfChance extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
counter: 1
|
||||
}
|
||||
};
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
}
|
||||
handleClick() {
|
||||
@ -143,7 +261,7 @@ class GameOfChance extends React.Component {
|
||||
});
|
||||
}
|
||||
render() {
|
||||
const expression = Math.random() >= .5;
|
||||
const expression = Math.random() >= 0.5;
|
||||
return (
|
||||
<div>
|
||||
<button onClick={this.handleClick}>Play Again</button>
|
||||
@ -152,7 +270,7 @@ class GameOfChance extends React.Component {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@ -8,22 +8,25 @@ forumTopicId: 301414
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<section id='description'>
|
||||
Before moving on to dynamic rendering techniques, there's one last way to use built-in JavaScript conditionals to render what you want: the <dfn>ternary operator</dfn>. The ternary operator is often utilized as a shortcut for <code>if/else</code> statements in JavaScript. They're not quite as robust as traditional <code>if/else</code> statements, but they are very popular among React developers. One reason for this is because of how JSX is compiled, <code>if/else</code> statements can't be inserted directly into JSX code. You might have noticed this a couple challenges ago — when an <code>if/else</code> statement was required, it was always <em>outside</em> the <code>return</code> 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:
|
||||
|
||||
```js
|
||||
condition ? expressionIfTrue : expressionIfFalse
|
||||
condition ? expressionIfTrue : expressionIfFalse;
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
|
||||
<section id='instructions'>
|
||||
The code editor has three constants defined within the <code>CheckUserAge</code> component's <code>render()</code> method. They are called <code>buttonOne</code>, <code>buttonTwo</code>, and <code>buttonThree</code>. Each of these is assigned a simple JSX expression representing a button element. First, initialize the state of <code>CheckUserAge</code> with <code>input</code> and <code>userAge</code> both set to values of an empty string.
|
||||
Once the component is rendering information to the page, users should have a way to interact with it. Within the component's <code>return</code> statement, set up a ternary expression that implements the following logic: when the page first loads, render the submit button, <code>buttonOne</code>, to the page. Then, when a user enters their age and clicks the button, render a different button based on the age. If a user enters a number less than <code>18</code>, render <code>buttonThree</code>. If a user enters a number greater than or equal to <code>18</code>, render <code>buttonTwo</code>.
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
@ -35,11 +38,101 @@ tests:
|
||||
- text: When the <code>CheckUserAge</code> component is first rendered to the DOM, the <code>button</code>'s inner text should be Submit.
|
||||
testString: assert(Enzyme.mount(React.createElement(CheckUserAge)).find('button').text() === 'Submit');
|
||||
- text: When a number of less than 18 is entered into the <code>input</code> element and the <code>button</code> is clicked, the <code>button</code>'s inner text should read <code>You Shall Not Pass</code>.
|
||||
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''); }; '
|
||||
testString: "(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(CheckUserAge));
|
||||
const initialButton = mockedComponent.find('button').text();
|
||||
const enter3AndClickButton = () => {
|
||||
mockedComponent
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: '3' } });
|
||||
mockedComponent.find('button').simulate('click');
|
||||
mockedComponent.update();
|
||||
return mockedComponent.find('button').text();
|
||||
};
|
||||
const enter17AndClickButton = () => {
|
||||
mockedComponent
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: '17' } });
|
||||
mockedComponent.find('button').simulate('click');
|
||||
mockedComponent.update();
|
||||
return mockedComponent.find('button').text();
|
||||
};
|
||||
const userAge3 = enter3AndClickButton();
|
||||
const userAge17 = enter17AndClickButton();
|
||||
assert(
|
||||
initialButton === 'Submit' &&
|
||||
userAge3 === 'You Shall Not Pass' &&
|
||||
userAge17 === 'You Shall Not Pass'
|
||||
);
|
||||
})();
|
||||
"
|
||||
- text: When a number greater than or equal to 18 is entered into the <code>input</code> element and the <code>button</code> is clicked, the <code>button</code>'s inner text should read <code>You May Enter</code>.
|
||||
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''); }; '
|
||||
testString: "(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(CheckUserAge));
|
||||
const initialButton = mockedComponent.find('button').text();
|
||||
const enter18AndClickButton = () => {
|
||||
mockedComponent
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: '18' } });
|
||||
mockedComponent.find('button').simulate('click');
|
||||
mockedComponent.update();
|
||||
return mockedComponent.find('button').text();
|
||||
};
|
||||
const enter35AndClickButton = () => {
|
||||
mockedComponent
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: '35' } });
|
||||
mockedComponent.find('button').simulate('click');
|
||||
mockedComponent.update();
|
||||
return mockedComponent.find('button').text();
|
||||
};
|
||||
const userAge18 = enter18AndClickButton();
|
||||
const userAge35 = enter35AndClickButton();
|
||||
assert(
|
||||
initialButton === 'Submit' &&
|
||||
userAge18 === 'You May Enter' &&
|
||||
userAge35 === 'You May Enter'
|
||||
);
|
||||
})();
|
||||
"
|
||||
- text: Once a number has been submitted, and the value of the <code>input</code> is once again changed, the <code>button</code> should return to reading <code>Submit</code>.
|
||||
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''); }; '
|
||||
testString: "(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(CheckUserAge));
|
||||
const enter18AndClickButton = () => {
|
||||
mockedComponent
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: '18' } });
|
||||
mockedComponent.find('button').simulate('click');
|
||||
mockedComponent.update();
|
||||
return mockedComponent.find('button').text();
|
||||
};
|
||||
const changeInputDontClickButton = () => {
|
||||
mockedComponent
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: '5' } });
|
||||
mockedComponent.update();
|
||||
return mockedComponent.find('button').text();
|
||||
};
|
||||
const enter10AndClickButton = () => {
|
||||
mockedComponent
|
||||
.find('input')
|
||||
.simulate('change', { target: { value: '10' } });
|
||||
mockedComponent.find('button').simulate('click');
|
||||
mockedComponent.update();
|
||||
return mockedComponent.find('button').text();
|
||||
};
|
||||
const userAge18 = enter18AndClickButton();
|
||||
const changeInput1 = changeInputDontClickButton();
|
||||
const userAge10 = enter10AndClickButton();
|
||||
const changeInput2 = changeInputDontClickButton();
|
||||
assert(
|
||||
userAge18 === 'You May Enter' &&
|
||||
changeInput1 === 'Submit' &&
|
||||
userAge10 === 'You Shall Not Pass' &&
|
||||
changeInput2 === 'Submit'
|
||||
);
|
||||
})();
|
||||
"
|
||||
- text: Your code should not contain any <code>if/else</code> statements.
|
||||
testString: assert(new RegExp(/(\s|;)if(\s|\()/).test(Enzyme.mount(React.createElement(CheckUserAge)).instance().render.toString()) === false);
|
||||
|
||||
@ -48,16 +141,16 @@ tests:
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
|
||||
<section id='challengeSeed'>
|
||||
|
||||
<div id='jsx-seed'>
|
||||
|
||||
```jsx
|
||||
|
||||
const inputStyle = {
|
||||
width: 235,
|
||||
margin: 5
|
||||
}
|
||||
};
|
||||
|
||||
class CheckUserAge extends React.Component {
|
||||
constructor(props) {
|
||||
@ -88,26 +181,26 @@ class CheckUserAge extends React.Component {
|
||||
<h3>Enter Your Age to Continue</h3>
|
||||
<input
|
||||
style={inputStyle}
|
||||
type="number"
|
||||
type='number'
|
||||
value={this.state.input}
|
||||
onChange={this.handleChange} /><br />
|
||||
{
|
||||
/* change code here */
|
||||
}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<br />
|
||||
{/* change code here */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
### After Test
|
||||
|
||||
<div id='jsx-teardown'>
|
||||
|
||||
```js
|
||||
ReactDOM.render(<CheckUserAge />, document.getElementById('root'))
|
||||
ReactDOM.render(<CheckUserAge />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
</div>
|
||||
@ -115,14 +208,14 @@ ReactDOM.render(<CheckUserAge />, document.getElementById('root'))
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
const inputStyle = {
|
||||
width: 235,
|
||||
margin: 5
|
||||
}
|
||||
};
|
||||
|
||||
class CheckUserAge extends React.Component {
|
||||
constructor(props) {
|
||||
@ -130,7 +223,7 @@ class CheckUserAge extends React.Component {
|
||||
this.state = {
|
||||
userAge: '',
|
||||
input: ''
|
||||
}
|
||||
};
|
||||
this.submit = this.submit.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
}
|
||||
@ -154,20 +247,20 @@ class CheckUserAge extends React.Component {
|
||||
<h3>Enter Your Age to Continue</h3>
|
||||
<input
|
||||
style={inputStyle}
|
||||
type="number"
|
||||
type='number'
|
||||
value={this.state.input}
|
||||
onChange={this.handleChange} /><br />
|
||||
{
|
||||
this.state.userAge === '' ?
|
||||
buttonOne :
|
||||
this.state.userAge >= 18 ?
|
||||
buttonTwo :
|
||||
buttonThree
|
||||
}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<br />
|
||||
{this.state.userAge === ''
|
||||
? buttonOne
|
||||
: this.state.userAge >= 18
|
||||
? buttonTwo
|
||||
: buttonThree}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@ -8,17 +8,20 @@ forumTopicId: 301415
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<section id='description'>
|
||||
In previous challenges, you learned how to inject JavaScript code into JSX code using curly braces, <code>{ }</code>, for tasks like accessing props, passing props, accessing state, inserting comments into your code, and most recently, styling your components. These are all common use cases to put JavaScript in JSX, but they aren't the only way that you can utilize JavaScript code in your React components.
|
||||
You can also write JavaScript directly in your <code>render</code> methods, before the <code>return</code> statement, <strong><em>without</em></strong> 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 <em>inside</em> the <code>return</code> statement, you place the variable name inside curly braces.
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
|
||||
<section id='instructions'>
|
||||
In the code provided, the <code>render</code> method has an array that contains 20 phrases to represent the answers found in the classic 1980's Magic Eight Ball toy. The button click event is bound to the <code>ask</code> method, so each time the button is clicked a random number will be generated and stored as the <code>randomIndex</code> in state. On line 52, delete the string <code>"change me!"</code> and reassign the <code>answer</code> const so your code randomly accesses a different index of the <code>possibleAnswers</code> array each time the component updates. Finally, insert the <code>answer</code> const inside the <code>p</code> tags.
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
@ -34,13 +37,89 @@ tests:
|
||||
- text: When <code>MagicEightBall</code> is first mounted to the DOM, it should return an empty <code>p</code> element.
|
||||
testString: assert(Enzyme.mount(React.createElement(MagicEightBall)).find('p').length === 1 && Enzyme.mount(React.createElement(MagicEightBall)).find('p').text() === '');
|
||||
- text: When text is entered into the <code>input</code> element and the button is clicked, the <code>MagicEightBall</code> component should return a <code>p</code> element that contains a random element from the <code>possibleAnswers</code> 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);}'
|
||||
testString: "(() => {
|
||||
const comp = Enzyme.mount(React.createElement(MagicEightBall));
|
||||
const simulate = () => {
|
||||
comp.find('input').simulate('change', { target: { value: 'test?' } });
|
||||
comp.find('button').simulate('click');
|
||||
};
|
||||
const result = () => comp.find('p').text();
|
||||
const _1 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _2 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _3 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _4 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _5 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _6 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _7 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _8 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _9 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _10 = () => {
|
||||
simulate();
|
||||
return result();
|
||||
};
|
||||
const _1_val = _1();
|
||||
const _2_val = _2();
|
||||
const _3_val = _3();
|
||||
const _4_val = _4();
|
||||
const _5_val = _5();
|
||||
const _6_val = _6();
|
||||
const _7_val = _7();
|
||||
const _8_val = _8();
|
||||
const _9_val = _9();
|
||||
const _10_val = _10();
|
||||
const actualAnswers = [
|
||||
_1_val,
|
||||
_2_val,
|
||||
_3_val,
|
||||
_4_val,
|
||||
_5_val,
|
||||
_6_val,
|
||||
_7_val,
|
||||
_8_val,
|
||||
_9_val,
|
||||
_10_val,
|
||||
];
|
||||
const hasIndex = actualAnswers.filter(
|
||||
(answer, i) => possibleAnswers.indexOf(answer) !== -1
|
||||
);
|
||||
const notAllEqual = new Set(actualAnswers);
|
||||
assert(notAllEqual.size > 1 && hasIndex.length === 10);
|
||||
})();
|
||||
"
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
|
||||
<section id='challengeSeed'>
|
||||
|
||||
<div id='jsx-seed'>
|
||||
@ -49,7 +128,7 @@ tests:
|
||||
const inputStyle = {
|
||||
width: 235,
|
||||
margin: 5
|
||||
}
|
||||
};
|
||||
|
||||
class MagicEightBall extends React.Component {
|
||||
constructor(props) {
|
||||
@ -57,7 +136,7 @@ class MagicEightBall extends React.Component {
|
||||
this.state = {
|
||||
userInput: '',
|
||||
randomIndex: ''
|
||||
}
|
||||
};
|
||||
this.ask = this.ask.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
}
|
||||
@ -90,45 +169,67 @@ class MagicEightBall extends React.Component {
|
||||
'Better not tell you now',
|
||||
'Cannot predict now',
|
||||
'Concentrate and ask again',
|
||||
'Don\'t count on it',
|
||||
"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
|
||||
const answer = 'change me!'; // << change code here
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
type='text'
|
||||
value={this.state.userInput}
|
||||
onChange={this.handleChange}
|
||||
style={inputStyle} /><br />
|
||||
<button onClick={this.ask}>
|
||||
Ask the Magic Eight Ball!
|
||||
</button><br />
|
||||
style={inputStyle}
|
||||
/>
|
||||
<br />
|
||||
<button onClick={this.ask}>Ask the Magic Eight Ball!</button>
|
||||
<br />
|
||||
<h3>Answer:</h3>
|
||||
<p>
|
||||
{ /* change code below this line */ }
|
||||
{/* change code below this line */}
|
||||
|
||||
{ /* change code above this line */ }
|
||||
{/* change code above this line */}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
### After Test
|
||||
|
||||
<div id='jsx-teardown'>
|
||||
|
||||
```js
|
||||
var possibleAnswers = [ 'It is certain', 'It is decidedly so', 'Without a doubt', 'Yes, definitely', 'You may rely on it', 'As I see it, yes', 'Outlook good', 'Yes', 'Signs point to yes', 'Reply hazy try again', 'Ask again later', 'Better not tell you now', 'Cannot predict now', 'Concentrate and ask again', 'Don\'t count on it', 'My reply is no', 'My sources say no', 'Outlook not so good','Very doubtful', 'Most likely' ];
|
||||
ReactDOM.render(<MagicEightBall />, document.getElementById('root'))
|
||||
var possibleAnswers = [
|
||||
'It is certain',
|
||||
'It is decidedly so',
|
||||
'Without a doubt',
|
||||
'Yes, definitely',
|
||||
'You may rely on it',
|
||||
'As I see it, yes',
|
||||
'Outlook good',
|
||||
'Yes',
|
||||
'Signs point to yes',
|
||||
'Reply hazy try again',
|
||||
'Ask again later',
|
||||
'Better not tell you now',
|
||||
'Cannot predict now',
|
||||
'Concentrate and ask again',
|
||||
"Don't count on it",
|
||||
'My reply is no',
|
||||
'My sources say no',
|
||||
'Outlook not so good',
|
||||
'Very doubtful',
|
||||
'Most likely'
|
||||
];
|
||||
ReactDOM.render(<MagicEightBall />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
</div>
|
||||
@ -136,14 +237,14 @@ ReactDOM.render(<MagicEightBall />, document.getElementById('root'))
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
const inputStyle = {
|
||||
width: 235,
|
||||
margin: 5
|
||||
}
|
||||
};
|
||||
|
||||
class MagicEightBall extends React.Component {
|
||||
constructor(props) {
|
||||
@ -151,7 +252,7 @@ class MagicEightBall extends React.Component {
|
||||
this.state = {
|
||||
userInput: '',
|
||||
randomIndex: ''
|
||||
}
|
||||
};
|
||||
this.ask = this.ask.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
}
|
||||
@ -170,30 +271,45 @@ class MagicEightBall extends React.Component {
|
||||
}
|
||||
render() {
|
||||
const possibleAnswers = [
|
||||
"It is certain", "It is decidedly so", "Without a doubt",
|
||||
"Yes, definitely", "You may rely on it", "As I see it, yes",
|
||||
"Outlook good", "Yes", "Signs point to yes", "Reply hazy try again",
|
||||
"Ask again later", "Better not tell you now", "Cannot predict now",
|
||||
"Concentrate and ask again", "Don't count on it", "My reply is no",
|
||||
"My sources say no", "Outlook not so good","Very doubtful", "Most likely"
|
||||
'It is certain',
|
||||
'It is decidedly so',
|
||||
'Without a doubt',
|
||||
'Yes, definitely',
|
||||
'You may rely on it',
|
||||
'As I see it, yes',
|
||||
'Outlook good',
|
||||
'Yes',
|
||||
'Signs point to yes',
|
||||
'Reply hazy try again',
|
||||
'Ask again later',
|
||||
'Better not tell you now',
|
||||
'Cannot predict now',
|
||||
'Concentrate and ask again',
|
||||
"Don't count on it",
|
||||
'My reply is no',
|
||||
'My sources say no',
|
||||
'Outlook not so good',
|
||||
'Very doubtful',
|
||||
'Most likely'
|
||||
];
|
||||
const answer = possibleAnswers[this.state.randomIndex];
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
type='text'
|
||||
value={this.state.userInput}
|
||||
onChange={this.handleChange}
|
||||
style={inputStyle} /><br />
|
||||
<button onClick={this.ask}>Ask the Magic Eight Ball!</button><br />
|
||||
style={inputStyle}
|
||||
/>
|
||||
<br />
|
||||
<button onClick={this.ask}>Ask the Magic Eight Ball!</button>
|
||||
<br />
|
||||
<h3>Answer:</h3>
|
||||
<p>
|
||||
{answer}
|
||||
</p>
|
||||
<p>{answer}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@ -8,17 +8,20 @@ forumTopicId: 301416
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<section id='description'>
|
||||
The <code>map</code> array method is a powerful tool that you will use often when working with React. Another method related to <code>map</code> is <code>filter</code>, which filters the contents of an array based on a condition, then returns a new array. For example, if you have an array of users that all have a property <code>online</code> which can be set to <code>true</code> or <code>false</code>, you can filter only those users that are online by writing:
|
||||
<code>let onlineUsers = users.filter(user => user.online);</code>
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
|
||||
<section id='instructions'>
|
||||
In the code editor, <code>MyComponent</code>'s <code>state</code> is initialized with an array of users. Some users are online and some aren't. Filter the array so you see only the users who are online. To do this, first use <code>filter</code> to return a new array containing only the users whose <code>online</code> property is <code>true</code>. Then, in the <code>renderOnline</code> variable, map over the filtered array, and return a <code>li</code> element for each user that contains the text of their <code>username</code>. Be sure to include a unique <code>key</code> as well, like in the last challenges.
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
@ -28,9 +31,68 @@ tests:
|
||||
- text: <code>MyComponent</code>'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);
|
||||
- text: <code>MyComponent</code> should return a <code>div</code>, an <code>h1</code>, and then an unordered list containing <code>li</code> elements for every user whose online status is set to <code>true</code>.
|
||||
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); }; '
|
||||
testString: "(() => {
|
||||
const comp = Enzyme.mount(React.createElement(MyComponent));
|
||||
const users = (bool) => ({
|
||||
users: [
|
||||
{ username: 'Jeff', online: bool },
|
||||
{ username: 'Alan', online: bool },
|
||||
{ username: 'Mary', online: bool },
|
||||
{ username: 'Jim', online: bool },
|
||||
{ username: 'Laura', online: bool },
|
||||
],
|
||||
});
|
||||
const result = () => comp.find('li').length;
|
||||
const _1 = result();
|
||||
const _2 = () => {
|
||||
comp.setState(users(true));
|
||||
return result();
|
||||
};
|
||||
const _3 = () => {
|
||||
comp.setState(users(false));
|
||||
return result();
|
||||
};
|
||||
const _4 = () => {
|
||||
comp.setState({ users: [] });
|
||||
return result();
|
||||
};
|
||||
const _2_val = _2();
|
||||
const _3_val = _3();
|
||||
const _4_val = _4();
|
||||
assert(
|
||||
comp.find('div').length === 1 &&
|
||||
comp.find('h1').length === 1 &&
|
||||
comp.find('ul').length === 1 &&
|
||||
_1 === 4 &&
|
||||
_2_val === 5 &&
|
||||
_3_val === 0 &&
|
||||
_4_val === 0
|
||||
);
|
||||
})();
|
||||
"
|
||||
- text: <code>MyComponent</code> should render <code>li</code> 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 === ''<ul><li>Jeff</li><li>Alan</li><li>Mary</li><li>Jim</li><li>Laura</li></ul>''); }; '
|
||||
testString: "(() => {
|
||||
const comp = Enzyme.mount(React.createElement(MyComponent));
|
||||
const users = (bool) => ({
|
||||
users: [
|
||||
{ username: 'Jeff', online: bool },
|
||||
{ username: 'Alan', online: bool },
|
||||
{ username: 'Mary', online: bool },
|
||||
{ username: 'Jim', online: bool },
|
||||
{ username: 'Laura', online: bool },
|
||||
],
|
||||
});
|
||||
const ul = () => {
|
||||
comp.setState(users(true));
|
||||
return comp.find('ul').html();
|
||||
};
|
||||
const html = ul();
|
||||
assert(
|
||||
html ===
|
||||
'<ul><li>Jeff</li><li>Alan</li><li>Mary</li><li>Jim</li><li>Laura</li></ul>'
|
||||
);
|
||||
})();
|
||||
"
|
||||
- text: Each list item element should have a unique <code>key</code> 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; })());
|
||||
|
||||
@ -39,6 +101,7 @@ tests:
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
|
||||
<section id='challengeSeed'>
|
||||
|
||||
<div id='jsx-seed'>
|
||||
@ -74,31 +137,29 @@ class MyComponent extends React.Component {
|
||||
online: true
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
render() {
|
||||
const usersOnline = null; // change code here
|
||||
const renderOnline = null; // change code here
|
||||
return (
|
||||
<div>
|
||||
<h1>Current Online Users:</h1>
|
||||
<ul>
|
||||
{renderOnline}
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h1>Current Online Users:</h1>
|
||||
<ul>{renderOnline}</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
### After Test
|
||||
|
||||
<div id='jsx-teardown'>
|
||||
|
||||
```js
|
||||
ReactDOM.render(<MyComponent />, document.getElementById('root'))
|
||||
ReactDOM.render(<MyComponent />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
</div>
|
||||
@ -106,8 +167,8 @@ ReactDOM.render(<MyComponent />, document.getElementById('root'))
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
class MyComponent extends React.Component {
|
||||
@ -140,27 +201,23 @@ class MyComponent extends React.Component {
|
||||
online: true
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
render() {
|
||||
const usersOnline = this.state.users.filter(user => {
|
||||
return user.online;
|
||||
});
|
||||
const renderOnline = usersOnline.map(user => {
|
||||
return (
|
||||
<li key={user.username}>{user.username}</li>
|
||||
);
|
||||
return <li key={user.username}>{user.username}</li>;
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
<h1>Current Online Users:</h1>
|
||||
<ul>
|
||||
{renderOnline}
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h1>Current Online Users:</h1>
|
||||
<ul>{renderOnline}</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@ -8,12 +8,14 @@ forumTopicId: 301417
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<section id='description'>
|
||||
Conditional rendering is useful, but you may need your components to render an unknown number of elements. Often in reactive programming, a programmer has no way to know what the state of an application is until runtime, because so much depends on a user's interaction with that program. Programmers need to write their code to correctly handle that unknown state ahead of time. Using <code>Array.map()</code> 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.
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
|
||||
<section id='instructions'>
|
||||
The code editor has most of the <code>MyToDoList</code> component set up. Some of this code should look familiar if you completed the controlled form challenge. You'll notice a <code>textarea</code> and a <code>button</code>, along with a couple of methods that track their states, but nothing is rendered to the page yet.
|
||||
Inside the <code>constructor</code>, create a <code>this.state</code> object and define two states: <code>userInput</code> should be initialized as an empty string, and <code>toDoList</code> should be initialized as an empty array. Next, delete the comment in the <code>render()</code> method next to the <code>items</code> variable. In its place, map over the <code>toDoList</code> array stored in the component's internal state and dynamically render a <code>li</code> for each item. Try entering the string <code>eat, code, sleep, repeat</code> into the <code>textarea</code>, then click the button and see what happens.
|
||||
@ -21,6 +23,7 @@ Inside the <code>constructor</code>, create a <code>this.state</code> object and
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
@ -38,13 +41,62 @@ tests:
|
||||
- text: The state of <code>MyToDoList</code> should be initialized with <code>userInput</code> 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; })());
|
||||
- text: When the <code>Create List</code> button is clicked, the <code>MyToDoList</code> component should dynamically return an unordered list that contains a list item element for every item of a comma-separated list entered into the <code>textarea</code> 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''); }; '
|
||||
testString: "(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyToDoList));
|
||||
const simulateChange = (el, value) =>
|
||||
el.simulate('change', { target: { value } });
|
||||
const state_1 = () => {
|
||||
return mockedComponent.find('ul').find('li');
|
||||
};
|
||||
const setInput = () => {
|
||||
return simulateChange(
|
||||
mockedComponent.find('textarea'),
|
||||
'testA, testB, testC'
|
||||
);
|
||||
};
|
||||
const click = () => {
|
||||
return mockedComponent.find('button').simulate('click');
|
||||
};
|
||||
const state_2 = () => {
|
||||
const nodes = mockedComponent.find('ul').find('li');
|
||||
return { nodes, text: nodes.reduce((t, n) => t + n.text(), '') };
|
||||
};
|
||||
const setInput_2 = () => {
|
||||
return simulateChange(
|
||||
mockedComponent.find('textarea'),
|
||||
't1, t2, t3, t4, t5, t6'
|
||||
);
|
||||
};
|
||||
const click_1 = () => {
|
||||
return mockedComponent.find('button').simulate('click');
|
||||
};
|
||||
const state_3 = () => {
|
||||
const nodes = mockedComponent.find('ul').find('li');
|
||||
return { nodes, text: nodes.reduce((t, n) => t + n.text(), '') };
|
||||
};
|
||||
const awaited_state_1 = state_1();
|
||||
const awaited_setInput = setInput();
|
||||
const awaited_click = click();
|
||||
const awaited_state_2 = state_2();
|
||||
const awaited_setInput_2 = setInput_2();
|
||||
const awaited_click_1 = click_1();
|
||||
const awaited_state_3 = state_3();
|
||||
assert(
|
||||
awaited_state_1.length === 0 &&
|
||||
awaited_state_2.nodes.length === 3 &&
|
||||
awaited_state_3.nodes.length === 6 &&
|
||||
awaited_state_2.text === 'testA testB testC' &&
|
||||
awaited_state_3.text === 't1 t2 t3 t4 t5 t6'
|
||||
);
|
||||
})();
|
||||
"
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
|
||||
<section id='challengeSeed'>
|
||||
|
||||
<div id='jsx-seed'>
|
||||
@ -83,26 +135,26 @@ class MyToDoList extends React.Component {
|
||||
onChange={this.handleChange}
|
||||
value={this.state.userInput}
|
||||
style={textAreaStyles}
|
||||
placeholder="Separate Items With Commas" />
|
||||
placeholder='Separate Items With Commas'
|
||||
/>
|
||||
<br />
|
||||
<button onClick={this.handleSubmit}>Create List</button>
|
||||
<h1>My "To Do" List:</h1>
|
||||
<ul>
|
||||
{items}
|
||||
</ul>
|
||||
<ul>{items}</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
### After Test
|
||||
|
||||
<div id='jsx-teardown'>
|
||||
|
||||
```js
|
||||
ReactDOM.render(<MyToDoList />, document.getElementById('root'))
|
||||
ReactDOM.render(<MyToDoList />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
</div>
|
||||
@ -110,8 +162,8 @@ ReactDOM.render(<MyToDoList />, document.getElementById('root'))
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
const textAreaStyles = {
|
||||
@ -125,7 +177,7 @@ class MyToDoList extends React.Component {
|
||||
this.state = {
|
||||
toDoList: [],
|
||||
userInput: ''
|
||||
}
|
||||
};
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
}
|
||||
@ -141,8 +193,8 @@ class MyToDoList extends React.Component {
|
||||
});
|
||||
}
|
||||
render() {
|
||||
const items = this.state.toDoList.map( (item, i) => {
|
||||
return <li key={i}>{item}</li>
|
||||
const items = this.state.toDoList.map((item, i) => {
|
||||
return <li key={i}>{item}</li>;
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
@ -150,16 +202,16 @@ class MyToDoList extends React.Component {
|
||||
onChange={this.handleChange}
|
||||
value={this.state.userInput}
|
||||
style={textAreaStyles}
|
||||
placeholder="Separate Items With Commas" /><br />
|
||||
placeholder='Separate Items With Commas'
|
||||
/>
|
||||
<br />
|
||||
<button onClick={this.handleSubmit}>Create List</button>
|
||||
<h1>My "To Do" List:</h1>
|
||||
<ul>
|
||||
{items}
|
||||
</ul>
|
||||
<ul>{items}</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@ -8,8 +8,9 @@ forumTopicId: 301421
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<section id='description'>
|
||||
Sometimes you might need to know the previous state when updating the state. However, state updates may be asynchronous - this means React may batch multiple <code>setState()</code> calls into a single update. This means you can't rely on the previous value of <code>this.state</code> or <code>this.props</code> when calculating the next value. So, you should not use code like this:
|
||||
Sometimes you might need to know the previous state when updating the state. However, state updates may be asynchronous - this means React may batch multiple <code>setState()</code> calls into a single update. This means you can't rely on the previous value of <code>this.state</code> or <code>this.props</code> when calculating the next value. So, you should not use code like this:
|
||||
|
||||
```js
|
||||
this.setState({
|
||||
@ -17,7 +18,7 @@ this.setState({
|
||||
});
|
||||
```
|
||||
|
||||
Instead, you should pass <code>setState</code> a function that allows you to access state and props. Using a function with <code>setState</code> guarantees you are working with the most current values of state and props. This means that the above should be rewritten as:
|
||||
Instead, you should pass <code>setState</code> a function that allows you to access state and props. Using a function with <code>setState</code> guarantees you are working with the most current values of state and props. This means that the above should be rewritten as:
|
||||
|
||||
```js
|
||||
this.setState((state, props) => ({
|
||||
@ -34,9 +35,11 @@ this.setState(state => ({
|
||||
```
|
||||
|
||||
Note that you have to wrap the object literal in parentheses, otherwise JavaScript thinks it's a block of code.
|
||||
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
|
||||
<section id='instructions'>
|
||||
<code>MyComponent</code> has a <code>visibility</code> property which is initialized to <code>false</code>. The render method returns one view if the value of <code>visibility</code> is true, and a different view if it is false.
|
||||
Currently, there is no way of updating the <code>visibility</code> property in the component's <code>state</code>. The value should toggle back and forth between true and false. There is a click handler on the button which triggers a class method called <code>toggleVisibility()</code>. Pass a function to <code>setState</code> to define this method so that the <code>state</code> of <code>visibility</code> toggles to the opposite value when the method is called. If <code>visibility</code> is <code>false</code>, the method sets it to <code>true</code>, and vice versa.
|
||||
@ -45,6 +48,7 @@ Finally, click the button to see the conditional rendering of the component base
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
@ -54,17 +58,35 @@ tests:
|
||||
- text: The state of <code>MyComponent</code> should initialize with a <code>visibility</code> property set to <code>false</code>.
|
||||
testString: assert.strictEqual(Enzyme.mount(React.createElement(MyComponent)).state('visibility'), false);
|
||||
- text: Clicking the button element should toggle the <code>visibility</code> property in state between <code>true</code> and <code>false</code>.
|
||||
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); }; '
|
||||
testString: "(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
const first = () => {
|
||||
mockedComponent.setState({ visibility: false });
|
||||
return mockedComponent.state('visibility');
|
||||
};
|
||||
const second = () => {
|
||||
mockedComponent.find('button').simulate('click');
|
||||
return mockedComponent.state('visibility');
|
||||
};
|
||||
const third = () => {
|
||||
mockedComponent.find('button').simulate('click');
|
||||
return mockedComponent.state('visibility');
|
||||
};
|
||||
const firstValue = first();
|
||||
const secondValue = second();
|
||||
const thirdValue = third();
|
||||
assert(!firstValue && secondValue && !thirdValue);
|
||||
})();"
|
||||
- text: An anonymous function should be passed to <code>setState</code>.
|
||||
testString: const paramRegex = '[a-zA-Z$_]\\w*(,[a-zA-Z$_]\\w*)?'; const noSpaces = code.replace(/\s/g, ''); assert(new RegExp('this\\.setState\\((function\\(' + paramRegex + '\\){|([a-zA-Z$_]\\w*|\\(' + paramRegex + '\\))=>)').test(noSpaces));
|
||||
- text: <code>this</code> should not be used inside <code>setState</code>
|
||||
testString: assert(!/this\.setState\([^}]*this/.test(code));
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
|
||||
<section id='challengeSeed'>
|
||||
|
||||
<div id='jsx-seed'>
|
||||
@ -99,17 +121,17 @@ class MyComponent extends React.Component {
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
### After Test
|
||||
|
||||
<div id='jsx-teardown'>
|
||||
|
||||
```js
|
||||
ReactDOM.render(<MyComponent />, document.getElementById('root'))
|
||||
ReactDOM.render(<MyComponent />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
</div>
|
||||
@ -117,8 +139,8 @@ ReactDOM.render(<MyComponent />, document.getElementById('root'))
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
class MyComponent extends React.Component {
|
||||
@ -128,7 +150,7 @@ class MyComponent extends React.Component {
|
||||
visibility: false
|
||||
};
|
||||
this.toggleVisibility = this.toggleVisibility.bind(this);
|
||||
}
|
||||
}
|
||||
toggleVisibility() {
|
||||
this.setState(state => ({
|
||||
visibility: !state.visibility
|
||||
@ -138,19 +160,19 @@ class MyComponent extends React.Component {
|
||||
if (this.state.visibility) {
|
||||
return (
|
||||
<div>
|
||||
<button onClick = {this.toggleVisibility}>Click Me</button>
|
||||
<button onClick={this.toggleVisibility}>Click Me</button>
|
||||
<h1>Now you see me!</h1>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
<button onClick = {this.toggleVisibility}>Click Me</button>
|
||||
<button onClick={this.toggleVisibility}>Click Me</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
</section>
|
||||
|
@ -8,17 +8,20 @@ forumTopicId: 301422
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<section id='description'>
|
||||
Most web developers, at some point, need to call an API endpoint to retrieve data. If you're working with React, it's important to know where to perform this action.
|
||||
The best practice with React is to place API calls or any calls to your server in the lifecycle method <code>componentDidMount()</code>. This method is called after a component is mounted to the DOM. Any calls to <code>setState()</code> 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.
|
||||
</section>
|
||||
|
||||
## Instructions
|
||||
|
||||
<section id='instructions'>
|
||||
There is a mock API call in <code>componentDidMount()</code>. It sets state after 2.5 seconds to simulate calling a server to retrieve data. This example requests the current total active users for a site. In the render method, render the value of <code>activeUsers</code> in the <code>h1</code>. Watch what happens in the preview, and feel free to change the timeout to see the different effects.
|
||||
</section>
|
||||
|
||||
## Tests
|
||||
|
||||
<section id='tests'>
|
||||
|
||||
```yml
|
||||
@ -28,13 +31,25 @@ tests:
|
||||
- text: Component state should be updated with a timeout function in <code>componentDidMount</code>.
|
||||
testString: assert((() => { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return new RegExp('setTimeout(.|\n)+setState(.|\n)+activeUsers').test(String(mockedComponent.instance().componentDidMount)); })());
|
||||
- text: The <code>h1</code> tag should render the <code>activeUsers</code> value from <code>MyComponent</code>'s state.
|
||||
testString: 'async () => { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); const first = () => { mockedComponent.setState({ activeUsers: 1237 }); return mockedComponent.find(''h1'').text(); }; const second = () => { mockedComponent.setState({ activeUsers: 1000 }); return mockedComponent.find(''h1'').text(); }; assert(new RegExp(''1237'').test(first()) && new RegExp(''1000'').test(second())); }; '
|
||||
testString: "(() => {
|
||||
const mockedComponent = Enzyme.mount(React.createElement(MyComponent));
|
||||
const first = () => {
|
||||
mockedComponent.setState({ activeUsers: 1237 });
|
||||
return mockedComponent.find('h1').text();
|
||||
};
|
||||
const second = () => {
|
||||
mockedComponent.setState({ activeUsers: 1000 });
|
||||
return mockedComponent.find('h1').text();
|
||||
};
|
||||
assert(new RegExp('1237').test(first()) && new RegExp('1000').test(second()));
|
||||
})();"
|
||||
|
||||
```
|
||||
|
||||
</section>
|
||||
|
||||
## Challenge Seed
|
||||
|
||||
<section id='challengeSeed'>
|
||||
|
||||
<div id='jsx-seed'>
|
||||
@ -48,7 +63,7 @@ class MyComponent extends React.Component {
|
||||
};
|
||||
}
|
||||
componentDidMount() {
|
||||
setTimeout( () => {
|
||||
setTimeout(() => {
|
||||
this.setState({
|
||||
activeUsers: 1273
|
||||
});
|
||||
@ -57,21 +72,21 @@ class MyComponent extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Active Users: { /* change code here */ }</h1>
|
||||
<h1>Active Users: {/* change code here */}</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
### After Test
|
||||
|
||||
<div id='jsx-teardown'>
|
||||
|
||||
```js
|
||||
ReactDOM.render(<MyComponent />, document.getElementById('root'))
|
||||
ReactDOM.render(<MyComponent />, document.getElementById('root'));
|
||||
```
|
||||
|
||||
</div>
|
||||
@ -79,8 +94,8 @@ ReactDOM.render(<MyComponent />, document.getElementById('root'))
|
||||
</section>
|
||||
|
||||
## Solution
|
||||
<section id='solution'>
|
||||
|
||||
<section id='solution'>
|
||||
|
||||
```js
|
||||
class MyComponent extends React.Component {
|
||||
@ -91,7 +106,7 @@ class MyComponent extends React.Component {
|
||||
};
|
||||
}
|
||||
componentDidMount() {
|
||||
setTimeout( () => {
|
||||
setTimeout(() => {
|
||||
this.setState({
|
||||
activeUsers: 1273
|
||||
});
|
||||
@ -104,7 +119,7 @@ class MyComponent extends React.Component {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
</section>
|
||||
|
Reference in New Issue
Block a user