diff --git a/challenges/03-front-end-libraries/react-and-redux.json b/challenges/03-front-end-libraries/react-and-redux.json index 020aef9d41..04645dcd12 100644 --- a/challenges/03-front-end-libraries/react-and-redux.json +++ b/challenges/03-front-end-libraries/react-and-redux.json @@ -50,7 +50,7 @@ }, "tests": [ "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); return mockedComponent.find('div').text() === '' })(), 'message: The DisplayMessages component should render an empty div element.');", - "getUserInput => assert((function() { const noWhiteSpace = getUserInput('index').replace(/ /g,''); return noWhiteSpace.includes('constructor(props)') && noWhiteSpace.includes('super(props'); })(), 'message: The DisplayMessages constructor should be called properly with super, passing in props.');", + "getUserInput => assert((function() { const noWhiteSpace = getUserInput('index').replace(/\\s/g,''); return noWhiteSpace.includes('constructor(props)') && noWhiteSpace.includes('super(props'); })(), 'message: The DisplayMessages constructor should be called properly with super, passing in props.');", "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(DisplayMessages)); const initialState = mockedComponent.state(); return typeof initialState === 'object' && initialState.input === '' && Array.isArray(initialState.messages) && initialState.messages.length === 0; })(), 'message: The DisplayMessages component should have an initial state equal to {input: \"\", messages: []}.');" ], "solutions": [ @@ -258,7 +258,7 @@ }, "tests": [ "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return mockedComponent.find('AppWrapper').length === 1; })(), 'message: The AppWrapper should render.');", - "getUserInput => assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return getUserInput('index').replace(/ /g,'').includes(''); })(), 'message: The Provider wrapper component should have a prop of store passed to it, equal to the Redux store.');", + "getUserInput => assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return getUserInput('index').replace(/\\s/g,'').includes(''); })(), 'message: The Provider wrapper component should have a prop of store passed to it, equal to the Redux store.');", "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return mockedComponent.find('AppWrapper').find('DisplayMessages').length === 1; })(), 'message: DisplayMessages should render as a child of AppWrapper.');", "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(AppWrapper)); return mockedComponent.find('div').length === 1 && mockedComponent.find('h2').length === 1 && mockedComponent.find('button').length === 1 && mockedComponent.find('ul').length === 1; })(), 'message: The DisplayMessages component should render an h2, input, button, and ul element.');" ], diff --git a/challenges/03-front-end-libraries/react.json b/challenges/03-front-end-libraries/react.json index 6ee2923436..3eaca1d226 100644 --- a/challenges/03-front-end-libraries/react.json +++ b/challenges/03-front-end-libraries/react.json @@ -12,7 +12,7 @@ "src": "https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react-dom.js" } ], - "template": "
${ source }", + "template": "
${ source }", "challenges": [ { "id": "587d7dbc367417b2b2512bb1", @@ -143,11 +143,7 @@ "releasedOn": "December 25, 2017", "description": [ "So far, you've learned that JSX is a convenient tool to write readable HTML within JavaScript. With React, we can render this JSX directly to the HTML DOM using React's rendering API known as ReactDOM.", - "ReactDOM offers a simple method to render React elements to the DOM which looks like this: ReactDOM.render(componentToRender, targetNode).", - "", + "ReactDOM offers a simple method to render React elements to the DOM which looks like this: ReactDOM.render(componentToRender, targetNode), where the first argument is the React element or component that you want to render, and the second argument is the DOM node that you want to render the component to.", "As you would expect, ReactDOM.render() must be called after the JSX element declarations, just like how you must declare variables before using them.", "
", "The code editor has a simple JSX component. Use the ReactDOM.render() method to render this component to the page. You can pass defined JSX elements directly in as the first argument and use document.getElementById() to select the DOM node to render them to. There is a div with id='challenge-node' available for you to use. Make sure you don't change the JSX constant." @@ -166,8 +162,7 @@ ");", "// change code below this line", "" - ], - "tail": "ReactDOM.render(JSX, document.getElementById('root'))" + ] } }, "tests": [ @@ -251,9 +246,9 @@ " remove comment and change code above this line */}", " ", ");", - "", - "ReactDOM.render(JSX, document.getElementById('root'));" - ] + "" + ], + "tail": "ReactDOM.render(JSX, document.getElementById('root'))" } }, "tests": [ @@ -680,8 +675,7 @@ "", "// change code below this line", "" - ], - "tail": "ReactDOM.render(, document.getElementById('root'))" + ] } }, "tests": [ @@ -716,8 +710,7 @@ "contents": [ "// change code below this line", "" - ], - "tail": "ReactDOM.render(, document.getElementById('root'))" + ] } }, "tests": [ @@ -1129,8 +1122,8 @@ "tests": [ "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(CampSite)); return mockedComponent.find('CampSite').length === 1; })(), 'message: The CampSite component should render.');", "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(CampSite)); return mockedComponent.find('Camper').length === 1; })(), 'message: The Camper component should render.');", - "getUserInput => assert((function() { const noWhiteSpace = getUserInput('index').replace(/ /g, '').replace(/\\r?\\n|\\r/g, ''); const verify1 = 'Camper.defaultProps={name:\\'CamperBot\\'}'; const verify2 = 'Camper.defaultProps={name:\"CamperBot\"}'; return (noWhiteSpace.includes(verify1) || noWhiteSpace.includes(verify2)); })(), 'message: The Camper component should include default props which assign the string CamperBot to the key name.');", - "getUserInput => assert((function() { const mockedComponent = Enzyme.mount(React.createElement(CampSite)); const noWhiteSpace = getUserInput('index').replace(/ /g, '').replace(/\\r?\\n|\\r/g, ''); const verifyDefaultProps = 'Camper.propTypes={name:PropTypes.string.isRequired'; return noWhiteSpace.includes(verifyDefaultProps); })(), 'message: The Camper component should include prop types which require the name prop to be of type string.');", + "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)); })(), 'message: The Camper component should include default props which assign the string CamperBot to the key name.');", + "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); })(), 'message: The Camper component should include prop types which require the name prop to be of type string.');", "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(CampSite)); return mockedComponent.find('p').text() === mockedComponent.find('Camper').props().name; })(), 'message: The Camper component should contain a p element with only the text from the name prop.');" ], "solutions": [ @@ -1235,7 +1228,7 @@ "tests": [ "assert(Enzyme.mount(React.createElement(MyComponent)).state('name') === 'freeCodeCamp', 'message: MyComponent should have a key name with value freeCodeCamp stored in its state.');", "assert(/

.*<\\/h1><\\/div>/.test(Enzyme.mount(React.createElement(MyComponent)).html()), 'message: MyComponent should render an h1 header enclosed in a single div.');", - "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 === '

TestName

', 'message: The rendered h1 header should contain text rendered from the component's state.'); };" + "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 === '

