Array.map()
in React illustrates this concept.
For example, you create a simple "To Do List" app. As the programmer, you have no way of knowing how many items a user might have on their list. You need to set up your component to dynamically render the correct number of list elements long before someone using the program decides that today is laundry day.
MyToDoList
component set up. Some of this code should look familiar if you completed the controlled form challenge. You'll notice a textarea
and a button
, along with a couple of methods that track their states, but nothing is rendered to the page yet.
Inside the constructor
, create a this.state
object and define two states: userInput
should be initialized as an empty string, and toDoList
should be initialized as an empty array. Next, delete the comment in the render()
method next to the items
variable. In its place, map over the toDoList
array stored in the component's internal state and dynamically render a li
for each item. Try entering the string eat, code, sleep, repeat
into the textarea
, then click the button and see what happens.
Note: You may know that all sibling child elements created by a mapping operation like this do need to be supplied with a unique key
attribute. Don't worry, this is the topic of the next challenge.
MyToDoList
should be a textarea
element.
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyToDoList)); return mockedComponent.find('MyToDoList').children().childAt(0).type() === 'textarea'; })(), 'The first child of MyToDoList
should be a textarea
element.');
- text: The third child of MyToDoList
should be a button
element.
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyToDoList)); return mockedComponent.find('MyToDoList').children().childAt(2).type() === 'button'; })(), 'The third child of MyToDoList
should be a button
element.');
- text: The state of MyToDoList
should be initialized with toDoList
as an empty array.
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyToDoList)); const initialState = mockedComponent.state(); return Array.isArray(initialState.toDoList) === true && initialState.toDoList.length === 0; })(), 'The state of MyToDoList
should be initialized with toDoList
as an empty array.');
- text: The state of MyToDoList
should be initialized with userInput
as an empty string.
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyToDoList)); const initialState = mockedComponent.state(); return typeof initialState.userInput === 'string' && initialState.userInput.length === 0; })(), 'The state of MyToDoList
should be initialized with userInput
as an empty string.');
- text: When the Create List
button is clicked, the MyToDoList
component should dynamically return an unordered list that contains a list item element for every item of a comma-separated list entered into the textarea
element.
testString: 'async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); const mockedComponent = Enzyme.mount(React.createElement(MyToDoList)); const simulateChange = (el, value) => el.simulate(''change'', {target: {value}}); const state_1 = () => { return waitForIt(() => mockedComponent.find(''ul'').find(''li''))}; const setInput = () => { return waitForIt(() => simulateChange(mockedComponent.find(''textarea''), "testA, testB, testC"))}; const click = () => { return waitForIt(() => mockedComponent.find(''button'').simulate(''click''))}; const state_2 = () => { return waitForIt(() => { const nodes = mockedComponent.find(''ul'').find(''li''); return { nodes, text: nodes.reduce((t, n) => t + n.text(), '''') }; })}; const setInput_2 = () => { return waitForIt(() => simulateChange(mockedComponent.find(''textarea''), "t1, t2, t3, t4, t5, t6"))}; const click_1 = () => { return waitForIt(() => mockedComponent.find(''button'').simulate(''click''))}; const state_3 = () => { return waitForIt(() => { const nodes = mockedComponent.find(''ul'').find(''li''); return { nodes, text: nodes.reduce((t, n) => t + n.text(), '''') }; })}; const awaited_state_1 = await state_1(); const awaited_setInput = await setInput(); const awaited_click = await click(); const awaited_state_2 = await state_2(); const awaited_setInput_2 = await setInput_2(); const awaited_click_1 = await click_1(); const awaited_state_3 = await state_3(); assert(awaited_state_1.length === 0 && awaited_state_2.nodes.length === 3 && awaited_state_3.nodes.length === 6 && awaited_state_2.text === ''testA testB testC'' && awaited_state_3.text === ''t1 t2 t3 t4 t5 t6'', ''When the Create List
button is clicked, the MyToDoList
component should dynamically return an unordered list that contains a list item element for every item of a comma-separated list entered into the textarea
element.''); }; '
```