TestName

', 'message: The rendered 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

{this.state.name}

\n { /* change code above this line */ }\n
\n );\n }\n};" @@ -1289,7 +1282,7 @@ "assert(Enzyme.mount(React.createElement(MyComponent)).state('name') === 'freeCodeCamp', 'message: MyComponent should have a key name with value freeCodeCamp stored in its state.');", "assert(/

.*<\\/h1><\\/div>/.test(Enzyme.mount(React.createElement(MyComponent)).html()), 'message: MyComponent should render an h1 header enclosed in a single div.');", "getUserInput => assert(/

\\n*\\s*\\{\\s*name\\s*\\}\\s*\\n*<\\/h1>/.test(getUserInput('index')), 'message: The rendered h1 tag should include a reference to {name}.');", - "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 === '

TestName

', 'message: The rendered h1 header should contain text rendered from the component's state.'); };" + "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 === '

TestName

', 'message: The rendered 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

{name}

\n { /* change code above this line */ }\n
\n );\n }\n};" @@ -1346,7 +1339,7 @@ "tests": [ "assert(Enzyme.mount(React.createElement(MyComponent)).state('name') === 'Initial State', 'message: The state of MyComponent should initialize with the key value pair { name: Initial State }.');", "assert(Enzyme.mount(React.createElement(MyComponent)).find('h1').length === 1, 'message: MyComponent should render an h1 header.');", - "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), 'message: The rendered h1 header should contain text rendered from the component's state.'); };", + "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), 'message: The rendered h1 header should contain text rendered from the component's state.'); };", "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.setState({ name: 'React Rocks!' }); return waitForIt(() => mockedComponent.state('name')); }; const firstValue = await first(); const secondValue = await second(); assert(firstValue === 'Before' && secondValue === 'React Rocks!', 'message: Calling the handleClick method on MyComponent should set the name property in state to equal React Rocks!.'); }; " ], "solutions": [ @@ -1658,9 +1651,9 @@ "tests": [ "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) })(), 'message: MyForm should return a div element which contains a form and an h1 tag. The form should include an input and a button.');", "assert(Enzyme.mount(React.createElement(MyForm)).state('input') === '' && Enzyme.mount(React.createElement(MyForm)).state('submit') === '', 'message: The state of MyForm should initialize with input and submit properties, both set to empty strings.');", - "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', 'message: Typing in the input element should update the input property of the component's state.'); }; ", + "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', 'message: Typing in the input element should update the input property of the component's state.'); }; ", "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', 'message: Submitting the form should run handleSubmit which should set the submit property in state equal to the current input.'); };", - "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', 'message: The h1 header should render the value of the submit field from the component's state.'); }; " + "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', 'message: The h1 header should render the value of the submit 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

{this.state.submit}

\n
\n );\n }\n};" @@ -1739,7 +1732,7 @@ "description": [ "You can pass state as props to child components, but you're not limited to passing data. You can also pass handler functions or any method that's defined on a React component to a child component. This is how you allow child components to interact with their parent components. You pass methods to a child just like a regular prop. It's assigned a name and you have access to that method name under this.props in the child component.", "
", - "There are three components outlined in the code editor. The MyApp component is the parent that will render the GetInput and RenderInput child components. Add the GetInput component to the render method in MyApp, then pass it a prop called input assigned to inputValue from MyApp's state. Also create a prop called handleChange and pass the input handler handleChange to it.", + "There are three components outlined in the code editor. The MyApp component is the parent that will render the GetInput and RenderInput child components. Add the GetInput component to the render method in MyApp, then pass it a prop called input assigned to inputValue from MyApp's state. Also create a prop called handleChange and pass the input handler handleChange to it.", "Next, add RenderInput to the render method in MyApp, then create a prop called input and pass the inputValue from state to it. Once you are finished you will be able to type in the input field in the GetInput component, which then calls the handler method in its parent via props. This updates the input in the state of the parent, which is passed as props to both children. Observe how the data flows between the components and how the single source of truth remains the state of the parent component. Admittedly, this example is a bit contrived, but should serve to illustrate how data and callbacks can be passed between React components." ], "files": { @@ -1917,7 +1910,7 @@ "tests": [ "assert((() => { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return (mockedComponent.find('div').length === 1 && mockedComponent.find('h1').length === 1); })(), 'message: MyComponent should render a div element which wraps an h1 tag.');", "assert((() => { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return new RegExp('setTimeout(.|\\n)+setState(.|\\n)+activeUsers').test(String(mockedComponent.instance().componentDidMount)); })(), 'message: Component state should be updated with a timeout function in componentDidMount.');", - "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), 'message: The h1 tag should render the activeUsers value from MyComponent's state.); }; " + "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), 'message: The h1 tag should render the activeUsers value from MyComponent'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

Active Users: {this.state.activeUsers}

\n
\n );\n }\n};" @@ -2340,9 +2333,9 @@ }, "tests": [ "assert.strictEqual(Enzyme.mount(React.createElement(MagicEightBall)).find('MagicEightBall').length, 1, 'message: The MagicEightBall compponent should exist and should render to the page.');", - "assert.strictEqual(Enzyme.mount(React.createElement(MagicEightBall)).children().childAt(0).name(), 'input', \"message: MagicEightBall's first child should be an input element.\");", - "assert.strictEqual(Enzyme.mount(React.createElement(MagicEightBall)).children().childAt(2).name(), 'button', \"message: MagicEightBall's third child should be a button element.\");", - "assert(Enzyme.mount(React.createElement(MagicEightBall)).state('randomIndex') === '' && Enzyme.mount(React.createElement(MagicEightBall)).state('userInput') === '', \"message: MagicEightBall's state should be initialized with a property of userInput and a property of randomIndex both set to a value of an empty string.\");", + "assert.strictEqual(Enzyme.mount(React.createElement(MagicEightBall)).children().childAt(0).name(), 'input', \"message: MagicEightBall's first child should be an input element.\");", + "assert.strictEqual(Enzyme.mount(React.createElement(MagicEightBall)).children().childAt(2).name(), 'button', \"message: MagicEightBall's third child should be a button element.\");", + "assert(Enzyme.mount(React.createElement(MagicEightBall)).state('randomIndex') === '' && Enzyme.mount(React.createElement(MagicEightBall)).state('userInput') === '', \"message: MagicEightBall's state should be initialized with a property of userInput and a property of randomIndex both set to a value of an empty string.\");", "assert(Enzyme.mount(React.createElement(MagicEightBall)).find('p').length === 1 && Enzyme.mount(React.createElement(MagicEightBall)).find('p').text() === '', 'message: When MagicEightBall is first mounted to the DOM, it should return an empty p element.');", "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, 'message: When text is entered into the input element and the button is clicked, the MagicEightBall compponent should return a p element that contains a random element from the possibleAnswers array.'); }; " ], @@ -2402,7 +2395,7 @@ "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return mockedComponent.find('MyComponent').length === 1; })(), 'message: MyComponent should exist and render.');", "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, 'message: When display is set to true, a div, button, and h1 should render.'); }; ", "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, 'message: When display is set to false, only a div and button should render.'); }; ", - "getUserInput => assert(getUserInput('index').includes('if') === true && getUserInput('index').includes('else') === true, 'message: The render method should use an if/else statement to check the condition of this.state.display.');" + "getUserInput => assert(getUserInput('index').includes('if') && getUserInput('index').includes('else'), 'message: The render method should use an if/else statement 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 if (this.state.display) {\n return (\n
\n \n

Displayed!

\n
\n );\n } else {\n return (\n
\n \n
\n );\n }\n }\n};" @@ -2460,7 +2453,7 @@ "assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return mockedComponent.find('MyComponent').length; })(), 'message: MyComponent should exist and render.');", "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, 'message: When display is set to true, a div, button, and h1 should render.'); }; ", "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, 'message: When display is set to false, only a div and button should render.'); }; ", - "getUserInput=> assert(getUserInput('index').includes('&&'), 'message: The render method should use the && logical operator to check the condition of this.state.display.');" + "getUserInput => assert(getUserInput('index').includes('&&'), 'message: 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 &&

Displayed!

}\n
\n );\n }\n};" @@ -2538,10 +2531,10 @@ }, "tests": [ "assert(Enzyme.mount(React.createElement(CheckUserAge)).find('div').find('input').length === 1 && Enzyme.mount(React.createElement(CheckUserAge)).find('div').find('button').length === 1, 'message: The CheckUserAge component should render with a single input element and a single button element.');", - "assert(Enzyme.mount(React.createElement(CheckUserAge)).state().input === '' && Enzyme.mount(React.createElement(CheckUserAge)).state().userAge === '', \"message: The CheckUserAge component's state should be initialized with a property of userAge and a property of input, both set to a value of an empty string.\");", - "assert(Enzyme.mount(React.createElement(CheckUserAge)).find('button').text() === 'Submit', \"message: When the CheckUserAge component is first rendered to the DOM, the button's inner text should be Submit.\");", - "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', 'message: When a number of less than 18 is entered into the input element and the button is clicked, the button's inner text should read You Shall Not Pass.); }; ", - "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', 'message: When a number greater than or equal to 18 is entered into the input element and the button is clicked, the button's inner text should read You May Enter.); }; ", + "assert(Enzyme.mount(React.createElement(CheckUserAge)).state().input === '' && Enzyme.mount(React.createElement(CheckUserAge)).state().userAge === '', 'message: The CheckUserAge component's state should be initialized with a property of userAge and a property of input, both set to a value of an empty string.');", + "assert(Enzyme.mount(React.createElement(CheckUserAge)).find('button').text() === 'Submit', 'message: When the CheckUserAge component is first rendered to the DOM, the button's inner text should be Submit.');", + "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', 'message: When a number of less than 18 is entered into the input element and the button is clicked, the button's inner text should read You Shall Not Pass.'); }; ", + "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', 'message: When a number greater than or equal to 18 is entered into the input element and the button is clicked, the button's inner text should read You May Enter.'); }; ", "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', 'message: Once a number has been submitted, and the value of the input is once again changed, the button should return to reading Submit.'); }; ", "assert(new RegExp(/(\\s|;)if(\\s|\\()/).test(Enzyme.mount(React.createElement(CheckUserAge)).instance().render.toString()) === false, 'message: Your code should not contain any if/else statements.');" ], @@ -2844,7 +2837,7 @@ "The map array method is a powerful tool that you will use often when working with React. Another method related to map is filter, which filters the contents of an array based on a condition, then returns a new array. For example, if you have an array of users that all have a property online which can be set to true or false, you can filter only those users that are online by writing:", "let onlineUsers = users.filter(user => user.online);", "
", - "In the code editor, MyComponent's state is initialized with an array of users. Some users are online and some aren't. Filter the array so you see only the users who are online. To do this, first use filter to return a new array containing only the users whose online property is true. Then, in the renderOnline variable, map over the filtered array, and return a li element for each user that contains the text of their username. Be sure to include a unique key as well, like in the last challenges." + "In the code editor, MyComponent's state is initialized with an array of users. Some users are online and some aren't. Filter the array so you see only the users who are online. To do this, first use filter to return a new array containing only the users whose online property is true. Then, in the renderOnline variable, map over the filtered array, and return a li element for each user that contains the text of their username. Be sure to include a unique key as well, like in the last challenges." ], "files": { "indexjsx": { @@ -2903,9 +2896,9 @@ }, "tests": [ "assert.strictEqual(Enzyme.mount(React.createElement(MyComponent)).find('MyComponent').length, 1, 'message: MyComponent should exist and render to the page.');", - "assert(Array.isArray(Enzyme.mount(React.createElement(MyComponent)).state('users')) === true && Enzyme.mount(React.createElement(MyComponent)).state('users').length === 6, \"message: MyComponent's state should be initialized to an array of six users.\");", + "assert(Array.isArray(Enzyme.mount(React.createElement(MyComponent)).state('users')) === true && Enzyme.mount(React.createElement(MyComponent)).state('users').length === 6, \"message: MyComponent's state should be initialized to an array of six users.\");", "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, 'message: MyComponent should return a div, an h1, and then an unordered list containing li elements for every user whose online status is set to true.'); }; ", - "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
, 'message: MyComponent should render li elements that contain the username of each online user.); }; ", + "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
, 'message: MyComponent should render li elements that contain the username of each online user.'); }; ", "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; })(), 'message: Each list item element should have a unique key attribute.');" ], "solutions": [ diff --git a/challenges/03-front-end-libraries/redux.json b/challenges/03-front-end-libraries/redux.json index 61d69b4bdb..49fd798443 100644 --- a/challenges/03-front-end-libraries/redux.json +++ b/challenges/03-front-end-libraries/redux.json @@ -195,7 +195,7 @@ "tests": [ "assert(loginAction().type === 'LOGIN', 'message: Calling the function loginAction should return an object with type property set to the string LOGIN.');", "assert(store.getState().login === false, 'message: The store should be initialized with an object with property login set to false.');", - "getUserInput => assert((function() { let noWhiteSpace = getUserInput('index').replace(/ /g,''); return noWhiteSpace.includes('store.dispatch(loginAction())') || noWhiteSpace.includes('store.dispatch({type: \\'LOGIN\\'})') === true })(), 'message: The store.dispatch() method should be used to dispatch an action of type LOGIN.');" + "getUserInput => assert((function() { let noWhiteSpace = getUserInput('index').replace(/\\s/g,''); return noWhiteSpace.includes('store.dispatch(loginAction())') || noWhiteSpace.includes('store.dispatch({type: \\'LOGIN\\'})') === true })(), 'message: The store.dispatch() method should be used to dispatch an action of type LOGIN.');" ], "solutions": [ "const store = Redux.createStore(\n (state = {login: false}) => state\n);\n\nconst loginAction = () => {\n return {\n type: 'LOGIN'\n }\n};\n\n// Dispatch the action here:\nstore.dispatch(loginAction());" @@ -381,8 +381,8 @@ "assert((function() { const initialState = store.getState(); store.dispatch(loginUser()); const afterLogin = store.getState(); return initialState.authenticated === false && afterLogin.authenticated === true })(), 'message: Dispatching loginUser should update the login property in the store state to true.');", "assert((function() { store.dispatch(loginUser()); const loggedIn = store.getState(); store.dispatch(logoutUser()); const afterLogout = store.getState(); return loggedIn.authenticated === true && afterLogout.authenticated === false })(), 'message: Dispatching logoutUser should update the login property in the store state to false.');", "getUserInput => assert((function() { return typeof authReducer === 'function' && getUserInput('index').toString().includes('switch') && getUserInput('index').toString().includes('case') && getUserInput('index').toString().includes('default') })(), 'message: The authReducer function should handle multiple action types with a switch statement.');", - "getUserInput => assert((function() { const noWhiteSpace = getUserInput('index').toString().replace(/ /g,''); return (noWhiteSpace.includes('constLOGIN=\\'LOGIN\\'') || noWhiteSpace.includes('constLOGIN=\"LOGIN\"')) && (noWhiteSpace.includes('constLOGOUT=\\'LOGOUT\\'') || noWhiteSpace.includes('constLOGOUT=\"LOGOUT\"')) })(), 'message: LOGIN and LOGOUT should be declared as const values and should be assigned strings of LOGINand LOGOUT.');", - "getUserInput => assert((function() { const noWhiteSpace = getUserInput('index').toString().replace(/ /g,''); return noWhiteSpace.includes('caseLOGIN:') && noWhiteSpace.includes('caseLOGOUT:') && noWhiteSpace.includes('type:LOGIN') && noWhiteSpace.includes('type:LOGOUT') })(), 'message: The action creators and the reducer should reference the LOGIN and LOGOUT constants.');" + "getUserInput => assert((function() { const noWhiteSpace = getUserInput('index').toString().replace(/\\s/g,''); return (noWhiteSpace.includes('constLOGIN=\\'LOGIN\\'') || noWhiteSpace.includes('constLOGIN=\"LOGIN\"')) && (noWhiteSpace.includes('constLOGOUT=\\'LOGOUT\\'') || noWhiteSpace.includes('constLOGOUT=\"LOGOUT\"')) })(), 'message: LOGIN and LOGOUT should be declared as const values and should be assigned strings of LOGINand LOGOUT.');", + "getUserInput => assert((function() { const noWhiteSpace = getUserInput('index').toString().replace(/\\s/g,''); return noWhiteSpace.includes('caseLOGIN:') && noWhiteSpace.includes('caseLOGOUT:') && noWhiteSpace.includes('type:LOGIN') && noWhiteSpace.includes('type:LOGOUT') })(), 'message: The action creators and the reducer should reference the LOGIN and LOGOUT constants.');" ], "solutions": [ "const LOGIN = 'LOGIN';\nconst LOGOUT = 'LOGOUT';\n\nconst defaultState = {\n authenticated: false\n};\n\nconst authReducer = (state = defaultState, action) => {\n\n switch (action.type) {\n\n case LOGIN:\n return {\n authenticated: true\n }\n\n case LOGOUT:\n return {\n authenticated: false\n }\n\n default:\n return state;\n\n }\n\n};\n\nconst store = Redux.createStore(authReducer);\n\nconst loginUser = () => {\n return {\n type: LOGIN\n }\n};\n\nconst logoutUser = () => {\n return {\n type: LOGOUT\n }\n};" @@ -514,7 +514,7 @@ "assert((function() { const initalState = store.getState().count; store.dispatch({type: INCREMENT}); store.dispatch({type: INCREMENT}); const firstState = store.getState().count; store.dispatch({type: DECREMENT}); const secondState = store.getState().count; return firstState === initalState + 2 && secondState === firstState - 1 })(), 'message: The counterReducer should increment and decrement the state.');", "assert((function() { store.dispatch({type: LOGIN}); const loggedIn = store.getState().auth.authenticated; store.dispatch({type: LOGOUT}); const loggedOut = store.getState().auth.authenticated; return loggedIn === true && loggedOut === false })(), 'message: The authReducer should toggle the state of authenticated between true and false.');", "assert((function() { const state = store.getState(); return typeof state.auth === 'object' && typeof state.auth.authenticated === 'boolean' && typeof state.count === 'number' })(), 'message: The store state should have two keys: count, which holds a number, and auth, which holds an object. The auth object should have a property of authenticated, which holds a boolean.');", - "getUserInput => assert((function() { const noWhiteSpace = getUserInput('index').replace(/ /g,''); return typeof rootReducer === 'function' && noWhiteSpace.includes('Redux.combineReducers') })(), 'message: The rootReducer should be a function that combines the counterReducer and the authReducer.');" + "getUserInput => assert((function() { const noWhiteSpace = getUserInput('index').replace(/\\s/g,''); return typeof rootReducer === 'function' && noWhiteSpace.includes('Redux.combineReducers') })(), 'message: The rootReducer should be a function that combines the counterReducer and the authReducer.');" ], "solutions": [ "const INCREMENT = 'INCREMENT';\nconst DECREMENT = 'DECREMENT';\n\nconst counterReducer = (state = 0, action) => {\n switch(action.type) {\n case INCREMENT:\n return state + 1;\n case DECREMENT:\n return state - 1;\n default:\n return state;\n }\n};\n\nconst LOGIN = 'LOGIN';\nconst LOGOUT = 'LOGOUT';\n\nconst authReducer = (state = {authenticated: false}, action) => {\n switch(action.type) {\n case LOGIN:\n return {\n authenticated: true\n }\n case LOGOUT:\n return {\n authenticated: false\n }\n default:\n return state;\n }\n};\n\nconst rootReducer = Redux.combineReducers({\n count: counterReducer,\n auth: authReducer\n});\n\nconst store = Redux.createStore(rootReducer);" @@ -651,7 +651,7 @@ "assert(receivedData('data').type === RECEIVED_DATA, 'message: The receivedData action creator should return an object of type equal to the value of RECEIVED_DATA.');", "assert(typeof asyncDataReducer === 'function', 'message: asyncDataReducer should be a function.');", "assert((function() { const initialState = store.getState(); store.dispatch(requestingData()); const reqState = store.getState(); return initialState.fetching === false && reqState.fetching === true })(), 'message: Dispatching the requestingData action creator should update the store state property of fetching to true.');", - "assert((function() { const noWhiteSpace = handleAsync.toString().replace(/ /g,''); return noWhiteSpace.includes('dispatch(requestingData())') === true && noWhiteSpace.includes('dispatch(receivedData(data))') === true })(), 'message: Dispatching handleAsync should dispatch the data request action and then dispatch the received data action after a delay.');" + "assert((function() { const noWhiteSpace = handleAsync.toString().replace(/\\s/g,''); return noWhiteSpace.includes('dispatch(requestingData())') === true && noWhiteSpace.includes('dispatch(receivedData(data))') === true })(), 'message: Dispatching handleAsync should dispatch the data request action and then dispatch the received data action after a delay.');" ], "solutions": [ "const REQUESTING_DATA = 'REQUESTING_DATA'\nconst RECEIVED_DATA = 'RECEIVED_DATA'\n\nconst requestingData = () => { return {type: REQUESTING_DATA} }\nconst receivedData = (data) => { return {type: RECEIVED_DATA, users: data.users} }\n\nconst handleAsync = () => {\n return function(dispatch) {\n dispatch(requestingData());\n setTimeout(function() {\n let data = {\n users: ['Jeff', 'William', 'Alice']\n }\n dispatch(receivedData(data));\n }, 2500);\n }\n};\n\nconst defaultState = {\n fetching: false,\n users: []\n};\n\nconst asyncDataReducer = (state = defaultState, action) => {\n switch(action.type) {\n case REQUESTING_DATA:\n return {\n fetching: true,\n users: []\n }\n case RECEIVED_DATA:\n return {\n fetching: false,\n users: action.users\n }\n default:\n return state;\n }\n};\n\nconst store = Redux.createStore(\n asyncDataReducer,\n Redux.applyMiddleware(ReduxThunk.default)\n);"