this
关键字。一类组件中访问的道具,你前言您使用与访问它的代码this
。例如,如果ES6类组件具有名为data
的prop,则在JSX中编写{this.props.data}
。 this
关键字。要访问类组件中的 props,你需要在在访问它的代码前面添加this
。例如,如果 ES6 类组件有一个名为data
的 prop,你可以在 JSX 中这样写:{this.props.data}
。
+ResetPassword
呈现ReturnTempPassword
组件的实例。在这里,给ReturnTempPassword
一个tempPassword
的prop,并为它tempPassword
一个至少8个字符长的字符串的值。在子项ReturnTempPassword
,访问strong
标记内的tempPassword
prop,以确保用户看到临时密码。 ResetPassword
中渲染ReturnTempPassword
组件的一个实例。在这里,为ReturnTempPassword
提供一个tempPassword
prop,并赋值给 prop 一个长度至少为 8 个字符的字符串。在子组件ReturnTempPassword
中,访问strong
标签中的tempPassword
prop,以确保用户看到临时密码。
+ResetPassword
组件应返回单个div
元素。
+ - text: ResetPassword
组件应该返回单个div
元素。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ResetPassword)); return mockedComponent.children().type() === 'div'; })());
- - text: ResetPassword
的第四个子ResetPassword
应该是ReturnTempPassword
组件。
+ - text: ResetPassword
的第四个子组件应该是ReturnTempPassword
组件。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ResetPassword)); return mockedComponent.children().childAt(3).name() === 'ReturnTempPassword'; })());
- - text: ReturnTempPassword
组件应该有一个名为tempPassword
的prop。
+ - text: ReturnTempPassword
组件应该有一个名为tempPassword
的属性。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ResetPassword)); return mockedComponent.find('ReturnTempPassword').props().tempPassword; })());
- - text: ReturnTempPassword
的tempPassword
道具应该等于至少8
字符的字符串。
+ - text: ReturnTempPassword
组件的tempPassword
prop 值应该是一个字符串,其长度至少为8
。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ResetPassword)); const temp = mockedComponent.find('ReturnTempPassword').props().tempPassword; return typeof temp === 'string' && temp.length >= 8; })());
- - text: ReturnTempPassword
组件应显示您在strong
标记内作为tempPassword
支柱创建的密码。
+ - text: ReturnTempPassword
组件应该显示你作为tempPassword
prop 创建的密码,并且密码显示在strong
标签中。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ResetPassword)); return mockedComponent.find('strong').text() === mockedComponent.find('ReturnTempPassword').props().tempPassword; })());
```
@@ -73,7 +78,6 @@ class ResetPassword extends React.Component {
);
}
};
-
```
@@ -83,7 +87,7 @@ class ResetPassword extends React.Component {
Your temporary password is: {this.props.tempPassword}
+{/* */}
来包含注释文本。 {/* */}
语法来包裹注释文本。
+div
元素中的某处添加注释,而不修改现有的h1
或p
元素。 div
元素中的某处添加注释,而不修改现有的h1
或p
元素。
+JSX
应该返回一个div
元素。
- testString: 'assert(JSX.type === "div", "The constant JSX
should return a div
element.");'
- - text: div
应包含一个h1
标记作为第一个元素。
- testString: 'assert(JSX.props.children[0].type === "h1", "The div
should contain an h1
tag as the first element.");'
+ testString: assert(JSX.type === 'div');
+ - text: div
应该包含一个h1
标签作为第一个元素。
+ testString: assert(JSX.props.children[0].type === 'h1');
- text: div
应该包含一个p
标签作为第二个元素。
- testString: 'assert(JSX.props.children[1].type === "p", "The div
should contain a p
tag as the second element.");'
- - text: JSX
应该包含一条评论。
- testString: 'getUserInput => assert(getUserInput("index").includes("/*") && getUserInput("index").includes("*/"), "The JSX
should include a comment.");'
-
+ testString: assert(JSX.props.children[1].type === 'p');
+ - text: 当前的 h1
和 p
元素不能被修改。
+ testString: assert(JSX.props.children[0].props.children === 'This is a block of JSX' && JSX.props.children[1].props.children === 'Here\'s a subtitle');
+ - text: JSX
应该包含一个注释。
+ testString: assert(/Here's a subtitle
Here's a subtitle
+componentDidMount()
方法也是附加您需要为特定功能添加的任何事件侦听器的最佳位置。 React提供了一个合成事件系统,它包装了浏览器中的本机事件系统。这意味着无论用户的浏览器如何,合成事件系统的行为都完全相同 - 即使本机事件在不同浏览器之间的行为可能不同。您已经使用了一些合成事件处理程序,如onClick()
。 React的合成事件系统非常适合用于您在DOM元素上管理的大多数交互。但是,如果要将事件处理程序附加到文档或窗口对象,则必须直接执行此操作。 componentDidMount()
方法也是添加特定功能所需的任何事件监听器的最佳位置。React 提供了一个合成事件系统,它将本地事件系统封装在浏览器中。这意味着,不管用户的浏览器如何,合成事件系统的行为都完全相同--即使不同浏览器之间的本地事件的行为可能不同。
+你已经使用了一些合成事件处理程序,如onClick()
。React 的合成事件系统非常适合用于你在 DOM 元素上管理的大多数交互。但是,如果要将事件处理程序附加到 document 或 window 对象,则必须直接执行此操作。
+componentDidMount()
方法中为keydown
事件附加事件侦听器,并让这些事件触发回调handleKeyPress()
。您可以使用document.addEventListener()
,它将事件(在引号中)作为第一个参数,将回调作为第二个参数。然后,在componentWillUnmount()
,删除此相同的事件侦听器。您可以将相同的参数传递给document.removeEventListener()
。在卸载和销毁之前,使用此生命周期方法对React组件进行任何清理是一种很好的做法。删除事件侦听器就是一个这样的清理操作的示例。 componentDidMount()
方法中为keydown
事件添加事件监听器,并让这些事件触发回调handleKeyPress()
。你可以使用document.addEventListener()
,它将事件(用引号括起来)作为第一个参数,将回调作为第二个参数。
+然后,在componentWillUnmount()
中移除相同的事件监听器。你可以把相同的参数传递给document.removeEventListener()
。在卸载和销毁 React 组件之前,最好使用这种生命周期方法对它们进行清理。移除事件监听器就是这样一个清理操作的例子。
+MyComponent
应该呈现一个包含h1
标记的div
元素。
+ - text: MyComponent
应该渲染一个包含h1
标签的div
元素。
testString: assert((() => { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return mockedComponent.find('div').children().find('h1').length === 1; })());
- - text: keydown侦听器应附加到componentDidMount
的文档。
+ - text: 应该在componentDidMount
中将 keydown 事件监听添加到到 document 上。
testString: assert((() => { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); const didMountString = mockedComponent.instance().componentDidMount.toString(); return new RegExp('document\.addEventListener(.|\n|\r)+keydown(.|\n|\r)+this\.handleKeyPress').test(didMountString); })());
- - text: 应该从componentWillUnmount
的文档中删除keydown侦听器。
+ - text: 应该在componentWillUnmount
中将 document 上的 keydown 事件监听移除。
testString: assert((() => { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); const willUnmountString = mockedComponent.instance().componentWillUnmount.toString(); return new RegExp('document\.removeEventListener(.|\n|\r)+keydown(.|\n|\r)+this\.handleKeyPress').test(willUnmountString); })());
- - text: 组件安装完成后,按enter
应更新其状态和渲染的h1
标签。
+ - text: 一旦组件装载完毕,按enter
应该会更新其 state 并渲染到h1
标签。
testString: 'async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); const beforeState = mockedComponent.state(''message''); const beforeText = mockedComponent.find(''h1'').text(); const pressEnterKey = () => { mockedComponent.instance().handleKeyPress({ keyCode: 13 }); return waitForIt(() => { mockedComponent.update(); return { state: mockedComponent.state(''message''), text: mockedComponent.find(''h1'').text()}; });}; const afterKeyPress = await pressEnterKey(); assert(beforeState !== afterKeyPress.state && beforeText !== afterKeyPress.text); }; '
```
@@ -41,7 +47,7 @@ class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
- message: "
+ message: ''
};
this.handleEnter = this.handleEnter.bind(this);
this.handleKeyPress = this.handleKeyPress.bind(this);
@@ -72,7 +78,6 @@ class MyComponent extends React.Component {
);
}
};
-
```
@@ -82,7 +87,7 @@ class MyComponent extends React.Component {
style
属性之外,还有HTML内联样式的其他几种语法差异。首先,某些CSS样式属性的名称使用驼峰大小写。例如,最后一个挑战使用fontSize
而不是font-size
设置font-size
。像font-size
这样的连字符是JavaScript对象属性的无效语法,因此React使用驼峰大小写。通常,任何带连字符的样式属性都是使用JSX中的camel case编写的。除非另有说明,否则假定所有属性值长度单位(如height
, width
和fontSize
)均为px
。例如,如果要使用em
,则将值和单位用引号括起来,如{fontSize: "4em"}
。除了默认为px
的长度值之外,所有其他属性值都应该用引号括起来。 style
属性之外,与 HTML 内联样式相比,React 的内联样式还有其他几个语法差异。首先,某些 CSS 样式属性的名称使用驼峰式命名。例如,最后一个挑战用fontSize
而不是font-size
来设置字体的大小。对于 JavaScript 对象属性来说,像font-size
这样的连字符命名是无效的语法,所以 React 使用驼峰式命名。通常,任何连字符的 style 属性在 JSX 中都是使用驼峰式命名的。
+除非另有规定,否则所有属性值是长度的(如height
、width
和fontSize
)其单位都假定为px
。例如,如果要使用em
,可以用引号将值和单位括起来,例如{fontSize: "4em"}
。除了默认为px
的长度值之外,所有其他属性值都应该用引号括起来。
+object
分配给常量以保持代码的有序性。取消注释styles
常量并声明具有三个样式属性及其值的object
。给div
一个颜色为"purple"
,字体大小为40
,边框为"2px solid purple"
。然后将style
属性设置为等于styles
常量。 对象
分配给一个常量,以保持代码的组织有序。取消对styles
常量的注释,并声明具有三个样式属性及对应值的对象
。使div
的文字颜色为"purple"
、字号为40
、边框为"2px solid purple"
。然后设置style
属性,使其等于styles
常量。
+styles
变量应该是具有三个属性的object
。
+ - text: styles
变量应该是具有三个属性的对象
。
testString: assert(Object.keys(styles).length === 3);
- - text: styles
变量的color
属性应设置为purple
的值。
+ - text: styles
变量的color
属性应该设置为purple
。
testString: assert(styles.color === 'purple');
- - text: styles
变量应该将fontSize
属性设置为值40
。
+ - text: styles
变量应该将fontSize
属性设置为40
。
testString: assert(styles.fontSize === 40);
- - text: styles
变量应该将border
属性设置为2px solid purple
的值。
+ - text: styles
变量的border
属性应该设置为2px solid purple
。
testString: assert(styles.border === "2px solid purple");
- - text: 该组件应呈现div
元素。
+ - text: 组件应该渲染一个div
元素。
testString: assert((function() { const mockedComponent = Enzyme.shallow(React.createElement(Colorful)); return mockedComponent.type() === 'div'; })());
- text: div
元素的样式应该由styles
对象定义。
testString: assert((function() { const mockedComponent = Enzyme.shallow(React.createElement(Colorful)); return (mockedComponent.props().style.color === "purple" && mockedComponent.props().style.fontSize === 40 && mockedComponent.props().style.border === "2px solid purple"); })());
@@ -41,6 +46,7 @@ tests:
state
,您还可以为组件类定义方法。类方法通常需要使用this
关键字,以便它可以访问方法范围内的类(例如state
和props
)上的属性。有几种方法可以让您的类方法访问this
。一个常用的方法是显式绑定this
所以在构造this
组件已初始化变为绑定到类方法。您可能已经注意到最后一个挑战使用this.handleClick = this.handleClick.bind(this)
作为构造函数中的handleClick
方法。然后,当您在类方法中调用类似this.setState()
的函数时, this
引用该类,并且不会被undefined
。 注意: this
关键字是JavaScript中最令人困惑的方面之一,但它在React中起着重要作用。虽然这里的行为是完全正常的,但这些课程并不是this
进行深入审查的地方,所以如果上述内容令人困惑,请参考其他课程! state
之外,你还可以为组件类定义方法。类方法通常需要使用this
关键字,以便它可以访问方法中类的属性(例如state
和props
)。有几种方法可以让你的类方法访问this
。
+一种常见的方法是在构造函数中显式地绑定this
,这样当组件初始化时,this
就会绑定到类方法。你可能已经注意到上一个挑战使用了this.handleClick = this.handleClick.bind(this)
用于其在构造函数中的handleClick
方法。然后,当你在类方法中调用像this.setState()
这样的函数时,this
指的是这个类,而不是undefined
。
+注意: this
关键字是 JavaScript 中最令人困惑的方面之一,但它在 React 中扮演着重要的角色。虽然它的行为在这里是完全正常的,但是这些课程并不深入研究this
,所以如果以上内容令你感到困惑,请参考其他课程!
+state
保持跟踪的项目计数。它还有一个方法,允许您增加此项目计数。但是,该方法不起作用,因为它使用未定义的this
关键字。通过明确地结合修复它this
到addItem()
在组件的构造方法。接下来,将单击处理程序添加到render方法中的button
元素。当按钮收到click事件时,它应该触发addItem()
方法。请记住,传递给onClick
处理程序的方法需要花括号,因为它应该直接解释为JavaScript。完成上述步骤后,您应该可以单击按钮并查看HTML中的项目计数增量。 state
的组件,用于跟踪项目计数。它还有一个方法,允许你增加此项目计数。但是,该方法不起作用,因为它使用了未定义的this
关键字。可以通过将this
显式绑定到组件构造函数中的addItem()
方法来修复它。
+接下来,向 render 方法中的button
元素添加一个单击处理程序。当按钮接收到单击事件时,它应该触发addItem()
方法。记住,传递给onClick
处理程序的方法需要使用花括号,因为它应该直接被解释为 JavaScript。
+完成上述步骤后,你应该可以单击按钮并查看 HTML 中的项目计数增量。
+MyComponent
应该返回一个div
元素,它按顺序包装两个元素,一个按钮和一个h1
元素。
+ - text: MyComponent
应返回div
元素,该元素按顺序包含两个元素,一个按钮和一个h1
元素。
testString: assert(Enzyme.mount(React.createElement(MyComponent)).find('div').length === 1 && Enzyme.mount(React.createElement(MyComponent)).find('div').childAt(0).type() === 'button' && Enzyme.mount(React.createElement(MyComponent)).find('div').childAt(1).type() === 'h1');
- - text: 'MyComponent
的状态应使用键值对{ itemCount: 0 }
初始化。'
+ - text: 'MyComponent
的 state 应该使用键值对{ itemCount: 0 }
进行初始化。'
testString: 'assert(Enzyme.mount(React.createElement(MyComponent)).state(''text'') === ''Hello'');'
- - text: 单击button
元素应该运行addItem
方法并将状态itemCount
递增1
。
+ - text: 单击button
元素应该运行addItem
方法,并使 stateitemCount
的计数增加1
。
testString: 'async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); const first = () => { mockedComponent.setState({ text: ''Hello'' }); return waitForIt(() => mockedComponent.state(''text'')); }; const second = () => { mockedComponent.find(''button'').simulate(''click''); return waitForIt(() => mockedComponent.state(''text'')); }; const firstValue = await first(); const secondValue = await second(); assert(firstValue === ''Hello'' && secondValue === ''You clicked!''); };'
```
@@ -39,15 +47,15 @@ class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
- itemCount: 0
+ text: "Hello"
};
// change code below this line
// change code above this line
}
- addItem() {
+ handleClick() {
this.setState({
- itemCount: this.state.itemCount + 1
+ text: "You clicked!"
});
}
render() {
@@ -56,12 +64,11 @@ class MyComponent extends React.Component {
{ /* change code below this line */ }
{ /* change code above this line */ }
- 3px solid red
。您可以通过在输入中输入文本来尝试。 3px solid red
。你可以通过在 input 中输入文本来尝试。
+GateKeeper
组件应该呈现div
元素。
+ - text: GateKeeper
组件应该渲染一个de>div元素。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(GateKeeper)); return mockedComponent.find('div').length === 1; })());
- - text: 应使用设置为空字符串的状态键input
初始化GateKeeper
组件。
+ - text: GateKeeper
组件应使用设置为空字符串的 state input
进行初始化。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(GateKeeper)); return mockedComponent.state().input === ''; })());
- - text: GateKeeper
组件应呈现h3
标记和input
标记。
+ - text: GateKeeper
组件应该渲染一个h3
标签和一个input
标签。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(GateKeeper)); return mockedComponent.find('h3').length === 1 && mockedComponent.find('input').length === 1; })());
- - text: input
标记最初应为border
属性的1px solid black
样式。
+ - text: input
标签border
属性的样式应该初始化为1px solid black
。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(GateKeeper)); return mockedComponent.find('input').props().style.border === '1px solid black'; })());
- - text: 如果状态中的输入值超过15个字符,则input
标记应使用3px solid red
边框进行样式设置。
+ - text: 如果 state 中 input 的值超过 15 个字符,则 input
标签的 border 样式应为3px solid red
。
testString: 'async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 100)); const mockedComponent = Enzyme.mount(React.createElement(GateKeeper)); const simulateChange = (el, value) => el.simulate(''change'', {target: {value}}); let initialStyle = mockedComponent.find(''input'').props().style.border; const state_1 = () => { mockedComponent.setState({input: ''this is 15 char'' }); return waitForIt(() => mockedComponent.find(''input'').props().style.border )}; const state_2 = () => { mockedComponent.setState({input: ''A very long string longer than 15 characters.'' }); return waitForIt(() => mockedComponent.find(''input'').props().style.border )}; const style_1 = await state_1(); const style_2 = await state_2(); assert(initialStyle === ''1px solid black'' && style_1 === ''1px solid black'' && style_2 === ''3px solid red''); }; '
```
@@ -39,11 +44,12 @@ tests:
TypesOfFood
组件已经在呈现一个名为Vegetables
的组件。此外,还有最后一项挑战中的Fruits
成分。将两种成分NonCitrus
Fruits
- 首先是NonCitrus
,然后是Citrus
。这两个组件都是在后台为您提供的。接下来,将Fruits
类组件嵌入到TypesOfFood
组件中,位于h1
标题下方和Vegetables
上方。结果应该是一系列嵌套组件,它们使用两种不同的组件类型。 TypesOfFood
组件已经渲染了一个名为Vegetables
的组件。此外,还有上次挑战中的Fruits
组件。
+在Fruits
中嵌套两个组件,首先NonCitrus
,然后是Citrus
,这两个组件都是在后台为你提供的。接下来,将Fruits
类组件嵌到TypesOfFood
组件中,位于h1
标题下方和Vegetables
上方。结果应该是一系列嵌套的组件,它们使用两种不同的组件类型。
+TypesOfFood
组件应返回单个div
元素。
+ - text: TypesOfFood
组件应该返回单个div
元素。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood)); return mockedComponent.children().type() === 'div'; })());
- - text: TypesOfFood
组件应返回Fruits
组件。
+ - text: TypesOfFood
组件应该返回Fruits
组件。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood)); return mockedComponent.children().childAt(1).name() === 'Fruits'; })());
- - text: Fruits
组件应返回NonCitrus
组件和Citrus
组件。
+ - text: Fruits
组件应该返回NonCitrus
组件和Citrus
组件。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood)); return (mockedComponent.find('Fruits').children().find('NonCitrus').length === 1 && mockedComponent.find('Fruits').children().find('Citrus').length === 1); })());
- - text: TypesOfFood
组件应返回Fruits
组件下面的Vegetables
组件。
+ - text: TypesOfFood
组件应该返回Vegetables
组件,且其位于Fruits
组件之下。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood)); return mockedComponent.children().childAt(2).name() === 'Vegetables'; })());
```
@@ -69,7 +74,6 @@ class TypesOfFood extends React.Component {
);
}
};
-
```
@@ -122,7 +126,6 @@ class Vegetables extends React.Component {
);
}
};
-
```
@@ -131,7 +134,7 @@ class Vegetables extends React.Component {
<DIV>JSX无效:
<p>第一段</ p>
<p>第二段</ p>
<p>第3段</ p>
</ DIV>
<p>第一段</ p>
<p>第二段</ p>
<p>第3段</ p>
Paragraph One
+Paragraph Two
+Paragraph Three
+Paragraph One
+Paragraph Two
+Paragraph Three
+``` + +JSX
,它呈现一个按顺序包含以下元素的div
:一个h1
,一个p
和一个包含三个li
项的无序列表。您可以在每个元素中包含所需的任何文本。 注意:渲染多个这样的元素时,可以将它们全部括在括号中,但并不是严格要求的。另请注意,此挑战使用div
标记将所有子元素包装在单个父元素中。如果删除div
,JSX将不再转换。请记住这一点,因为当您在React组件中返回JSX元素时它也将适用。 JSX
,渲染一个div
,其中依次包含以下元素:
+一个h1
,一个p
,一个包含三个li
项的无序列表。你可以在每个元素中包含任何你想要的文本。
+注意: 当像这样渲染多个元素时,你可以把它们都用圆括号括起来,但是这并不是必须的。还请注意,此挑战使用div
标签把所有子元素包裹在里面。如果删除div
,JSX 将不会编译这些元素。请记住这一点,因为当你在 React 组件中返回 JSX 元素时它也适用。
+div
应该包含一个p
标签作为第二个元素。
testString: assert(JSX.props.children[0].type === 'h1');
- - text: div
应包含ul
标记作为第三个元素。
+ - text: div
应该包含一个ul
标签作为第三个元素。
testString: assert(JSX.props.children[1].type === 'p');
- - text: div
应包含一个h1
标记作为第一个元素。
+ - text: div
应该包含一个h1
标签作为第一个元素。
testString: assert(JSX.props.children[2].type === 'ul');
- text: ul
应该包含三个li
元素。
testString: assert(JSX.props.children.filter(ele => ele.type === 'ul')[0].props.children.filter(ele => ele.type === 'li').length === 3);
@@ -50,7 +78,7 @@ tests:
Some info
+Navbar
, Dashboard
和Footer
。要将这些组件组合在一起,您可以创建一个App
父组件,它将这三个组件中的每一个都呈现为子组件。要在React组件中将组件呈现为子组件,请在JSX中包含作为自定义HTML标记编写的组件名称。例如,在render
方法中,您可以编写: 回来(当React遇到引用另一个组件的自定义HTML标记(在此示例中包含在
<应用>
<Navbar />
<仪表板/>
<页脚/>
</应用>
)
< />
的组件名称)时,它会在标记的位置呈现该组件的标记。这应该说明App
组件与Navbar
, Dashboard
和Footer
之间的父/子关系。 Navbar
、Dashboard
和Footer
。
+要将这些组件组合在一起,你可以创建一个App
父组件,将这三个组件分别渲染成为子组件。要在 React 组件中渲染一个子组件,你需要在 JSX 中包含作为自定义 HTML 标签编写的组件名称。例如,在render
方法中,你可以这样编写:
+
+```jsx
+return (
+ < />
中),它在标签的位置渲染该组件的标签。这可以说明App
组件和Navbar
、Dashboard
以及Footer
之间的父子关系。
+ChildComponent
的简单功能组件和一个名为ParentComponent
的React组件。通过在ParentComponent
呈现ChildComponent
将两者组合在一起。确保使用正斜杠关闭ChildComponent
标记。 注意: ChildComponent
是使用ES6箭头函数定义的,因为这是使用React时非常常见的做法。但是,要知道这只是一个功能。如果您不熟悉箭头函数语法,请参阅JavaScript部分。 ChildComponent
的简单功能组件和一个名为ParentComponent
的 React 组件。通过在ParentComponent
中渲染ChildComponent
来将两者组合在一起。确保使用正斜杠关闭ChildComponent
标签。
+注意: ChildComponent
是使用 ES6 的箭头函数定义的,因为这是使用 React 时非常常见的做法。但是,要知道这只是一个函数。如果你不熟悉箭头函数语法,请参阅 JavaScript 部分。
+div
元素。
+ - text: React 组件应该返回单个div
元素。
testString: assert((function() { var shallowRender = Enzyme.shallow(React.createElement(ParentComponent)); return shallowRender.type() === 'div'; })());
- - text: 该组件应返回两个嵌套元素。
+ - text: 组件应该返回两个嵌套的元素。
testString: assert((function() { var shallowRender = Enzyme.shallow(React.createElement(ParentComponent)); return shallowRender.children().length === 2; })());
- - text: 该组件应将ChildComponent作为其第二个子项返回。
+ - text: 组件的第二个子元素应该是 ChildComponent。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ParentComponent)); return mockedComponent.find('ParentComponent').find('ChildComponent').length === 1; })());
```
@@ -59,7 +77,6 @@ class ParentComponent extends React.Component {
);
}
};
-
```
@@ -69,7 +86,7 @@ class ParentComponent extends React.Component {
I am the child
+input
和textarea
等某些元素的内部状态,这使得它们成为受控组件。这也适用于其他表单元素,包括常规HTML form
元素。 input
和textarea
,这使得这些元素成为受控组件。这也适用于其他表单元素,包括常规的 HTML 表单form
元素。
+MyForm
组件设置为带有提交处理程序的空form
。提交表单时将调用提交处理程序。我们添加了一个提交表单的按钮。您可以看到它的type
设置为submit
表明它是控制表单的按钮。在form
添加input
元素并设置其value
和onChange()
属性, onChange()
一个挑战。然后,您应该完成handleSubmit
方法,以便将组件状态属性submit
设置为本地state
的当前输入值。 注意:您还必须在提交处理程序中调用event.preventDefault()
,以防止将刷新网页的默认表单提交行为。最后,在form
之后创建一个h1
标记,该form
从组件的state
呈现submit
值。然后,您可以键入表单并单击按钮(或按Enter键),您应该看到您的输入呈现给页面。 MyForm
组件中是一个带有提交处理程序的空form
元素,提交处理程序将在提交表单时被调用。
+我们增加了一个提交表单的按钮。你可以看到它的type
被设置为submit
,表明它是控制表单的按钮。在表单中添加input
元素,并像上次挑战一样设置其value
和onChange()
属性。然后,你应该完成handleSubmit
方法,以便将组件 state 属性submit
设置为本地state
下的当前输入值。
+注意: 你还必须在提交处理程序中调用event.preventDefault()
,以防止默认的表单提交行为刷新网页。
+最后,在form
元素之后创建一个h1
标签,该标签从组件的state
渲染submit
的值。然后,你可以在表单中键入任何内容,然后单击按钮(或按 enter 键),你的输入会渲染到页面上。
+MyForm
应该返回一个包含form
和h1
标记的div
元素。表单应包含input
和button
。
+ - text: MyForm
应该返回一个包含form
和h1
标签的div
元素,其中,表单中应该包括一个input
和一个button
。
testString: assert((() => { const mockedComponent = Enzyme.mount(React.createElement(MyForm)); return (mockedComponent.find('div').children().find('form').length === 1 && mockedComponent.find('div').children().find('h1').length === 1 && mockedComponent.find('form').children().find('input').length === 1 && mockedComponent.find('form').children().find('button').length === 1) })());
- - text: MyForm
的状态应该使用input
和submit
属性初始化,两者都设置为空字符串。
+ - text: MyForm
的 state 应该用input
和submit
属性初始化,且两者都为空字符串。
testString: assert(Enzyme.mount(React.createElement(MyForm)).state('input') === '' && Enzyme.mount(React.createElement(MyForm)).state('submit') === '');
- - text: 输入input
元素应该更新组件状态的input
属性。
+ - text: input
元素中的输入应该会更新组件中 state 的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: '''' }); 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''); }; '
- - text: 提交表单应该运行handleSubmit
,它应该将submit
属性设置为等于当前输入的状态。
+ - text: 提交表单应该运行handleSubmit
,它应该将 state 中的submit
属性设置为当前输入。
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''); };'
- - text: h1
标头应该从组件的状态呈现submit
字段的值。
+ - text: h1
标题应该从组件的 state 渲染submit
字段的值。
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''); }; '
```
@@ -43,8 +50,8 @@ class MyForm extends React.Component {
constructor(props) {
super(props);
this.state = {
- input: ",
- submit: "
+ input: '',
+ submit: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
@@ -75,7 +82,6 @@ class MyForm extends React.Component {
);
}
};
-
```
@@ -85,7 +91,7 @@ class MyForm extends React.Component {
state
和呈现的UI之间进行更复杂的交互。例如,用于文本输入的表单控件元素(例如input
和textarea
在用户键入时在DOM中维护它们自己的状态。使用React,您可以将此可变状态移动到React组件的state
。用户的输入成为应用程序state
一部分,因此React控制该输入字段的值。通常,如果React组件具有用户可以键入的输入字段,则它将是受控输入表单。 state
和渲染的 UI 之间有更复杂的交互。例如,用于文本输入的表单控件元素(如input
和textarea
)在用户键入时在 DOM 中维护自己的 state。通过 React,你可以将这种可变 state 转移到 React 组件的state
中。用户的输入变成了应用程序state
的一部分,因此 React 控制该输入字段的值。通常,如果你的 React 组件具有用户可以键入的输入字段,那么它将是一个受控的输入表单。
+ControlledInput
的组件的骨架,以创建受控input
元素。组件的state
已经使用包含空字符串的input
属性进行初始化。此值表示用户在input
字段中键入的文本。首先,创建一个名为handleChange()
的方法,该方法具有一个名为event
的参数。调用该方法时,它会接收一个event
对象,该对象包含input
元素中的一串文本。您可以使用方法内的event.target.value
访问此字符串。使用此新字符串更新组件state
的input
属性。在render方法中,在h4
标记上方创建input
元素。添加一个value
属性,该属性等于组件state
的input
属性。然后将onChange()
事件处理程序集添加到handleChange()
方法。当您在输入框中键入时,该文本由handleChange()
方法处理,设置为本地state
的input
属性,并在页面的input
框中呈现为值。组件state
是关于输入数据的单一事实来源。最后但并非最不重要的是,不要忘记在构造函数中添加必要的绑定。 ControlledInput
的组件框架,用于创建受控的input
元素。组件的state
已经被包含空字符串的input
属性初始化。此值表示用户在input
字段中键入的文本。
+首先,创建一个名为handleChange()
的方法,该方法具有一个名为event
的参数。方法被调用时,它接收一个event
对象,该对象包含一个来自input
元素的字符串文本。你可以使用方法内的event.target.value
来访问这个字符串。用这个新字符串更新组件的state
的input
属性。
+在 render 方法中,在h4
标签之上创建input
元素。添加一个value
属性,它等于组件的state
的input
属性。然后将onChange()
事件处理程序设置到handleChange()
方法。
+在输入框中键入时,该文本由handleChange()
方法处理,该文本被设置为本地state
中的input
属性,并渲染在页面上的input
框中。组件state
是输入数据的唯一真实来源。
+最后也是最重要的,不要忘记在构造函数中添加必要的绑定。
+ControlledInput
应返回包含input
和p
标记的div
元素。
+ - text: ControlledInput
应该返回包含一个input
标签和p
标签的div
元素。
testString: assert(Enzyme.mount(React.createElement(ControlledInput)).find('div').children().find('input').length === 1 && Enzyme.mount(React.createElement(ControlledInput)).find('div').children().find('p').length === 1);
- - text: ControlledInput
的状态应该初始化, input
属性设置为空字符串。
+ - text: ControlledInput
的 state 应该使用设置为空字符串的input
属性初始化。
testString: assert.strictEqual(Enzyme.mount(React.createElement(ControlledInput)).state('input'), '');
- - text: 输入input元素应更新输入的状态和值, p
元素应在键入时呈现此状态。
+ - text: input 元素中的键入值应该更新 input 的 state 和值,并且p
元素应该在输入时呈现 state。
testString: 'async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(ControlledInput)); const _1 = () => { mockedComponent.setState({ input: '''' }); return waitForIt(() => mockedComponent.state(''input''))}; const _2 = () => { mockedComponent.find(''input'').simulate(''change'', { target: { value: ''TestInput'' }}); return waitForIt(() => ({ state: mockedComponent.state(''input''), text: mockedComponent.find(''p'').text(), inputVal: mockedComponent.find(''input'').props().value }))}; const before = await _1(); const after = await _2(); assert(before === '''' && after.state === ''TestInput'' && after.text === ''TestInput'' && after.inputVal === ''TestInput''); }; '
```
@@ -39,7 +47,7 @@ class ControlledInput extends React.Component {
constructor(props) {
super(props);
this.state = {
- input: "
+ input: ''
};
// change code below this line
@@ -60,7 +68,6 @@ class ControlledInput extends React.Component {
);
}
};
-
```
@@ -70,7 +77,7 @@ class ControlledInput extends React.Component {
{this.state.input}
+class
语法。在以下示例中, Kitten
扩展了React.Component
: class Kitten扩展了React.Component {这将创建一个扩展
构造函数(道具){
超级(道具);
}
render(){
回来(
<H1>,您好</ H1>
);
}
}
React.Component
类的ES6类Kitten
。因此, Kitten
类现在可以访问许多有用的React功能,例如本地状态和生命周期钩子。如果您还不熟悉这些术语,请不要担心,在以后的挑战中将更详细地介绍它们。另请注意, Kitten
类在其中定义了一个调用super()
的constructor
函数。它使用super()
来调用父类的构造函数,在本例中为React.Component
。构造函数是在使用class
关键字创建的对象初始化期间使用的特殊方法。最好用super
调用组件的constructor
,并将props
传递给它们。这可确保组件正确初始化。现在,请知道包含此代码是标准的。很快你会看到构造函数和props
其他用途。 class
语法。在以下示例中,Kitten
扩展了React.Component
:
+
+```jsx
+class Kitten extends React.Component {
+ constructor(props) {
+ super(props);
+ }
+
+ render() {
+ return (
+ Kitten
,它扩展了React.Component
类。因此,Kitten
类现在可以访问许多有用的 React 功能,例如本地状态和生命周期钩子。如果你还不熟悉这些术语,请不要担心,在以后的挑战中我们将更详细地介绍它们。
+另请注意,Kitten
类中定义了一个调用super()
方法的constructor
。它使用super()
调用父类的构造函数,即本例中的React.Component
。构造函数是使用class
关键字创建的特殊方法,它用在实例初始化之前。最佳做法是在组件的constructor
里调用super
,并将props
传递给它们,这样可以保证组件能够正确地初始化。现在,你只需要知道这是标准的做法。很快你会看到构造函数的其他用途以及props
。
+MyComponent
是使用类语法在代码编辑器中定义的。完成编写render
方法,以便返回包含带有文本Hello React!
的h1
的div
元素Hello React!
。 MyComponent
是使用类语法在代码编辑器中定义的。完成render
方法的编写,使其返回div
元素,其中包含文本内容为Hello React!
的h1
元素。
+div
元素。
+ - text: 该 React 组件应该返回一个div
元素。
testString: assert(Enzyme.shallow(React.createElement(MyComponent)).type() === 'div');
- - text: 返回的div
应该在其中呈现一个h1
头。
+ - text: 返回的div
中应该渲染一个h1
标题。
testString: assert(/h1
标头应该包含字符串Hello React!
。
+ - text: h1
标题中应该包含字符串Hello React!
。
testString: assert(Enzyme.shallow(React.createElement(MyComponent)).html() === '{ 'this is treated as JavaScript code' }
: { 'this is treated as JavaScript code' }
。记住这一点,因为它用于未来的几个挑战。但是,由于JSX不是有效的JavaScript,因此必须将JSX代码编译为JavaScript。转换器Babel是这个过程的流行工具。为了您的方便,它已经在幕后为这些挑战添加。如果您碰巧编写语法无效的JSX,您将看到这些挑战中的第一个测试失败。值得注意的是,在引擎盖下,挑战是调用ReactDOM.render(JSX, document.getElementById('root'))
。这个函数调用是将JSX置于React自己的DOM轻量级表示中的原因。然后,React使用自己的DOM快照来优化仅更新实际DOM的特定部分。 {“这被视为 JavaScript 代码”}
。请牢记这个写法,你将会在接下来的挑战中使用。
+但是,由于浏览器不能解析 JSX,因此必须将 JSX 代码编译为 JavaScript。在这个过程中,转换器 Babel 是一个很受欢迎的工具。后续挑战已经在后台引入了 Babel,你可以直接写 JSX 代码。如果你的代码不符合 JSX 语法,那么挑战中的第一个测试就不会通过。
+值得注意的是,这些挑战在底层调用ReactDOM.render(JSX, document.getElementById('root'))
。这个函数调用是将你的 JSX 置于 React 自己的轻量级 DOM 中。然后,React 使用自己的 DOM 快照来优化更新实际 DOM 的特定部分。
+div
元素分配给常量JSX
。用h1
元素替换div
并添加文本Hello JSX!
在里面。 div
元素赋值给常量JSX
。将div
替换为h1
元素,并在其中添加文本Hello JSX!
。
+JSX
应该返回一个h1
元素。
testString: assert(JSX.type === 'h1');
- - text: h1
标签应该包含文本Hello JSX!
+ - text: h1
标签应该包含文本Hello JSX!
。
testString: assert(Enzyme.shallow(JSX).contains('Hello JSX!'));
```
@@ -33,6 +41,7 @@ tests:
state
。 State包含应用程序需要了解的任何数据,这些数据可能会随时间而变化。您希望应用程序响应状态更改并在必要时显示更新的UI。 React为现代Web应用程序的状态管理提供了一个很好的解决方案。您可以通过在constructor
声明组件类的state
属性来在React组件中创建状态。这与初始化该组件state
被创建时。 state
属性必须设置为JavaScript object
。声明它看起来像这样: this.state = {
//在这里描述你的州
您可以在组件的整个生命周期内访问state
对象。您可以更新它,在UI中呈现它,并将其作为道具传递给子组件。state
对象可以像您需要的那样复杂或简单。请注意,您必须通过扩展React.Component
来创建类组件,以便创建这样的state
。
state
。 state 包含应用程序需要了解的任何数据,这些数据可能会随时间而变化。你希望应用程序能够响应 state 的变更,并在必要时显示更新后的 UI。React 为现代 Web 应用程序的状态管理提供了一个很好的解决方案。
+你可以通过在constructor
中的组件类上声明state
属性来在 React 组件中创建 state,它在创建时使用state
初始化组件。state
属性必须设置为 JavaScript对象
。声明如下:
+
+```jsx
+this.state = {
+ // describe your state here
+}
+```
+
+你可以在组件的整个生命周期内访问state
对象,你可以更新它、在 UI 中渲染它,也可以将其作为 props 传递给子组件。state
对象的使用可以很简单,亦可以很复杂,就看你怎么用了。请注意,你必须通过扩展React.Component
来创建类组件,以便像这样创建state
。
+state
呈现name
属性。但是,没有定义state
。初始化与组件state
的constructor
,并指定你的名字的属性name
。 state
中渲染一个name
属性,但是state
还没有定义。在constructor
中使用state
初始化组件,并将你的名字赋给name
属性。
+StatefulComponent
应该存在并呈现。
+ - text: StatefulComponent
应该存在并被渲染。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(StatefulComponent)); return mockedComponent.find('StatefulComponent').length === 1; })());
- - text: StatefulComponent
应该呈现div
和h1
元素。
+ - text: StatefulComponent
应该渲染一个div
元素和一个h1
元素。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(StatefulComponent)); return mockedComponent.find('div').length === 1 && mockedComponent.find('h1').length === 1; })());
- - text: 应使用设置为字符串的属性name
初始化StatefulComponent
。
+ - text: 应使用被设置为字符串的name
属性来初始化StatefulComponent
的 state。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(StatefulComponent)); const initialState = mockedComponent.state(); return ( typeof initialState === 'object' && typeof initialState.name === 'string'); })());
- - text: StatefulComponent
的属性name
应在h1
元素中呈现。
+ - text: StatefulComponent
中 state 的name
属性应该渲染在h1
元素里。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(StatefulComponent)); const initialState = mockedComponent.state(); return mockedComponent.find('h1').text() === initialState.name; })());
```
@@ -37,6 +50,7 @@ tests:
null
的JavaScript函数。需要注意的一件重要事情是,React要求您的函数名称以大写字母开头。这是一个在JSX中分配HTML类的无状态功能组件的示例: //被转换后,<div>将有一个CSS类'customClass'因为JSX组件代表HTML,所以您可以将几个组件放在一起以创建更复杂的HTML页面。这是React提供的组件架构的关键优势之一。它允许您从许多独立的,独立的组件中组合UI。这使得构建和维护复杂的用户界面变得更加容易。
const DemoComponent = function(){
回来(
<div className ='customClass'/>
);
};
null
的 JavaScript 函数。需要注意的一点是,React 要求你的函数名以大写字母开头。下面是一个无状态功能组件的示例,该组件在 JSX 中分配一个 HTML 的 class:
+
+```jsx
+// After being transpiled, the MyComponent
的函数。完成此函数,以便返回包含一些文本字符串的单个div
元素。 注意:该文本被视为div
元素的子元素,因此您将无法使用自闭合标记。 MyComponent
的函数。完成此函数,使其返回包含一些文本字符串的单个div
元素。
+注意: 文本被视为是div
的子元素,因此你将不能使用自闭合标签。
+MyComponent
应该返回JSX。
+ - text: MyComponent
应该返回 JSX。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return mockedComponent.length === 1; })());
- text: MyComponent
应该返回一个div
元素。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return mockedComponent.children().type() === 'div' })());
- - text: div
元素应包含一串文本。
+ - text: div
元素应该包含一个文本字符串。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return mockedComponent.find('div').text() !== ''; })());
```
@@ -42,7 +60,6 @@ const MyComponent = function() {
// change code above this line
}
-
```
class
来定义HTML类。这是因为class
是JavaScript中的保留字。相反,JSX使用className
。事实上,JSX中所有HTML属性和事件引用的命名约定都变成了camelCase。例如,JSX中的单击事件是onClick
,而不是onclick
。同样, onchange
变为onChange
。虽然这是一个微妙的差异,但重要的是要记住前进。 class
这个单词来定义 HTML 的 class 名。这是因为class
是 JavaScript 中的关键字。JSX 使用className
代替。
+事实上,JSX 中所有 HTML 属性和事件引用的命名约定都变成了驼峰式。例如,JSX 中的单击事件是 onClick
,而不是 onclick
。同样,onchange
变成了onChange
。虽然这是一个微妙的差异,但请你一定要记住。
+myDiv
类myDiv
JSX代码中提供的div
。 myDiv
应用于 JSX 提供的div
上。
+JSX
应该返回一个div
元素。
testString: assert.strictEqual(JSX.type, 'div');
- - text: div
有一类myDiv
。
+ - text: div
有一个myDiv
class。
testString: assert.strictEqual(JSX.props.className, 'myDiv');
```
@@ -38,7 +45,6 @@ const JSX = (
map
方法根据用户输入动态呈现多个元素。但是,这个例子中缺少一个重要的部分。创建元素数组时,每个元素都需要将key
属性设置为唯一值。 React使用这些键来跟踪添加,更改或删除的项目。当以任何方式修改列表时,这有助于使重新呈现过程更有效。请注意,键只需要在兄弟元素之间是唯一的,它们在您的应用程序中不需要是全局唯一的。 map
方法根据用户输入动态渲染多个元素。然而,这个例子中缺少一个重要的部分。创建元素数组时,每个元素都需要一个设置为唯一值的key
属性。React 使用这些键来跟踪哪些项目被添加、更改或删除。这有助于在以任何方式修改列表时提高重新渲染过程的效率。请注意,键只需要在同级元素之间是唯一的,它们不需要在应用程序中是全局唯一的。
+Frameworks()
的无状态功能组件。 Frameworks()
需要将数组映射到无序列表,就像上一次挑战一样。完成编写map
回调以返回frontEndFrameworks
数组中每个框架的li
元素。这一次,请确保为每个li
一个key
属性,设置为唯一值。通常,您希望使键成为唯一标识要呈现的元素的键。作为最后的手段,可以使用数组索引,但通常您应该尝试使用唯一标识。 Frameworks()
的无状态函数组件。Frameworks()
需要将数组映射到无序列表,就像上一个挑战一样。完成map
回调,为frontEndFrameworks
数组中的每个框架返回一个li
元素。这次,确保给每个li
的key
属性设置一个唯一的值。
+通常,你希望使 key 能唯一标识要渲染的元素。作为最后的手段,可以使用数组索引,但通常你应该尝试使用唯一标识。
+Frameworks
组件应该存在并呈现给页面。
- testString: 'assert(Enzyme.mount(React.createElement(Frameworks)).find("Frameworks").length === 1, "The Frameworks
component should exist and render to the page.");'
- - text: Frameworks
应该呈现h1
元素。
- testString: 'assert(Enzyme.mount(React.createElement(Frameworks)).find("h1").length === 1, "Frameworks
should render an h1
element.");'
- - text: Frameworks
应该呈现ul
元素。
- testString: 'assert(Enzyme.mount(React.createElement(Frameworks)).find("ul").length === 1, "Frameworks
should render a ul
element.");'
- - text: ul
标记应呈现6个子li
元素。
- testString: 'assert(Enzyme.mount(React.createElement(Frameworks)).find("ul").children().length === 6 && Enzyme.mount(React.createElement(Frameworks)).find("ul").childAt(0).name() === "li" && Enzyme.mount(React.createElement(Frameworks)).find("li").length === 6, "The ul
tag should render 6 child li
elements.");'
- - text: 每个列表项元素都应具有唯一的key
属性。
- testString: 'assert((() => { const ul = Enzyme.mount(React.createElement(Frameworks)).find("ul"); const keys = new Set([ ul.childAt(0).key(), ul.childAt(1).key(), ul.childAt(2).key(), ul.childAt(3).key(), ul.childAt(4).key(), ul.childAt(5).key(), ]); return keys.size === 6; })(), "Each list item element should have a unique key
attribute.");'
+ - text: Frameworks
组件应该存在并渲染到页面。
+ testString: assert(Enzyme.mount(React.createElement(Frameworks)).find('Frameworks').length === 1);
+ - text: Frameworks
应该渲染一个h1
元素。
+ testString: assert(Enzyme.mount(React.createElement(Frameworks)).find('h1').length === 1);
+ - text: Frameworks
应该渲染一个ul
元素。
+ testString: assert(Enzyme.mount(React.createElement(Frameworks)).find('ul').length === 1);
+ - text: ul
标签应该渲染 6 个li
子元素。
+ testString: assert(Enzyme.mount(React.createElement(Frameworks)).find('ul').children().length === 6 && Enzyme.mount(React.createElement(Frameworks)).find('ul').childAt(0).name() === 'li' && Enzyme.mount(React.createElement(Frameworks)).find('li').length === 6);
+ - text: 每个列表项元素都应该有一个唯一的key
属性。
+ testString: assert((() => { const ul = Enzyme.mount(React.createElement(Frameworks)).find('ul'); const keys = new Set([ ul.childAt(0).key(), ul.childAt(1).key(), ul.childAt(2).key(), ul.childAt(3).key(), ul.childAt(4).key(), ul.childAt(5).key(), ]); return keys.size === 6; })());
+ - text: 每个列表元素都应该包含 frontEndFrameworks
里的文本。
+ testString: assert((() => {const li = Enzyme.mount(React.createElement(Frameworks)).find('ul').children(); return [...Array(5)].every((_, i) => frontEndFrameworks.includes(li.at(i).text()))})());
```
@@ -39,6 +46,7 @@ tests:
className
属性将类应用于JSX元素,并将样式应用于样式表中的类。另一种选择是应用内联样式,这在ReactJS开发中非常常见。您将内联样式应用于JSX元素,类似于您在HTML中的操作方式,但有一些JSX差异。以下是HTML中内联样式的示例: <div style="color: yellow; font-size: 16px">Mellow Yellow</div>
JSX元素使用style
属性,但由于JSX的转换方式,您可以不要将值设置为string
。相反,您将其设置为等于JavaScript object
。这是一个例子: <div style={{color: "yellow", fontSize: 16}}>Mellow Yellow</div>
注意我们如何使用“fontSize”属性?这是因为React不接受样式对象中的kebab-case键。 React将在HTML中为我们应用正确的属性名称。 className
属性将 class 应用于 JSX 元素,并将样式应用于样式表中的 class。另一种选择是使用内联样式,这在 ReactJS 开发中非常常见。
+你将内联样式应用于 JSX 元素,类似于你在 HTML 中的操作方式,但有一些 JSX 差异。以下是 HTML 中内联样式的示例:
+<div style="color: yellow; font-size: 16px">Mellow Yellow</div>
+JSX 元素使用style
属性,但是由于 JSX 的传输方式,你不能将值设置为字符串
。相反,你应将其设置为 JavaScript对象
。这里有一个例子:
+<div style={{color: "yellow", fontSize: 16}}>Mellow Yellow</div>
+注意我们使用驼峰式命名的 "fontSize" 属性,这是因为 React 不会接受样式对象中的连字符。React 将在 HTML 中为我们应用正确的属性名称。
+div
添加style
属性,为文本提供红色和字体大小为72px的颜色。请注意,您可以选择将字体大小设置为数字,省略单位“px”,或将其写为“72px”。 div
中添加一个style
属性,使文本颜色为红色,字体大小为 72px。
+请注意,你可以选择将字体大小设置为数字,省略单位 "px",或者将其写为 "72px"。
+div
元素。
+ - text: 组件应该渲染一个div
元素。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(Colorful)); return mockedComponent.children().type() === 'div'; })());
- - text: div
元素应该是red
。
+ - text: div
元素应该是红色
的。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(Colorful)); return mockedComponent.children().props().style.color === 'red'; })());
- text: div
元素的字体大小应为72px
。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(Colorful)); return (mockedComponent.children().props().style.fontSize === 72 || mockedComponent.children().props().style.fontSize === '72' || mockedComponent.children().props().style.fontSize === '72px'); })());
@@ -35,6 +46,7 @@ tests:
className
与class
来定义HTML类。 JSX与HTML的另一个重要方式是自闭标签。在HTML中,几乎所有标签都有开始和结束标签: <div></div>
;结束标记在您要关闭的标记名称之前始终具有正斜杠。但是,HTML中有一些称为“自闭标签”的特殊实例,或者在另一个标签可以启动之前不需要开始和结束标签的标签。例如,换行标记可以写成<br>
或<br />
,但不应该写为<br></br>
,因为它不包含任何内容。在JSX中,规则略有不同。任何JSX元素都可以使用自闭合标记编写,并且必须关闭每个元素。例如,换行标记必须始终写为<br />
才能成为可以转换的有效JSX。另一方面, <div>
可以写为<div />
或<div></div>
。不同之处在于,在第一个语法版本中,无法在<div />
包含任何内容。您将在以后的挑战中看到,在呈现React组件时,此语法非常有用。 className
和使用class
来定义 HTML 的 class。
+JSX 不同于 HTML 的另一个重要方面是自闭合标签。
+在HTML中,几乎所有的标签都有一个开始和结束标签:<div></div>
,结束标签在你要关闭的标签名之前始终具有正斜杠。但是,HTML 中有一些称为“自闭合标签”的特殊实例,它们在另一个标签开始之前,不需要开始和结束标签都存在。
+例如,换行标签可以写成<br>
或者<br />
,但是不应该写成<br></br>
,因为它不包含任何内容。
+在 JSX 中,规则略有不同。任何 JSX 元素都可以使用自闭合标签编写,并且每个元素都必须关闭。例如,换行标签必须始终编写为<br />
。另一方面<div>
可以写成<div />
或者<div></div>
。不同之处在于,在第一个语法版本中,无法在<div />
中包含任何内容。在后面的挑战中你会发现,这种语法在渲染 React 组件时非常有用。
+div
应该包含一个br
标签。
testString: assert(Enzyme.shallow(JSX).find('br').length === 1);
- - text: div
应包含hr
标记。
+ - text: div
应该包含一个hr
标签。
testString: assert(Enzyme.shallow(JSX).find('hr').length === 1);
```
@@ -54,7 +62,7 @@ const JSX = (
Be sure to close all tags!
+state
或新的props
,它会重新呈现自己及其所有子项。这通常没问题。但是React提供了一个生命周期方法,您可以在子组件接收新state
或props
时调用它,并特别声明组件是否应该更新。方法是shouldComponentUpdate()
,它将nextProps
和nextState
作为参数。此方法是优化性能的有用方法。例如,默认行为是您的组件在收到新props
时重新渲染,即使props
未更改。您可以使用shouldComponentUpdate()
通过比较props
来防止这种情况。该方法必须返回一个boolean
值,告诉React是否更新组件。您可以将当前道具( this.props
)与下一个道具( nextProps
)进行比较,以确定是否需要更新,并相应地返回true
或false
。 state
或新的props
,它会重新渲染自己及其所有子组件。这通常是好的。但是 React 提供了一种生命周期方法,当子组件接收到新的state
或props
时,你可以调用该方法,并特别声明组件是否应该更新。方法是shouldComponentUpdate()
,它将nextProps
和nextState
作为参数。
+这种方法是优化性能的有效方法。例如,默认行为是,当组件接收到新的props
时,即使props
没有改变,它也会重新渲染。你可以通过使用shouldComponentUpdate()
比较props
来防止这种情况。该方法必须返回一个布尔值,该值告诉 React 是否更新组件。你可以比较当前的 props(this.props
)和下一个 props(nextProps
),以确定你是否需要更新,并相应地返回true
或false
。
+shouldComponentUpdate()
方法添加到名为OnlyEvens
的组件中。目前,此方法返回true
因此每次收到新props
时, OnlyEvens
重新渲染。修改方法,以便OnlyEvens
更新仅当value
的新道具的是偶数。单击“ Add
按钮,在触发其他生命周期挂钩时,在浏览器控制台中查看事件的顺序。 shouldComponentUpdate()
方法添加到名为OnlyEvens
的组件中。目前,该方法返回true
,因此每次收到新的props
时,OnlyEvens
都会重新渲染。修改该方法,以便OnlyEvens
仅在其新 props 的value
为偶数时更新。单击Add
按钮,在触发其他生命周期钩子时,在浏览器控制台中查看事件的顺序。
+Controller
组件应将OnlyEvens
组件呈现为OnlyEvens
组件。
+ - text: Controller
组件应该将OnlyEvens
组件渲染为子组件。
testString: assert((() => { const mockedComponent = Enzyme.mount(React.createElement(Controller)); return mockedComponent.find('Controller').length === 1 && mockedComponent.find('OnlyEvens').length === 1; })());
- text: 应该在OnlyEvens
组件上定义shouldComponentUpdate
方法。
testString: assert((() => { const child = React.createElement(OnlyEvens).type.prototype.shouldComponentUpdate.toString().replace(/s/g,''); return child !== 'undefined'; })());
- - text: OnlyEvens
组件应返回一个h1
标记,该标记呈现this.props.value
的值。
+ - text: OnlyEvens
组件应该返回一个h1
标签,该标签渲染this.props.value
的值。
testString: 'async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(Controller)); const first = () => { mockedComponent.setState({ value: 1000 }); return waitForIt(() => mockedComponent.find(''h1'').html()); }; const second = () => { mockedComponent.setState({ value: 10 }); return waitForIt(() => mockedComponent.find(''h1'').html()); }; const firstValue = await first(); const secondValue = await second(); assert(firstValue === ''OnlyEvens
只有在nextProps.value
为偶数OnlyEvens
应该重新渲染。
+ - text: 只有在nextProps.value
为偶数时,OnlyEvens
才会重新渲染。
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''); }; '
```
@@ -47,9 +52,6 @@ class OnlyEvens extends React.Component {
return true;
// change code above this line
}
- componentWillReceiveProps(nextProps) {
- console.log('Receiving new props...');
- }
componentDidUpdate() {
console.log('Component re-rendered.');
}
@@ -80,7 +82,6 @@ class Controller extends React.Component {
);
}
};
-
```
ShoppingCart
组件现在呈现子组件Items
。此Items
组件的默认prop quantity
设置为整数0
。通过为quantity
传递值10
来覆盖默认支柱。 注意:请记住,向组件添加prop的语法与添加HTML属性的方式类似。但是,由于quantity
的值是一个整数,因此它不会引用引号,但应该用大括号括起来。例如, {100}
。此语法告诉JSX将大括号内的值直接解释为JavaScript。 ShoppingCart
组件现在渲染了一个子组件Items
。该Items
组件有一个默认quantity
prop,其值被设置为整数0
。通过传入数值10
来覆盖quantity
的默认 prop。
+注意: 请记住,向组件添加 prop 的语法与添加 HTML 属性类似。但是,由于quantity
的值是整数,所以它不会加引号,但应该用花括号括起来,例如{100}
。这个语法告诉 JSX 直接将花括号中的值解释为 JavaScript。
+ShoppingCart
应该呈现组件。
+ - text: 应该渲染ShoppingCart
组件。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart)); return mockedComponent.find('ShoppingCart').length === 1; })());
- - text: 该组件Items
应该呈现。
+ - text: 应该渲染Items
组件。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart)); return mockedComponent.find('Items').length === 1; })());
- - text: 'Items
组件应具有从ShoppingCart
组件传递的{ quantity: 10 }
的prop。'
+ - text: 'Items
组件应该有一个{ quantity: 10 }
的prop,该 prop 是从ShoppingCart
组件传递过去的。'
testString: "getUserInput => assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ShoppingCart)); return mockedComponent.find('Items').props().quantity == 10 && getUserInput('index').replace(/ /g,'').includes('state
作为道具传递给子组件,但您不仅限于传递数据。您还可以将处理函数或在React组件上定义的任何方法传递给子组件。这是允许子组件与其父组件交互的方式。您可以将方法传递给孩子,就像常规道具一样。它被分配了一个名称,您可以在子组件中的this.props
下访问该方法名称。 state
作为 props 传递给子组件,但不仅限于传递数据。你也可以将处理函数或在 React 组件中定义的任何方法传递给子组件。这就是允许子组件与父组件交互的方式。你可以把方法像普通 prop 一样传递给子组件,它会被分配一个名字,你可以在子组件中的this.props
下访问该方法的名字。
+MyApp
组件是将呈现GetInput
和RenderInput
子组件的父组件。将GetInput
组件添加到MyApp
的render方法,然后从MyApp
的state
向它传递一个名为input
的prop,该input
分配给inputValue
。还要创建一个名为handleChange
的prop,并将输入处理程序handleChange
给它。接下来,将RenderInput
添加到MyApp
的render方法,然后创建一个名为input
的prop,并将inputValue
从state
传递给它。完成后,您将能够在GetInput
组件中input
字段,然后通过props调用其父级中的处理程序方法。这将更新父级state
的输入,该输入作为props传递给两个子级。观察数据如何在组件之间流动以及单个事实源如何保持父组件的state
。不可否认,这个例子有点人为,但应该用来说明如何在React组件之间传递数据和回调。 MyApp
是父组件,GetInput
和RenderInput
是它的子组件。将GetInput
组件添加到MyApp
的 render 方法,然后将MyApp
的state
中的inputValue
传入名为input
的 prop。还要创建一个名为handleChange
的 prop,并将输入处理程序handleChange
传递给它。
+接下来,将RenderInput
添加到MyApp
中的 render 方法中,然后创建一个名为input
的 prop,并将state
中的inputValue
传递给它。完成后,你将能够在GetInput
组件中的input
字段中键入内容,然后该组件通过 props 调用其父组件中的处理函数方法。这将更新处于父组件state
中的 input,该 input 将作为 props 传递给两个子组件。观察数据如何在组件之间流动,以及单一数据源如何保持父组件state
。诚然,这个示例有点做作,但是应该能用来说明数据和回调是如何在 React 组件之间传递的。
+MyApp
组件应该呈现。
+ - text: 应该渲染MyApp
组件。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyApp)); return mockedComponent.find('MyApp').length === 1; })());
- - text: GetInput
组件应该呈现。
+ - text: 应该渲染GetInput
组件。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyApp)); return mockedComponent.find('GetInput').length === 1; })());
- - text: RenderInput
组件应该呈现。
+ - text: 应该渲染RenderInput
组件。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyApp)); return mockedComponent.find('RenderInput').length === 1; })());
- - text: GetInput
组件应该将MyApp
状态属性inputValue
作为props接收,并包含一个修改MyApp
状态的input
元素。
+ - text: GetInput
组件应该接收MyApp
的 state 属性inputValue
作为 props,并包含一个修改MyApp
state 的input
元素。
testString: 'async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(MyApp)); const state_1 = () => { mockedComponent.setState({inputValue: ''''}); return waitForIt(() => mockedComponent.state() )}; const state_2 = () => { mockedComponent.find(''input'').simulate(''change'', {target: {value: ''TestInput''}}); return waitForIt(() => mockedComponent.state() )}; const updated_1 = await state_1(); const updated_2 = await state_2(); assert(updated_1.inputValue === '''' && updated_2.inputValue === ''TestInput''); }; '
- - text: RenderInput
组件应该将MyApp
状态属性inputValue
作为props接收。
+ - text: RenderInput
组件应该接收MyApp
state 属性inputValue
作为 props。
testString: 'async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(MyApp)); const state_1 = () => { mockedComponent.setState({inputValue: ''TestName''}); return waitForIt(() => mockedComponent )}; const updated_1 = await state_1(); assert(updated_1.find(''p'').text().includes(''TestName'')); }; '
```
@@ -43,7 +48,7 @@ class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {
- inputValue: "
+ inputValue: ''
}
this.handleChange = this.handleChange.bind(this);
}
@@ -92,7 +97,6 @@ class RenderInput extends React.Component {
);
}
};
-
```
@@ -102,7 +106,7 @@ class RenderInput extends React.Component {
{this.props.input}
+props
或属性。这个挑战着眼于如何将数组作为props
传递。要将数组传递给JSX元素,必须将其视为JavaScript并用大括号括起来。 <为父级>然后子组件可以访问数组属性
<ChildComponent colors = {[“green”,“blue”,“red”]} />
</为父级>
colors
。访问属性时可以使用诸如join()
类的数组方法。 const ChildComponent = (props) => <p>{props.colors.join(', ')}</p>
这会将所有colors
数组项连接成逗号分隔的字符串并生成: <p>green, blue, red</p>
稍后,我们将了解在React中呈现数据数组的其他常用方法。 props
传递给子组件。这个挑战着眼于如何将数组作为props
传递。要将数组传递给 JSX 元素,必须将其视为 JavaScript 并用花括号括起来。
+
+```jsx
+colors
。访问属性时可以使用join()
等数组方法。
+const ChildComponent = (props) => <p>{props.colors.join(', ')}</p>
+这将把所有colors
数组项连接成一个逗号分隔的字符串并生成:
+ <p>green, blue, red</p>
+稍后,我们将了解在 React 中渲染数组数据的其他常用方法。
+List
和ToDo
组件。从ToDo
组件渲染每个List
,传入分配给待办任务数组的tasks
属性,例如["walk dog", "workout"]
。然后在List
组件中访问此tasks
数组,在p
元素中显示其值。使用join(", ")
以逗号分隔列表的形式显示p
元素中的props.tasks
数组。今天的列表应该至少有2个任务,明天应该至少有3个任务。 List
和ToDo
组件。在ToDo
组件中渲染每个List
时,传入tasks
属性并将其分配给待办任务数组,例如["walk dog", "workout"]
。然后访问List
组件中的tasks
数组,在p
元素中显示其值。使用join(", ")
把props.tasks
数组作为逗号分隔列表显示在p
元素中。今天的列表应该至少有 2 个任务,明天应该至少有 3 个任务。
+ToDo
组件应返回单个外部div
。
+ - text: ToDo
组件应该返回单个外部div
。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ToDo)); return mockedComponent.children().first().type() === 'div'; })());
- - text: ToDo
组件的第三个子ToDo
应该是List
组件的实例。
+ - text: ToDo
组件的第三个子元素应该是List
组件的一个实例。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ToDo)); return mockedComponent.children().first().childAt(2).name() === 'List'; })());
- - text: ToDo
组件的第五个子ToDo
应该是List
组件的一个实例。
+ - text: ToDo
组件的第五个子元素应该是List
组件的一个实例。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ToDo)); return mockedComponent.children().first().childAt(4).name() === 'List'; })());
- - text: List
组件的两个实例都应该有一个名为tasks
的属性,而tasks
应该是array类型。
+ - text: List
组件的两个实例都应该具有一个名为tasks
的属性,并且tasks
的类型应该是数组。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ToDo)); return Array.isArray(mockedComponent.find('List').get(0).props.tasks) && Array.isArray(mockedComponent.find('List').get(1).props.tasks); })());
- - text: 表示今天任务的第一个List
组件应该有2个或更多项。
+ - text: 表示今天任务的第一个List
组件应该有 2 个或更多项。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ToDo)); return mockedComponent.find('List').get(0).props.tasks.length >= 2; })());
- - text: 表示明天任务的第二个List
组件应该有3个或更多项。
+ - text: 表示明天任务的第二个List
组件应该有 3 个或更多项。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ToDo)); return mockedComponent.find('List').get(1).props.tasks.length >= 3; })());
- - text: 'List
组件应该将p
标记中的tasks
prop的值呈现为以逗号分隔的列表,例如walk dog, workout
。'
+ - text: List
组件应在p
标签中渲染tasks
属性的值。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(ToDo)); return mockedComponent.find('p').get(0).props.children === mockedComponent.find('List').get(0).props.tasks.join(', ') && mockedComponent.find('p').get(1).props.children === mockedComponent.find('List').get(1).props.tasks.join(', '); })());
```
@@ -43,7 +59,7 @@ tests:
{}
{ /* change code above this line */ } @@ -67,7 +83,6 @@ class ToDo extends React.Component { ); } }; - ```{props.tasks.join(', ')}
+}; + +class ToDo extends React.Component { + constructor(props) { + super(props); + } + render() { + return ( +App
组件,它呈现一个名为Welcome
的子组件,它是一个无状态功能组件。您可以通过编写以下方式传递Welcome
user
属性: <应用>您使用React提供支持的自定义HTML属性 ,以将属性
<Welcome user ='Mark'/>
</应用>
user
传递给组件Welcome
。由于Welcome
是一个无状态功能组件,因此它可以访问此值,如下所示: const Welcome =(props)=> <h1> Hello,{props.user}!</ h1>调用此值
props
是标准的,在处理无状态函数组件时,您基本上将其视为返回JSX的函数的参数。您可以在函数体中访问参数的值。使用类组件,您会发现这有点不同。 App
组件,该组件渲染了一个名为Welcome
的子组件,它是一个无状态函数组件。你可以通过以下方式给Welcome
传递一个user
属性:
+
+```jsx
+user
传递给组件Welcome
。由于Welcome
是一个无状态函数组件,它可以像这样访问该值:
+
+```jsx
+const Welcome = (props) => props
这个值是常见做法,当处理无状态函数组件时,你基本上可以将其视为返回 JSX 的函数的参数。这样,你就可以在函数体中访问该值。但对于类组件,访问方式会略有不同。
+Calendar
和CurrentDate
组件。当渲染CurrentDate
从Calendar
组件,通过在属性date
分配给从JavaScript的当前日期Date
对象。然后在CurrentDate
组件中访问此prop
,在p
标签中显示其值。请注意,对于要作为JavaScript计算的prop
值,它们必须用大括号括起来,例如date={Date()}
。 Calendar
和CurrentDate
组件。从Calendar
组件渲染CurrentDate
时,从 JavaScript 的Date
对象分配当前日期,并将其作为date
属性传入。然后访问CurrentDate
组件的prop
,并在p
标签中显示其值。请注意,要将prop
的值视为 JavaScript,必须将它们括在花括号中,例如date={Date()}
。
+Calendar
组件应返回单个div
元素。
- testString: 'assert((function() { const mockedComponent = Enzyme.mount(React.createElement(Calendar)); return mockedComponent.children().type() === "div"; })(), "The Calendar
component should return a single div
element.");'
- - text: Calendar
组件的第二个子CurrentDate
应该是CurrentDate
组件。
- testString: 'assert((function() { const mockedComponent = Enzyme.mount(React.createElement(Calendar)); return mockedComponent.children().childAt(1).name() === "CurrentDate"; })(), "The second child of the Calendar
component should be the CurrentDate
component.");'
- - text: CurrentDate
组件应该有一个名为date
的prop。
- testString: 'assert((function() { const mockedComponent = Enzyme.mount(React.createElement(Calendar)); return mockedComponent.children().childAt(1).props().date })(), "The CurrentDate
component should have a prop called date
.");'
- - text: CurrentDate
的date
道具应包含一串文本。
- testString: 'assert((function() { const mockedComponent = Enzyme.mount(React.createElement(Calendar)); const prop = mockedComponent.children().childAt(1).props().date; return( typeof prop === "string" && prop.length > 0 ); })(), "The date
prop of the CurrentDate
should contain a string of text.");'
- - text: CurrentDate
组件应该从p
标记中的date
道具中呈现值。
- testString: 'assert((function() { const mockedComponent = Enzyme.mount(React.createElement(Calendar)); return mockedComponent.find("p").html().includes(Date().substr(3)); })(), "The CurrentDate
component should render the value from the date
prop in the p
tag.");'
+ - text: Calendar
组件应该返回单个div
元素。
+ testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(Calendar)); return mockedComponent.children().type() === 'div'; })());
+ - text: Calendar
组件的第二个子元素应该是CurrentDate
组件。
+ testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(Calendar)); return mockedComponent.children().childAt(1).name() === 'CurrentDate'; })());
+ - text: CurrentDate
组件应该有一个名为date
的属性。
+ testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(Calendar)); return mockedComponent.children().childAt(1).props().date })());
+ - text: CurrentDate
的date
属性应该包含一段文本字符串。
+ testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(Calendar)); const prop = mockedComponent.children().childAt(1).props().date; return( typeof prop === 'string' && prop.length > 0 ); })());
+ - text: CurrentDate
组件应该把date
属性渲染在p
标签内。
+ testString: assert(/CurrentDate
组件应该把 date
属性渲染在 p
标签内。
+ testString: let date = "dummy date"; assert((function() { const mockedComponent = Enzyme.mount(React.createElement(CurrentDate, {date})); return mockedComponent.find('p').html().includes(date); })());
+
```
@@ -39,6 +60,7 @@ tests:
The current date is: {props.date}
+ { /* change code above this line */ } +state
,然后呈现子组件。您希望这些组件可以访问该state
某些部分,这些部分作为props传递。例如,您可能有一个App
组件可以呈现Navbar
以及其他组件。在您的App
,您的state
包含大量用户信息,但Navbar
只需要访问用户的用户名,以便显示它。您将该state
作为prop传递给Navbar
组件。这种模式说明了React中的一些重要范例。第一种是单向数据流 。状态沿着应用程序组件树的一个方向流动,从有状态父组件到子组件。子组件仅接收所需的状态数据。第二,复杂的有状态应用程序可以分解为几个或者一个有状态的组件。其余组件只是从父级接收状态作为props,并从该状态呈现UI。它开始创建一个分离,其中状态管理在代码的一部分中处理,而UI在另一部分中呈现。将状态逻辑与UI逻辑分离的原则是React的关键原则之一。当它被正确使用时,它使复杂的有状态应用程序的设计更容易管理。 state
,然后用它渲染子组件。你希望这些组件能够访问该state
的某些部分,就把这些部分作为 props 传入。
+例如,也许你有一个App
组件可以渲染Navbar
以及其他组件。在你的App
中,你的state
中包含大量用户信息,但是Navbar
只需要访问用户的用户名就可以显示出来,这时你将该state
作为一个 prop 传递给Navbar
组件即可。
+这个模式说明了 React 中的一些重要范例。第一个是单向数据流,state 沿着应用程序组件树的一个方向流动,从有状态父组件到子组件,子组件只接收它们需要的 state 数据。第二,复杂的有状态应用程序可以分解成几个,或者可能是一个单一的有状态组件。其余组件只是从父组件简单的接收 state 作为 props,并从该 state 渲染 UI。它开始创建一种分离,在这种分离中,state 管理在代码的一部分中处理,而 UI 渲染在另一部分中处理。将 state 逻辑与 UI 逻辑分离是 React 的关键原则之一。当它被正确使用时,它使得复杂的、有状态的应用程序的设计变得更容易管理。
+MyApp
组件是有状态的,并将Navbar
组件呈现为子组件。将name
属性的state
向下传递给子组件,然后在h1
标记中显示该name
,该name
是Navbar
render方法的一部分。 MyApp
组件是有状态的,它将Navbar
组件渲染成它的为子组件。将MyApp
组件state
中的name
属性向下传递给子组件,然后在h1
标签中显示name
,name
是Navbar
render 方法的一部分。
+MyApp
组件应该使用内部的Navbar
组件进行渲染。
+ - text: MyApp
组件应该在内部渲染一个Navbar
组件。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyApp)); return mockedComponent.find('MyApp').length === 1 && mockedComponent.find('Navbar').length === 1; })());
- - text: Navbar
组件应该将MyApp
状态属性name
作为props接收。
+ - text: Navbar
组件应该接收Navbar
的 state 中的name
属性作为 props。
testString: 'async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(MyApp)); const setState = () => { mockedComponent.setState({name: ''TestName''}); return waitForIt(() => mockedComponent.find(''Navbar'').props() )}; const navProps = await setState(); assert(navProps.name === ''TestName''); }; '
- - text: Navbar
的h1
元素应该呈现name
prop。
+ - text: Navbar
中的h1
元素应该渲染 propname
。
testString: 'async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(MyApp)); const navH1Before = mockedComponent.find(''Navbar'').find(''h1'').text(); const setState = () => { mockedComponent.setState({name: ''TestName''}); return waitForIt(() => mockedComponent.find(''Navbar'').find(''h1'').text() )}; const navH1After = await setState(); assert(new RegExp(''TestName'').test(navH1After) && navH1After !== navH1Before); }; '
```
@@ -58,12 +64,11 @@ class Navbar extends React.Component {
render() {
return (
ReactDOM.render(componentToRender, targetNode)
。第一个参数是要呈现的React组件。第二个参数是要在其中呈现该组件的DOM节点。 React组件传递到ReactDOM.render()
与JSX元素略有不同。对于JSX元素,您传入要呈现的元素的名称。但是,对于React组件,您需要使用与渲染嵌套组件相同的语法,例如ReactDOM.render(<ComponentToRender />, targetNode)
。您可以将此语法用于ES6类组件和功能组件。 ReactDOM.render(componentToRender, targetNode)
。第一个参数是要渲染的 React 组件。第二个参数是要在其中渲染该组件的 DOM 节点。
+React 组件传递到ReactDOM.render()
与 JSX 元素略有不同。对于 JSX 元素,你传入的是要渲染的元素的名称。但是,对于 React 组件,你需要使用与渲染嵌套组件相同的语法,例如ReactDOM.render(<ComponentToRender />, targetNode)
。你可以将此语法用于ES6类组件和函数组件。
+Fruits
和Vegetables
组件都是在幕后为您定义的。将两个组件渲染为TypesOfFood
组件的TypesOfFood
组件,然后将TypesOfFood
呈现给DOM。有一个div
, id='challenge-node'
可供您使用。 Fruits
和Vegetables
组件。将两个组件渲染为TypesOfFood
组件的子组件,然后将TypesOfFood
渲染到 DOM 节点,在这个挑战中,请渲染到 id 为challenge-node
的div
中。
+TypesOfFood
组件应返回单个div
元素。
+ - text: TypesOfFood
组件应该返回单个div
元素。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood)); return mockedComponent.children().type() === 'div'; })());
- - text: TypesOfFood
组件应该在h1
元素之后呈现Fruits
组件。
+ - text: TypesOfFood
组件应该在h1
元素之后渲染Fruits
组件。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood)); return mockedComponent.children().childAt(1).name() === 'Fruits'; })());
- - text: TypesOfFood
组件应该在Fruits
之后呈现Vegetables
组件。
+ - text: TypesOfFood
组件应该在Fruits
组件之后渲染Vegetables
组件。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(TypesOfFood)); return mockedComponent.children().childAt(2).name() === 'Vegetables'; })());
- - text: TypesOfFood
组件应该使用id challenge-node
呈现给div
的DOM。
+ - text: TypesOfFood
组件应该渲染到 id 为challenge-node
的div
中。
testString: assert((function() { const html = document.getElementById('challenge-node').childNodes[0].innerHTML; return html.includes('if/else
, &&,
null
和三元运算符( condition ? expressionIfTrue : expressionIfFalse
)来做出有关呈现内容和何时呈现的条件决策。但是,还有一个重要的话题要讨论,它可以让你将这些概念中的任何一个或全部与另一个强大的React功能结合起来:道具。使用props来有条件地呈现代码对于React开发人员来说非常普遍 - 也就是说,他们使用给定prop的值来自动决定渲染内容。在此挑战中,您将设置子组件以根据道具进行渲染决策。您还将使用三元运算符,但您可以看到在最后几个挑战中涵盖的其他几个概念在此上下文中可能同样有用。 if/else
、&&,
、null
和三元运算符(condition ? expressionIfTrue : expressionIfFalse
)对渲染什么和何时渲染做出有条件的判定。然而,还有一个重要的话题需要讨论,让你将这些概念中的任何一个或所有概念与另一个强大的 React 功能结合起来:props。使用 props 有条件地渲染代码在 React 开发人员中很常见--也就是说:他们使用给定 prop 的值来自动决定渲染什么。
+在这个挑战中,你将设置一个子组件来根据 props 做出渲染决定。你可以使用三元运算符,但是你可以看到过去几个挑战中涵盖的其他几个概念在这种情况下可能同样有用。
+GameOfChance
的父GameOfChance
和名为Results
的子级。它们用于创建一个简单的游戏,用户按下按钮以查看它们是赢还是输。首先,您需要一个简单的表达式,每次运行时都会随机返回一个不同的值。您可以使用Math.random()
。每次调用此方法时,此方法返回0
(包括)和1
(不包括)之间的值。因此,对于50/50赔率,请在表达式中使用Math.random() > .5
。从统计学上讲,这个表达式将在50%的时间内返回true
,而在其他50%时则返回false
。在第30行,用此表达式替换注释以完成变量声明。现在您有了一个表达式,您可以使用该表达式在代码中做出随机决策。接下来,您需要实现此功能。将Results
组件渲染为GameOfChance
的子GameOfChance
,并将expression
作为名为fiftyFifty
的prop fiftyFifty
。在Results
组件中,编写一个三元表达式来呈现文本"You win!"
或者"You lose!"
基于从GameOfChance
传入的fiftyFifty
道具。最后,确保handleClick()
方法正确计算每个回合,以便用户知道他们玩了多少次。这也用于让用户知道组件已经实际更新,以防它们连续两次赢或输。 GameOfChance
的父组件和一个名为Results
的子组件。他们被用来创建一个简单的游戏,用户按下按钮来看他们是赢还是输。
+首先,你需要一个简单的表达式,每次运行时都会随机返回一个不同的值。你可以使用Math.random()
。每次调用此方法时,此方法返回0
(包括)和1
(不包括)之间的值。因此,对于50/50的几率,请在表达式中使用Math.random() > .5
。从统计学上讲,这个表达式有 50% 的几率返回true
,另外 50% 返回false
。在第 30 行,用此表达式替换注释以完成变量声明。
+现在你有了一个表达式,可以用来在代码中做出随机决定,接下来你需要实现以下功能:将Results
组件渲染为GameOfChance
的子组件,并将expression
作为 prop 传递出去,prop 的名字是fiftyFifty
。在Results
组件中,编写一个三元表达式基于从GameOfChance
传来的 propfiftyFifty
来渲染文本"You win!"
或者"You lose!"
。最后,确保handleClick()
方法正确计算每个回合,以便用户知道他们玩过多少次。这也可以让用户知道组件实际上已经更新,以防他们连续赢两次或输两次时自己不知道。
+GameOfChance
组件应存在并呈现给页面。
+ - text: GameOfChance
组件应该存在并渲染到页面。
testString: assert.strictEqual(Enzyme.mount(React.createElement(GameOfChance)).find('GameOfChance').length, 1);
- - text: GameOfChance
应返回单个button
元素。
+ - text: GameOfChance
应该返回单个button
元素。
testString: assert.strictEqual(Enzyme.mount(React.createElement(GameOfChance)).find('button').length, 1);
- - text: GameOfChance
应返回Results
组件的单个实例,其中有一个名为fiftyFifty
的prop。
+ - text: GameOfChance
应该返回Results
组件的一个实例,它有一个名为fiftyFifty
的 prop。
testString: assert(Enzyme.mount(React.createElement(GameOfChance)).find('Results').length === 1 && Enzyme.mount(React.createElement(GameOfChance)).find('Results').props().hasOwnProperty('fiftyFifty') === true);
- - text: 应该使用counter
设置为值1
的属性初始化GameOfChance
状态。
+ - text: GameOfChance
的 state 应该使用值为1
的counter
属性来初始化。
testString: assert.strictEqual(Enzyme.mount(React.createElement(GameOfChance)).state().counter, 1);
- - text: '当GameOfChance
组件首次呈现给DOM时,应返回一个p
元素,内部文本为Turn: 1
。'
+ - text: '当GameOfChance
组件第一次渲染到 DOM 时,应该返回一个p
元素,其内部文本为Turn: 1
。'
testString: 'assert.strictEqual(Enzyme.mount(React.createElement(GameOfChance)).find(''p'').text(), ''Turn: 1'');'
- - text: 每次单击该按钮时,计数器状态应增加值1,并且应将单个p
元素呈现给包含文本“Turn:N”的DOM,其中N是计数器状态的值。
+ - text: '每次点击按钮,counter 应该增加 1,并且一个包含文本"Turn: N"
的p
元素应该渲染到DOM,其中N
是 counter 的值。'
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''); }; '
- - text: 首次将GameOfChance
组件安装到DOM时,每次单击该按钮时,应返回单个h1
元素,随机呈现You Win!
或者You Lose!
。
+ - text: 当GameOfChance
组件第一次挂载到 DOM 上时,每次按钮被点击,都应该返回一个h1
元素,元素中随机渲染You Win!
或者You Lose!
。
testString: 'async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const comp = Enzyme.mount(React.createElement(GameOfChance)); const simulate = () => { comp.find(''button'').simulate(''click''); };const result = () => ({ h1: comp.find(''h1'').length, text: comp.find(''h1'').text() });const _1 = result(); const _2 = () => { simulate(); return waitForIt(() => result())}; const _3 = () => { simulate(); return waitForIt(() => result())}; const _4 = () => { simulate(); return waitForIt(() => result())}; const _5 = () => { simulate(); return waitForIt(() => result())}; const _6 = () => { simulate(); return waitForIt(() => result())}; const _7 = () => { simulate(); return waitForIt(() => result())}; const _8 = () => { simulate(); return waitForIt(() => result())}; const _9 = () => { simulate(); return waitForIt(() => result())}; const _10 = () => { simulate(); return waitForIt(() => result())}; const _2_val = await _2(); const _3_val = await _3(); const _4_val = await _4(); const _5_val = await _5(); const _6_val = await _6(); const _7_val = await _7(); const _8_val = await _8(); const _9_val = await _9(); const _10_val = await _10(); const __text = new Set([_1.text, _2_val.text, _3_val.text, _4_val.text, _5_val.text, _6_val.text, _7_val.text, _8_val.text, _9_val.text, _10_val.text]); const __h1 = new Set([_1.h1, _2_val.h1, _3_val.h1, _4_val.h1, _5_val.h1, _6_val.h1, _7_val.h1, _8_val.h1, _9_val.h1, _10_val.h1]); assert(__text.size === 2 && __h1.size === 1); }; '
```
@@ -72,7 +79,7 @@ class GameOfChance extends React.Component {
});
}
render() {
- let expression = null; // change code here
+ const expression = null; // change code here
return (
{'Turn: ' + this.state.counter}
+ReactDOM.render(componentToRender, targetNode)
,其中第一个参数是要呈现的React元素或组件,第二个参数是DOM节点您想要将组件渲染到。正如您所料,必须在JSX元素声明之后调用ReactDOM.render()
,就像在使用它们之前必须声明变量一样。 ReactDOM.render(componentToRender, targetNode)
,其中第一个参数是要渲染的 React 元素或组件,第二个参数是要将组件渲染到的 DOM 节点。
+如你所料,必须在 JSX 元素声明之后调用ReactDOM.render()
,就像你在使用变量之前必须声明它一样。
+ReactDOM.render()
方法将此组件呈现给页面。您可以直接将定义的JSX元素作为第一个参数传递,并使用document.getElementById()
来选择要将其渲染到的DOM节点。有一个div
, id='challenge-node'
可供您使用。确保不要更改JSX
常量。 ReactDOM.render()
方法将该组件渲染到页面。可以将定义好的 JSX 元素直接作为第一个参数传入,并使用document.getElementById()
来选择要渲染到的 DOM 节点,在这个挑战中,请渲染到 id 为challenge-node
的div
中。
+JSX
应该返回一个div
元素。
testString: assert(JSX.type === 'div');
- - text: div
应包含一个h1
标记作为第一个元素。
+ - text: div
应该包含一个h1
标签作为第一个元素。
testString: assert(JSX.props.children[0].type === 'h1');
- text: div
应该包含一个p
标签作为第二个元素。
testString: assert(JSX.props.children[1].type === 'p');
- - text: 提供的JSX元素应该使用id challenge-node
呈现给DOM challenge-node
。
+ - text: 提供的 JSX 元素应该渲染到 id 为challenge-node
的 DOM 节点。
testString: assert(document.getElementById('challenge-node').childNodes[0].innerHTML === 'Lets render this to the DOM
'); ``` @@ -56,8 +62,16 @@ const JSX = ( ## SolutionLets render this to the DOM
+renderToString()
方法,您可以将其用于此目的。有两个关键原因可以解释为什么服务器上的渲染可能会在真实世界的应用程序中使用。首先,如果不这样做,你的React应用程序将包含一个相对空的HTML文件和一大堆JavaScript,当它最初加载到浏览器时。对于试图索引页面内容以便人们可以找到您的搜索引擎而言,这可能并不理想。如果在服务器上呈现初始HTML标记并将其发送到客户端,则初始页面加载包含搜索引擎可以抓取的所有页面标记。其次,这会创建更快的初始页面加载体验,因为呈现的HTML小于整个应用程序的JavaScript代码。 React仍然能够识别您的应用并在初始加载后进行管理。 renderToString()
方法。
+有两个关键原因可以解释为什么服务器上的渲染可能会在真实世界的应用程序中使用。首先,如果不这样做,你的 React 应用程序将包含一个代码量很少的 HTML 文件和一大堆 JavaScript,当它最初加载到浏览器时。这对于搜索引擎来说可能不太理想,因为它们试图为你的网页内容生成索引,以便人们可以找到你。如果在服务器上渲染初始 HTML 标记并将其发送到客户端,则初始页面加载的内容包含搜索引擎可以抓取的所有页面标记。其次,这创造了更快的初始页面加载体验,因为渲染的 HTML 代码量要比整个应用程序的 JavaScript 代码小。React 仍然能够识别你的应用并在初始加载后进行管理。
+renderToString()
方法在ReactDOMServer
上提供,在此处可用作全局对象。该方法采用一个参数,它是一个React元素。使用此选项将App
呈现为字符串。 renderToString()
方法由ReactDOMServer
提供,在这里已为你定义成全局变量。这个方法接受一个 React 元素作为参数。用它来把App
渲染成字符串。
+App
组件应使用ReactDOMServer.renderToString
呈现为字符串。
+ - text: App
组件应该使用ReactDOMServer.renderToString
渲染一个字符串。
testString: getUserInput => assert(getUserInput('index').replace(/ /g,'').includes('ReactDOMServer.renderToString(state
方法。在render()
方法中,在return
语句之前,您可以直接编写JavaScript。例如,您可以声明函数,从state
或props
访问数据,对此数据执行计算,等等。然后,您可以将任何数据分配给您可以在return
语句中访问的变量。 state
。在render()
方法中,在return
语句之前,你可以直接编写 JavaScript。例如,你可以声明函数、从state
或props
访问数据、对此数据执行计算等。然后,你可以将任何数据赋值给你在return
语句中可以访问的变量。
+MyComponent
render方法中,定义一个名为name
的const
,并将其设置为等于组件state
的name值。因为您可以直接在代码的这一部分编写JavaScript,所以您不必将此引用括在花括号中。接下来,在return语句中,使用变量name
在h1
标记中呈现此值。请记住,您需要在return语句中使用JSX语法(JavaScript的大括号)。 MyComponent
的 render 方法中,定义一个名为name
的常量
,并将其设置为组件state
中的 name 值。因为可以直接在代码部分编写 JavaScript,所以不需要用大括号括起来。
+接下来,在 return 语句中,在h1
标签中渲染变量name
的值。记住,在 return 语句中需要使用 JSX 语法(用到 JavaScript 的花括号)。
+MyComponent
应该有一个键name
,其freeCodeCamp
值存储在其状态中。
+ - text: MyComponent
应该有一个键name
,其值freeCodeCamp
存储在其 state 中。
testString: assert(Enzyme.mount(React.createElement(MyComponent)).state('name') === 'freeCodeCamp');
- - text: MyComponent
应该渲染一个包含在单个div
的h1
标头。
+ - text: MyComponent
应该在div
中渲染一个h1
标题。
testString: assert(/h1
标记应包含对{name}
的引用。'
+ - text: 渲染的h1
标签应该包含{name}
的引用。
testString: getUserInput => assert(/h1
标头应包含从组件状态呈现的文本。
+ - text: 渲染的h1
标题中应该包含一段文本,这段文本是从组件的 state 中渲染出来的。
testString: 'async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); const first = () => { mockedComponent.setState({ name: ''TestName'' }); return waitForIt(() => mockedComponent.html()) }; const firstValue = await first(); assert(firstValue === ''render()
方法中的state
数据。您可以使用this.state
访问数据。如果要在render方法的return
中访问状态值,则必须将值括在花括号中。 State
是React中组件最强大的功能之一。它允许您跟踪应用程序中的重要数据并呈现UI以响应此数据中的更改。如果您的数据发生变化,您的UI将会发生变化React使用所谓的虚拟DOM来跟踪幕后的变化。当状态数据更新时,它会触发使用该数据重新呈现组件 - 包括作为道具接收数据的子组件。 React更新实际的DOM,但仅在必要时更新。这意味着您不必担心更改DOM。您只需声明UI应该是什么样子。请注意,如果使组件有状态,则其他组件不会知道其state
。它的state
是完全封装的,或者是该组件的本地状态,除非您将状态数据作为props
传递给子组件。这种封装state
概念非常重要,因为它允许您编写某些逻辑,然后在代码中的某个位置包含和隔离该逻辑。 render()
方法中state
的数据。你就可以使用this.state
访问数据。
+如果你想在 render 方法的return
中访问 state 值,你必须把这个值用花括号括起来。
+state
是 React 组件中最强大的特性之一,它允许你跟踪应用程序中的重要数据,并根据数据的变化渲染 UI。如果你的数据发生变化,你的 UI 也会随之改变。React 使用所谓的虚拟 DOM 来跟踪幕后的变化。当 state 数据更新时,它会使用该数据触发组件的重新渲染--包括接收 prop 数据的子组件。React 只在必要的时候更新实际的DOM,这意味着你不必担心 DOM 的变更,只需声明 UI 的外观即可。
+注意,如果组件有状态,则没有其他组件知道它的state
。它的state
是完全封装的,或者是局限于组件本身的,除非你将 state 数据作为props
传递给子组件。封装state
的概念非常重要,因为它允许你编写特定的逻辑,然后将该逻辑包含并隔离在代码中的某个位置。
+MyComponent
已经是有状态的。在组件的render方法中定义h1
标记,该方法从组件的状态呈现name
的值。 注意: h1
应该只从state
呈现值而不是其他内容。在JSX中,您使用花括号{ }
编写的任何代码都将被视为JavaScript。因此,要从state
访问值,只需将引用括在花括号中。 MyComponent
是一个有状态组件,在组件的 render 方法中定义一个h1
标签,该方法从组件的 state 渲染name
的值。
+注意: h1
应该只渲染来自state
的值。在 JSX 中,使用花括号{ }
编写的任何代码都将被视为 JavaScript。因此,要访问state
中的值,只需将引用括在花括号中即可。
+MyComponent
应该有一个键name
,其freeCodeCamp
值存储在其状态中。
+ - text: MyComponent
应该有一个键name
,其值freeCodeCamp
存储在其 state 中。
testString: assert(Enzyme.mount(React.createElement(MyComponent)).state('name') === 'freeCodeCamp');
- - text: MyComponent
应该渲染一个包含在单个div
的h1
标头。
+ - text: MyComponent
应该在div
中渲染一个h1
标题。
testString: assert(/h1
标头应包含从组件状态呈现的文本。
+ - text: 渲染的h1
标题中应该包含一段文本,这段文本是从组件的 state 中渲染出来的。
testString: 'async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); const first = () => { mockedComponent.setState({ name: ''TestName'' }); return waitForIt(() => mockedComponent.html()) }; const firstValue = await first(); assert(firstValue === ''render()
方法中的标准if/else
语句执行此操作。 render()
方法中使用的标准if/else
语句来实现这一点。
+boolean
,用于跟踪是否要在UI中显示某个元素。该button
切换此值的状态。目前,它每次都呈现相同的UI。使用if/else
语句重写render()
方法,以便如果display
为true
,则返回当前标记。否则,返回没有h1
元素的标记。 注意:您必须编写if/else
以传递测试。使用三元运算符不会通过此处。 布尔值
,用于跟踪是否要在 UI 中显示某个元素。按钮
切换此值的状态。目前,它每次都呈现相同的 UI。用if/else
语句重写render()
方法,如果display
为true
则返回当前标记。否则,返回不带h1
元素的标记。
+注意: 写if/else
语句才能通过测试,使用三元运算符是不会通过的。
+MyComponent
应该存在并呈现。
+ - text: MyComponent
应该存在并被渲染。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return mockedComponent.find('MyComponent').length === 1; })());
- - text: 当display
设置为true
,应该渲染div
, button
和h1
。
+ - text: 当display
被设置为true
时,div
、button
和h1
标签应该被渲染。
testString: 'async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); const state_1 = () => { mockedComponent.setState({display: true}); return waitForIt(() => mockedComponent )}; const updated = await state_1(); assert(mockedComponent.find(''div'').length === 1 && mockedComponent.find(''div'').children().length === 2 && mockedComponent.find(''button'').length === 1 && mockedComponent.find(''h1'').length === 1); }; '
- - text: 当display
设置为false
,只应呈现div
和button
。
+ - text: 当display
被设置为false
时,只有div
和button
应该被渲染。
testString: 'async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); const state_1 = () => { mockedComponent.setState({display: false}); return waitForIt(() => mockedComponent )}; const updated = await state_1(); assert(mockedComponent.find(''div'').length === 1 && mockedComponent.find(''div'').children().length === 1 && mockedComponent.find(''button'').length === 1 && mockedComponent.find(''h1'').length === 0); }; '
- - text: render方法应使用if/else
语句来检查this.state.display
的条件。
+ - text: render 方法中应该使用if/else
语句来检查this.state.display
的条件。
testString: getUserInput => assert(getUserInput('index').includes('if') && getUserInput('index').includes('else'));
```
@@ -61,7 +66,6 @@ class MyComponent extends React.Component {
);
}
};
-
```
React.Component
的类,但不使用内部状态(在下一个挑战中涵盖)。最后,有状态组件是保持其自身内部状态的任何组件。您可能会看到有状态组件简称为组件或React组件。一种常见的模式是尽可能地减少有状态并创建无状态功能组件。这有助于将状态管理包含到应用程序的特定区域。反过来,通过更容易地了解状态更改如何影响其行为,这可以改善应用程序的开发和维护。 React.Component
,但是不使用内部状态(下一个挑战中讨论)。最后,状态组件是指维护其自身内部状态的组件,它简称组件或 React 组件。
+一种常见的应用模式是尽可能减少状态组件并创建无状态的函数组件。这有助于将状态管理包含到应用程序的特定区域。反过来,通过更容易地跟踪状态变化如何影响其行为,可以改进应用程序的开发和维护。
+CampSite
组件,它将Camper
组件呈现为子组件。定义Camper
组件并为其指定{ name: 'CamperBot' }
默认道具。在Camper
组件内部,渲染您想要的任何代码,但要确保有一个p
元素仅包含作为prop
传递的name
值。最后,在Camper
组件上定义propTypes
,要求将name
作为prop提供,并验证它是string
类型。 CampSite
组件,它把Camper
组件渲染为自己的子组件。定义Camper
组件,并为其分配默认 props{ name: 'CamperBot' }
。你可以在Camper
组件内部渲染任何你想要的代码,但是要确保有一个p
元素,它只包含作为prop
传递的name
值。最后,在Camper
组件上定义propTypes
,要求提供name
作为 prop,并验证它是string
类型。
+CampSite
组件应该呈现。
+ - text: 应该渲染CampSite
组件。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(CampSite)); return mockedComponent.find('CampSite').length === 1; })());
- - text: Camper
组件应呈现。
+ - text: 应该渲染Camper
组件。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(CampSite)); return mockedComponent.find('Camper').length === 1; })());
- - text: Camper
组件应该包含默认道具,它将字符串CamperBot
分配给键name
。
+ - text: Camper
组件应该包含默认 props,它将字符串CamperBot
赋值给关键字name
。
testString: assert(/Camper.defaultProps={name:(['"`])CamperBot\1,?}/.test(code.replace(/\s/g, '')));
- - text: Camper
组件应包含要求name
prop为string
类型的prop类型。
+ - text: Camper
组件应该包含string
类型的name
prop。
testString: assert(/Camper.propTypes={name:PropTypes.string.isRequired,?}/.test(code.replace(/\s/g, '')));
- - text: Camper
组件应包含一个p
元素,其中只包含name
prop的文本。
+ - text: Camper
组件应该包含一个p
元素,元素内是来自propname
的唯一文本。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(CampSite)); return mockedComponent.find('p').text() === mockedComponent.find('Camper').props().name; })());
```
@@ -64,7 +70,6 @@ class CampSite extends React.Component {
var PropTypes = {
string: { isRequired: true }
};
-
```
{props.name}
+state
以及如何在constructor
初始化状态。还有一种方法可以改变组件的state
。 React提供了一种更新名为setState
组件state
的方法。您可以在组件类中调用setState
方法,如下所示: this.setState()
,传入一个具有键值对的对象。键是您的状态属性,值是更新的状态数据。例如,如果我们在状态中存储username
并想要更新它,它将如下所示: this.setState({React希望你永远不要直接修改
用户名:'Lewis'
});
state
,而是在状态发生变化时总是使用this.setState()
。此外,您应该注意React可以批量处理多个状态更新以提高性能。这意味着通过setState
方法的状态更新可以是异步的。 setState
方法有一种替代语法,它提供了解决此问题的方法。这很少需要,但记住它是件好事!有关更多详细信息,请参阅React文档 。 state
以及如何在constructor
中初始化 state。还有一种方法可以更改组件的state
,React 提供了setState
方法来更新组件的state
。在组件类中调用setState
方法如下所示:this.setState()
,传入键值对的对象,其中键是 state 属性,值是更新后的 state 数据。例如,如果我们在 state 中存储username
,并想要更新它,代码如下所示:
+
+```jsx
+this.setState({
+ username: 'Lewis'
+});
+```
+
+React 希望你永远不要直接修改state
,而是在 state 发生改变时始终使用this.setState()
。此外,你应该注意,React 可以批量处理多个 state 更新以提高性能。这意味着通过setState
方法进行的 state 更新可以是异步的。setState
方法有一种替代语法可以解决异步问题,虽然这很少用到,但是最好还是记住它!有关详细信息,请参阅React 文档。
+button
元素,它有一个onClick()
处理程序。当button
在浏览器中收到单击事件时,将触发此处理程序,并运行MyComponent
定义的handleClick
方法。在handleClick
方法中,使用this.setState()
更新组件state
。设置name
的属性state
为等于字符串React Rocks!
。单击按钮并观察呈现的状态更新。如果您还不完全了解点击处理程序代码在此时的工作原理,请不要担心。它涵盖了即将到来的挑战。 button
元素,它有一个onClick()
处理程序。当button
在浏览器中接收到单击事件时触发此处理程序,并运行MyComponent
中定义的handleClick
方法。在handleClick
方法中,使用this.setState()
更新组件的state
。设置state
中的name
属性为字符串React Rocks!
。
+单击按钮查看渲染的 state 的更新。如果你不完全理解单击处理程序代码在此时的工作方式,请不要担心。在接下来的挑战中会有讲述。
+MyComponent
的状态应使用键值对{ name: Initial State }
。'
+ - text: 'MyComponent
的 state 应该使用键值对 { name: Initial State }
来初始化。'
testString: 'assert(Enzyme.mount(React.createElement(MyComponent)).state(''name'') === ''Initial State'');'
- - text: MyComponent
应该呈现一个h1
标头。
+ - text: MyComponent
应该渲染一个h1
标题。
testString: assert(Enzyme.mount(React.createElement(MyComponent)).find('h1').length === 1);
- - text: 渲染的h1
标头应包含从组件状态呈现的文本。
+ - text: 渲染的h1
标题中应该包含一段文本,这段文本是从组件的 state 中渲染出来的。
testString: 'async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); const first = () => { mockedComponent.setState({ name: ''TestName'' }); return waitForIt(() => mockedComponent.html()); }; const firstValue = await first(); assert(/MyComponent
上调用handleClick
方法应该将state属性设置为等于React Rocks!
。
+ - text: 调用MyComponent
的handleClick
方法应该将 state 的 name 属性设置为React Rocks!
。
testString: 'async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); const first = () => { mockedComponent.setState({ name: ''Before'' }); return waitForIt(() => mockedComponent.state(''name'')); }; const second = () => { mockedComponent.instance().handleClick(); return waitForIt(() => mockedComponent.state(''name'')); }; const firstValue = await first(); const secondValue = await second(); assert(firstValue === ''Before'' && secondValue === ''React Rocks!''); };'
```
@@ -59,7 +72,6 @@ class MyComponent extends React.Component {
);
}
};
-
```
else if
语句来返回略有不同的UI,你可能会重复代码,这会留下错误的余地。相反,您可以使用&&
logical运算符以更简洁的方式执行条件逻辑。这是可能的,因为您要检查条件是否为true
,如果是,则返回一些标记。下面是一个示例: {condition && <p>markup</p>}
如果condition
为true
,则返回标记。如果条件为false
,则在评估condition
后操作将立即返回false
并且不返回任何内容。您可以直接在JSX中包含这些语句,并在每个语句之后写入&&
多个条件串在一起。这允许您在render()
方法中处理更复杂的条件逻辑,而无需重复大量代码。 else if
语句来返回稍微不同的 UI,你可能会写很多重复代码,这就留下了出错的空间。相反,你可以使用&&
逻辑运算符以更简洁的方式执行条件逻辑。这是完全可行的,因为你希望检查条件是否为真,如果为真,则返回一些标记。这里有一个例子:
+{condition && <p>markup</p>}
+如果condition
为 true,则返回标记。如果 condition 为 false,操作将在判断condition
后立即返回false
,并且不返回任何内容。你可以将这些语句直接包含在 JSX 中,并通过在每个条件后面写&&
来将多个条件串在一起。这允许你在render()
方法中处理更复杂的条件逻辑,而无需重复大量代码。
+h1
仅在display
为true
呈现,但使用&&
logical运算符而不是if/else
语句。 h1
还是在display
为true
时被渲染,但使用&&
逻辑运算符代替if/else
语句。
+MyComponent
应该存在并呈现。
+ - text: MyComponent
应该存在并被渲染。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return mockedComponent.find('MyComponent').length; })());
- - text: 当display
设置为true
,应该渲染div
, button
和h1
。
+ - text: 当display
被设置为true
时,div
、button
和h1
标签应该被渲染。
testString: 'async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); const state_1 = () => { mockedComponent.setState({display: true}); return waitForIt(() => mockedComponent )}; const updated = await state_1(); assert(updated.find(''div'').length === 1 && updated.find(''div'').children().length === 2 && updated.find(''button'').length === 1 && updated.find(''h1'').length === 1); }; '
- - text: 当display
设置为false
,只应呈现div
和button
。
+ - text: 当display
被设置为false
时,只有div
和button
应该被渲染。
testString: 'async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); const state_1 = () => { mockedComponent.setState({display: false}); return waitForIt(() => mockedComponent )}; const updated = await state_1(); assert(updated.find(''div'').length === 1 && updated.find(''div'').children().length === 1 && updated.find(''button'').length === 1 && updated.find(''h1'').length === 0); }; '
- - text: render方法应该使用&& logical运算符来检查this.state.display的条件。
+ - text: render 方法应该使用&&
逻辑运算符来检查this.state.display
的条件。
testString: getUserInput => assert(getUserInput('index').includes('&&'));
```
@@ -46,9 +52,9 @@ class MyComponent extends React.Component {
this.toggleDisplay = this.toggleDisplay.bind(this);
}
toggleDisplay() {
- this.setState({
- display: !this.state.display
- });
+ this.setState(state => ({
+ display: !state.display
+ }));
}
render() {
// change code below this line
@@ -60,7 +66,6 @@ class MyComponent extends React.Component {
);
}
};
-
```
@@ -70,7 +75,7 @@ class MyComponent extends React.Component {
if/else
语句的快捷方式。它们不像传统的if/else
语句那样健壮,但它们在React开发人员中非常受欢迎。这样做的一个原因是因为JSX是如何编译的, if/else
语句不能直接插入到JSX代码中。您可能已经注意到这几个挑战 - 当需要if/else
语句时,它总是在return
语句之外 。如果要在JSX中实现条件逻辑,三元表达式可能是一个很好的选择。回想一下,三元运算符有三个部分,但是你可以将几个三元表达式组合在一起。这是基本语法: 条件? expressionIfTrue:expressionIfFalse
if/else
语句的缩写。它们不像传统的if/else
语句那样健壮,但是在 React 开发人员中非常流行,原因之一就是 JSX 的编译原理,if/else
语句不能直接插入到 JSX 代码中。你可能在前几个挑战就注意到了这一点--当需要if/else
语句时,它总是在return
语句外面。如果你想在 JSX 中实现条件逻辑,三元表达式是一个很好的选择。回想一下,三元运算符有三个部分,但是你可以将多个三元表达式组合在一起。以下是基本语法:
+
+```js
+condition ? expressionIfTrue : expressionIfFalse
+```
+
+CheckUserAge
组件的render()
方法中定义了三个常量。它们被称为buttonOne
, buttonTwo
和buttonThree
。每个都分配了一个表示按钮元素的简单JSX表达式。首先,使用input
和userAge
初始化CheckUserAge
的状态, userAge
其设置为空字符串的值。一旦组件向页面呈现信息,用户就应该有办法与它进行交互。在组件的return
语句中,设置一个实现以下逻辑的三元表达式:当页面首次加载时,将提交按钮buttonOne
呈现给页面。然后,当用户输入他们的年龄并单击该按钮时,根据年龄呈现不同的按钮。如果用户输入的数字小于18
,则渲染buttonThree
。如果用户输入的数字大于或等于18
,则渲染buttonTwo
。 CheckUserAge
组件的render()
方法中定义了三个常量,它们分别是buttonOne
、buttonTwo
和buttonThree
。每个都分配了一个表示按钮元素的简单 JSX 表达式。首先,使用input
和userAge
初始化CheckUserAge
的 state,并将其值设置为空字符串。
+一旦组件将信息渲染给页面,用户应该有一种方法与之交互。在组件的return
语句中,设置一个实现以下逻辑的三元表达式:当页面首次加载时,将提交按钮buttonOne
渲染到页面。然后,当用户输入年龄并点击该按钮时,根据年龄渲染不同的按钮。如果用户输入的数字小于18
,则渲染buttonThree
。如果用户输入的数字大于或等于18
,则渲染buttonTwo
。
+CheckUserAge
组件应使用单个input
元素和单个button
元素进行渲染。
+ - text: CheckUserAge
组件应该渲染出单个input
元素和单个button
元素。
testString: assert(Enzyme.mount(React.createElement(CheckUserAge)).find('div').find('input').length === 1 && Enzyme.mount(React.createElement(CheckUserAge)).find('div').find('button').length === 1);
- - text: 应使用userAge
属性和input
属性初始化CheckUserAge
组件的状态,两者都设置为空字符串的值。
+ - text: CheckUserAge
组件的 state 应该用userAge
属性和input
属性初始化,并且这两个属性的值都被设置为空字符串。
testString: assert(Enzyme.mount(React.createElement(CheckUserAge)).state().input === '' && Enzyme.mount(React.createElement(CheckUserAge)).state().userAge === '');
- - text: 当CheckUserAge
组件首次呈现给DOM时, button
的内部文本应该是Submit。
+ - text: 当CheckUserAge
组件首次渲染到 DOM 时,按钮
内部的文本应为 Submit。
testString: assert(Enzyme.mount(React.createElement(CheckUserAge)).find('button').text() === 'Submit');
- - text: 当进入一个数小于18 input
元件和button
被点击, button
的内部文本应该读You Shall Not Pass
。
+ - text: 当小于 18 的数字输入到input
元素中并点击该按钮
时,该按钮
的内部文本应该是You Shall Not Pass
。
testString: 'async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(CheckUserAge)); const initialButton = mockedComponent.find(''button'').text(); const enter3AndClickButton = () => { mockedComponent.find(''input'').simulate(''change'', {target: { value: ''3'' }}); mockedComponent.find(''button'').simulate(''click''); return waitForIt(() => { mockedComponent.update(); return mockedComponent.find(''button'').text(); }); }; const enter17AndClickButton = () => { mockedComponent.find(''input'').simulate(''change'', {target: { value: ''17'' }}); mockedComponent.find(''button'').simulate(''click''); return waitForIt(() => { mockedComponent.update(); return mockedComponent.find(''button'').text(); }); }; const userAge3 = await enter3AndClickButton(); const userAge17 = await enter17AndClickButton(); assert(initialButton === ''Submit'' && userAge3 === ''You Shall Not Pass'' && userAge17 === ''You Shall Not Pass''); }; '
- - text: 当数大于或等于18输入到input
元件和button
被点击, button
的内部文本应该阅读You May Enter
。
+ - text: 当大于或等于 18 的数字输入到input
元素中并点击该按钮
时,该按钮
的内部文本应该是You May Enter
。
testString: 'async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(CheckUserAge)); const initialButton = mockedComponent.find(''button'').text(); const enter18AndClickButton = () => { mockedComponent.find(''input'').simulate(''change'', {target: { value: ''18'' }}); mockedComponent.find(''button'').simulate(''click''); return waitForIt(() => { mockedComponent.update(); return mockedComponent.find(''button'').text(); }); }; const enter35AndClickButton = () => { mockedComponent.find(''input'').simulate(''change'', {target: { value: ''35'' }}); mockedComponent.find(''button'').simulate(''click''); return waitForIt(() => { mockedComponent.update(); return mockedComponent.find(''button'').text(); }); }; const userAge18 = await enter18AndClickButton(); const userAge35 = await enter35AndClickButton(); assert(initialButton === ''Submit'' && userAge18 === ''You May Enter'' && userAge35 === ''You May Enter''); }; '
- - text: 一旦提交了一个数字,并再次更改了input
的值,该button
应返回到Submit
。
+ - text: 一旦提交了一个数字,并再次更改了input
的值,该按钮
内部文本应该变回Submit
。
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''); }; '
- - text: 您的代码不应包含任何if/else
语句。
+ - text: 你的代码不应该包含任何if/else
语句。
testString: assert(new RegExp(/(\s|;)if(\s|\()/).test(Enzyme.mount(React.createElement(CheckUserAge)).instance().render.toString()) === false);
```
@@ -43,6 +53,7 @@ tests:
{ }
将JavaScript代码注入JSX代码,以执行诸如访问道具,传递道具,访问状态,在代码中插入注释以及最近为组件设置样式等任务。这些都是将JavaScript放入JSX的常见用例,但它们并不是您在React组件中使用JavaScript代码的唯一方法。您也可以在return
语句之前直接在render
方法中编写JavaScript, 而不必将其插入花括号中。这是因为它还不在JSX代码中。当你想在里面的JSX代码后使用一个变量return
的语句,你把变量名花括号内。 { }
将 JavaScript 代码插入到 JSX 代码中,用于访问 props、传递 props、访问 state、在代码中插入注释以及最近学习的定制组件样式等任务。这些都是将 JavaScript 放在 JSX 中的常见用例,但是在 React 组件中使用 JavaScript 代码还有其他方式。
+在render
方法中编写 JavaScript,你可以把 JavaScript 直接放在return
语句之前,而不必将其插入大括号中。这是因为它还不在 JSX 代码中。当你稍后想在return
语句中的 JSX 代码中使用变量时,可以将变量名放在大括号中。
+render
方法有一个数组,其中包含20个短语,用于表示经典1980年代Magic Eight Ball玩具中的答案。按钮单击事件绑定到ask
方法,因此每次单击该按钮时,将生成随机数并将其存储为randomIndex
状态。在第52行,删除字符串"change me!"
并重新分配answer
const,以便每次组件更新时,您的代码随机访问possibleAnswers
数组的不同索引。最后,在p
标签内插入answer
const。 render
方法中有一个包含 20 个短语的数组,用于表示 20 世纪 80 年代经典魔术八球玩具中的答案。绑定ask
方法到按钮的单击事件,每次单击该按钮时,将生成随机数并将其存储为 state 中的randomIndex
。在第 52 行,删除字符串"change me!"
并重新分配answer
常量,以便每次组件更新时,你的代码随机访问possibleAnswers
数组的不同索引。最后,在p
标签内插入answer
常量。
+MagicEightBall
组件应该存在并且应该呈现给页面。
+ - text: MagicEightBall
组件应该存在并被渲染到页面。
testString: assert.strictEqual(Enzyme.mount(React.createElement(MagicEightBall)).find('MagicEightBall').length, 1);
- - text: MagicEightBall
的第一个孩子应该是一个input
元素。
+ - text: MagicEightBall
的第一个子元素应该是input
元素。
testString: assert.strictEqual(Enzyme.mount(React.createElement(MagicEightBall)).children().childAt(0).name(), 'input');
- - text: MagicEightBall
的第三个孩子应该是一个button
元素。
+ - text: MagicEightBall
的第三个子元素应该是button
元素。
testString: assert.strictEqual(Enzyme.mount(React.createElement(MagicEightBall)).children().childAt(2).name(), 'button');
- - text: MagicEightBall
的状态应的属性初始化userInput
和属性randomIndex
都设置为空字符串的值。
+ - text: MagicEightBall
的 state 应该用userInput
属性和randomIndex
属性初始化,并且这两个属性的值都应该是空字符串。
testString: assert(Enzyme.mount(React.createElement(MagicEightBall)).state('randomIndex') === '' && Enzyme.mount(React.createElement(MagicEightBall)).state('userInput') === '');
- - text: 当MagicEightBall
首次挂载到DOM时,它应该返回一个空的p
元素。
+ - text: 当MagicEightBall
第一次加载到 DOM 中时,它应该返回一个空的p
元素。
testString: assert(Enzyme.mount(React.createElement(MagicEightBall)).find('p').length === 1 && Enzyme.mount(React.createElement(MagicEightBall)).find('p').text() === '');
- - text: 当文本输入到input
元素并单击该按钮时, MagicEightBall
组件应该返回一个p
元素,该元素包含possibleAnswers
数组中的随机元素。
+ - text: 当文本被输入到input
元素中并点击按钮时,MagicEightBall
组件应该返回一个p
元素,该元素包含数组possibleAnswers
中的随机一个元素。
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);}'
```
@@ -50,8 +55,8 @@ class MagicEightBall extends React.Component {
constructor(props) {
super(props);
this.state = {
- userInput: ",
- randomIndex: "
+ userInput: '',
+ randomIndex: ''
}
this.ask = this.ask.bind(this);
this.handleChange = this.handleChange.bind(this);
@@ -60,7 +65,7 @@ class MagicEightBall extends React.Component {
if (this.state.userInput) {
this.setState({
randomIndex: Math.floor(Math.random() * 20),
- userInput: "
+ userInput: ''
});
}
}
@@ -113,7 +118,6 @@ class MagicEightBall extends React.Component {
);
}
};
-
```
@@ -123,7 +127,8 @@ class MagicEightBall extends React.Component {
+ {answer} +
+map
数组方法是一个强大的工具,在使用React时经常会使用它。另一种与map
相关的方法是filter
,它根据条件过滤数组的内容,然后返回一个新数组。例如,如果您的所有用户都具有online
属性(可以设置为true
或false
,则可以通过以下方式仅过滤那些在线用户: let onlineUsers = users.filter(user => user.online);
map
数组方法是一个强大的工具,在使用 React 时经常使用。与map
相关的另一种方法是filter
,它根据条件过滤数组的内容,然后返回一个新数组。例如,如果你有一个 users 数组,每个数组元素都有一个可以设置为true
或false
的online
属性,你可以只过滤那些在线的用户:
+let onlineUsers = users.filter(user => user.online);
+MyComponent
的state
是用一组用户初始化的。有些用户在线,有些则没有。过滤数组,以便只查看在线用户。要执行此操作,请首先使用filter
返回仅包含online
属性为true
的用户的新数组。然后,在renderOnline
变量中,映射已过滤的数组,并为包含其username
文本的每个用户返回li
元素。确保包括一个独特的key
,就像在最后的挑战中一样。 MyComponent
的state
由一个 users 数组初始化。有些用户在线,有些则不在线。过滤数组,以便你只看到在线用户。为此,首先使用filter
返回一个新数组,该数组只包含online
属性为true
的用户。然后,在renderOnline
变量中,映射经过过滤的数组,并为每个用户返回一个包含它们username
文本的li
元素。确保像上一个挑战一样包含一个独特的key
。
+MyComponent
应该存在并呈现给页面。
+ - text: MyComponent
应该存在并被渲染到页面。
testString: assert.strictEqual(Enzyme.mount(React.createElement(MyComponent)).find('MyComponent').length, 1);
- - text: MyComponent
的状态应初始化为六个用户的数组。“)
+ - text: MyComponent
的 state 应该初始化为包含 6 个用户的数组。
testString: assert(Array.isArray(Enzyme.mount(React.createElement(MyComponent)).state('users')) === true && Enzyme.mount(React.createElement(MyComponent)).state('users').length === 6);
- - text: MyComponent
应该返回一个div
,一个h1
,然后是一个无序列表,其中包含每个在线状态设置为true
用户的li
元素。
+ - text: MyComponent
应该返回一个div
、一个h1
和一个包含li
元素的无序列表,该列表用于展示在线状态为true
的每个用户。
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); }; '
- - text: MyComponent
应该呈现包含每个在线用户的用户名的li
元素。
+ - text: MyComponent
应该渲染包含每个在线用户用户名的li
元素。
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 === ''key
属性。
+ - text: 每个列表项元素都应该有一个唯一的key
属性。
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; })());
```
@@ -84,7 +89,6 @@ class MyComponent extends React.Component {
);
}
};
-
```
@@ -94,7 +98,7 @@ class MyComponent extends React.Component {
Array.map()
说明了这个概念。例如,您创建一个简单的“待办事项列表”应用程序。作为程序员,您无法知道用户可能在其列表中有多少项。您需要设置组件,以便在使用该程序的人决定今天是洗衣日之前动态呈现正确数量的列表元素。 Array.map()
阐明了这个概念。
+例如,你创建一个简单的“To Do List”应用程序。作为程序员,你无法知道用户可能在其列表中有多少项。你需要设置组件,以便在使用该程序的人决定今天今日待办事项之前动态渲染正确数量的列表元素。
+MyToDoList
组件。如果您完成了受控制的表单质询,那么这些代您会注意到一个textarea
和一个button
,以及一些跟踪其状态的方法,但是还没有任何内容呈现给页面。在constructor
内部,创建一个this.state
对象并定义两个状态: userInput
应初始化为空字符串, toDoList
应初始化为空数组。接下来,删除items
变量旁边的render()
方法中的注释。取而代之,映射存储在组件内部状态中的toDoList
数组,并为每个项目动态呈现li
。尝试输入字符串eat, code, sleep, repeat
到textarea
,然后单击按钮,看看会发生什么。 注意:您可能知道由这样的映射操作创建的所有兄弟子元素都需要提供唯一的key
属性。别担心,这是下一个挑战的主题。 MyToDoList
组件的大部分设置。如果你完成了受控表单挑战,这些代码中的一些应该看起来很熟悉。你会注意到一个textarea
和一个button
,以及一些跟踪它们状态的方法,但是页面当前还没有任何东西被渲染。
+在constructor
中,创建一个this.state
对象并定义两个 state:userInput
应该初始化为空字符串,toDoList
应该初始化为空数组。接下来,删除items
变量旁边render()
方法中的注释。取而代之的是,将存储在组件内部 state 中的toDoList
数组一一映射并相应的动态呈现li
元素。尝试在textarea
中输入eat, code, sleep, repeat
,然后点击按钮,看看会发生什么。
+注意: 你可能知道,像这样的映射操作创建的所有兄弟子元素都需要提供唯一的key
属性。别担心,这是下一个挑战的主题。
+MyToDoList
的第一个子MyToDoList
应该是textarea
元素。
- 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: MyToDoList
的第三个子MyToDoList
应该是一个button
元素。
- 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: 应使用toDoList
将MyToDoList
的状态初始化为空数组。
- 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: 应使用userInput
将MyToDoList
的状态初始化为空字符串。
- 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: 单击“ Create List
按钮时, MyToDoList
组件应动态返回无序列表,该列表包含输入到textarea
元素中的逗号分隔列表的每个项目的列表项元素。
- 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."); }; '
+ - text: MyToDoList
组件应该存在并渲染到页面。
+ testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyToDoList)); return mockedComponent.find('MyToDoList').length === 1; })());
+ - text: MyToDoList
组件的第一个子元素应该是textarea
元素。
+ testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyToDoList)); return mockedComponent.find('MyToDoList').children().childAt(0).type() === 'textarea'; })());
+ - text: MyToDoList
组件的第二个子元素应该是br
元素。
+ testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyToDoList)); return mockedComponent.find('MyToDoList').children().childAt(1).type() === 'br'; })());
+ - text: MyToDoList
组件的第三个子元素应该是button
元素。
+ testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyToDoList)); return mockedComponent.find('MyToDoList').children().childAt(2).type() === 'button'; })());
+ - text: MyToDoList
的 state 应该使用被设置为空数组的toDoList
进行初始化。
+ testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyToDoList)); const initialState = mockedComponent.state(); return Array.isArray(initialState.toDoList) === true && initialState.toDoList.length === 0; })());
+ - text: MyToDoList
的 state 应该使用被设置为空字符串的userInput
进行初始化。
+ testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyToDoList)); const initialState = mockedComponent.state(); return typeof initialState.userInput === 'string' && initialState.userInput.length === 0; })());
+ - text: 单击Create List
按钮时,MyToDoList
组件应该动态返回一个无序列表,该列表包含输入到textarea
元素中的逗号分隔列表的每个项目的列表项目元素。
+ 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''); }; '
```
@@ -68,6 +77,73 @@ class MyToDoList extends React.Component {
}
render() {
const items = null; // change code here
+ return (
+ TypesOfFruit
和Fruits
。取TypesOfFruit
组分和构成它,或者它窝 ,所述内Fruits
组分。然后使用Fruits
组件并将其嵌套在TypesOfFood
组件中。结果应该是一个嵌套在父组件中的子组件,它嵌套在它自己的父组件中! TypesOfFruit
和Fruits
。请把TypesOfFruit
组件放到Fruits
组件中,然后把Fruits
组件放到TypesOfFood
组件中。结果应该是子组件嵌套在父组件中,父组件嵌套在它本身的父组件中!
+TypesOfFood
组件应返回单个div
元素。
+ - text: TypesOfFood
组件应该返回单个div
元素。
testString: assert(Enzyme.shallow(React.createElement(TypesOfFood)).type() === 'div');
- - text: TypesOfFood
组件应返回Fruits
组件。
+ - text: TypesOfFood
组件应该返回Fruits
组件。
testString: assert(Enzyme.shallow(React.createElement(TypesOfFood)).props().children[1].type.name === 'Fruits');
- - text: Fruits
组件应返回TypesOfFruit
组件。
+ - text: Fruits
组件应该返回TypesOfFruit
组件。
testString: assert(Enzyme.mount(React.createElement(TypesOfFood)).find('h2').html() === 'TypesOfFruit
组件应返回h2
和ul
元素。
+ - text: TypesOfFruit
组件应该返回h2
和ul
元素。
testString: assert(Enzyme.mount(React.createElement(TypesOfFood)).find('ul').text() === 'ApplesBlueberriesStrawberriesBananas');
```
@@ -77,7 +82,6 @@ class TypesOfFood extends React.Component {
);
}
};
-
```
@@ -87,7 +91,7 @@ class TypesOfFood extends React.Component {
state
。一个示例是监视值的状态,然后根据此值有条件地呈现UI。有几种不同的方法可以实现这一点,代码编辑器显示了一种方法。 setState()
集中在一起批量更新。所以设置 this.state
或者 this.props
后值没有立即更新。所以最好不要写如下的代码:
+
+```js
+this.setState({
+ counter: this.state.counter + this.props.increment
+});
+```
+
+正确的做法是,给 setState
传入一个函数,这个函数可以访问 state 和 props。给 setState
传入函数可以返回赋值后的 state 和 props。代码可以重写为这样:
+
+```js
+this.setState((state, props) => ({
+ counter: state.counter + props.increment
+}));
+```
+
+如果只需要 `state`,那么用下面的格式也是可以的:
+
+```js
+this.setState(state => ({
+ counter: state.counter + 1
+}));
+```
+
+注意一定要把 object 放在括号里,否则 JavaScript 会认为这只是代码片段。
+MyComponent
有一个visibility
属性,初始化为false
。如果visibility
值为true,则render方法返回一个视图;如果为false,则返回不同的视图。目前,无法更新组件state
的visibility
属性。该值应在true和false之间来回切换。按钮上有一个单击处理程序,它触发一个名为toggleVisibility()
的类方法。定义此方法,以便在调用方法时, visibility
state
切换为相反的值。如果visibility
是false
,该方法将其设置为true
,反之亦然。最后,单击按钮以根据其state
查看组件的条件呈现。 提示:不要忘记将this
关键字绑定到构造函数中的方法! MyComponent
有一个初始值为false
的visibility
属性。如果visibility
的值为 true,render 方法返回一个视图,如果为 false,返回另一个视图。
+目前,无法更新组件的state
中的visibility
属性,该值应在 true 和 false 之间来回切换。按钮上有一个单击处理程序,它触发一个名为toggleVisibility()
的类方法。定义此方法,以便visibility
的state
在调用方法时切换到相反的值。如果visibility
是false
,则该方法将其设置为true
,反之亦然。
+最后,单击按钮以查看基于其state
的组件的条件渲染。
+提示: 不要忘记将this
关键字绑定到构造函数中的方法上!
+MyComponent
应该返回一个包含button
的div
元素。
- testString: 'assert.strictEqual(Enzyme.mount(React.createElement(MyComponent)).find("div").find("button").length, 1, "MyComponent
should return a div
element which contains a button
.");'
- - text: MyComponent
的状态应该初始化,并将visibility
属性设置为false
。
- testString: 'assert.strictEqual(Enzyme.mount(React.createElement(MyComponent)).state("visibility"), false, "The state of MyComponent
should initialize with a visibility
property set to false
.");'
- - text: 单击按钮元素应该在true
和false
之间切换状态的visibility
属性。
- testString: 'async () => { const waitForIt = (fn) => new Promise((resolve, reject) => setTimeout(() => resolve(fn()), 250)); const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); const first = () => { mockedComponent.setState({ visibility: false }); return waitForIt(() => mockedComponent.state("visibility")); }; const second = () => { mockedComponent.find("button").simulate("click"); return waitForIt(() => mockedComponent.state("visibility")); }; const third = () => { mockedComponent.find("button").simulate("click"); return waitForIt(() => mockedComponent.state("visibility")); }; const firstValue = await first(); const secondValue = await second(); const thirdValue = await third(); assert(!firstValue && secondValue && !thirdValue, "Clicking the button element should toggle the visibility
property in state between true
and false
."); }; '
+ - text: MyComponent
应该返回一个div
元素,其中包含一个button
元素。
+ testString: assert.strictEqual(Enzyme.mount(React.createElement(MyComponent)).find('div').find('button').length, 1);
+ - text: MyComponent
应该使用设置为false
的visibility
属性来初始化其 state。
+ testString: assert.strictEqual(Enzyme.mount(React.createElement(MyComponent)).state('visibility'), false);
+ - text: 单击按钮元素应在true
和false
之间切换visibility
属性的状态。
+ 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); }; '
+ - text: 应该传入setState
一个匿名函数。
+ 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: 不要在 setState
里面使用 this
。
+ testString: assert(!/this\.setState\([^}]*this/.test(code));
```
@@ -65,7 +100,6 @@ class MyComponent extends React.Component {
}
}
};
-
```
@@ -75,7 +109,7 @@ class MyComponent extends React.Component {
componentDidMount()
中对服务器进行API调用或任何调用。将组件安装到DOM后调用此方法。此处对setState()
任何调用都将触发组件的重新呈现。在此方法中调用API并使用API返回的数据设置状态时,一旦收到数据,它将自动触发更新。 componentDidMount()
中对服务器进行 API 调用或任何其他调用。将组件装载到 DOM 后会调用此方法。此处对setState()
的任何调用都将触发组件的重新渲染。在此方法中调用 API 并使用 API 返回的数据设置 state 时,一旦收到数据,它将自动触发更新。
+componentDidMount()
有一个模拟API调用。它在2.5秒后设置状态以模拟调用服务器以检索数据。此示例请求站点的当前活动用户总数。在render方法中,在h1
呈现activeUsers
的值。观看预览中发生的事情,并随时更改超时以查看不同的效果。 componentDidMount()
中有一个模拟 API 调用。它在 2.5 秒后设置 state,以模拟调用服务器检索数据。本示例请求站点的当前活动用户总数。在 render 方法中,把activeUsers
渲染到h1
标签中。观看预览中发生的事情,随意更改超时时间以查看不同的效果。
+MyComponent
应该呈现一个包含h1
标记的div
元素。
+ - text: MyComponent
应该渲染一个包含h1
标签的div
元素。
testString: assert((() => { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return (mockedComponent.find('div').length === 1 && mockedComponent.find('h1').length === 1); })());
- - text: 应使用componentDidMount
的超时函数更新组件状态。
+ - text: 组件 state 应该用componentDidMount
中的延时函数来更新。
testString: assert((() => { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return new RegExp('setTimeout(.|\n)+setState(.|\n)+activeUsers').test(String(mockedComponent.instance().componentDidMount)); })());
- - text: h1
标记应该从MyComponent
的状态呈现activeUsers
值。
+ - text: h1
标签应该从MyComponent
的 state 渲染activeUsers
值。
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())); }; '
```
@@ -57,7 +62,6 @@ class MyComponent extends React.Component {
);
}
};
-
```
@@ -67,7 +71,7 @@ class MyComponent extends React.Component {
componentWillMount()
componentDidMount()
componentWillReceiveProps()
shouldComponentUpdate()
componentWillUpdate()
componentDidUpdate()
componentWillUnmount()
接下来的几节课将介绍这些生命周期方法的一些基本用例。 componentWillMount()
+componentDidMount()
+shouldComponentUpdate()
+componentDidUpdate()
+componentWillUnmount()
+接下来的几节课将讲述这些生命周期方法的一些基本用例。
+
+注意: `componentWillMount` 生命周期方法会在版本 16.X 废弃在版本 17 移除 [(Source)](https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html)
+render()
方法之前调用componentWillMount()
方法。在componentWillMount()
中将某些内容记录到控制台 - 您可能希望打开浏览器控制台以查看输出。 componentWillMount()
方法在render()
方法之前被调用。在componentWillMount()
中将一些内容记录到控制台--你需要打开浏览器控制台以查看输出。
+MyComponent
应该呈现div
元素。
+ - text: MyComponent
应该渲染一个div
元素。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return mockedComponent.find('div').length === 1; })());
- - text: 应该在componentWillMount
调用console.log
。
+ - text: 应该在componentWillMount
中调用console.log
。
testString: assert((function() { const lifecycle = React.createElement(MyComponent).type.prototype.componentWillMount.toString().replace(/ /g,''); return lifecycle.includes('console.log('); })());
```
@@ -46,7 +61,6 @@ class MyComponent extends React.Component {
return
}
};
-
```
@@ -56,7 +70,7 @@ class MyComponent extends React.Component {
React.Component
的ES6 class
。它有一个返回HTML(来自JSX)或null
的render方法。这是React组件的基本形式。一旦你理解了这一点,你就会准备开始构建更复杂的React项目。 class
,它扩展了React.Component
。它有一个返回 HTML(从 JSX 返回)或null
的渲染方法,这是 React 组件的基本形式。一旦你深刻地理解了这一点,你就可以开始构建更复杂的 React 项目了。
+React.Component
的类MyComponent
。它的render方法应该返回一个div
,其中包含一个带有文本的h1
标签: My First React Component!
在里面。准确使用此文本,案例和标点符号很重要。确保也调用组件的构造函数。使用ReactDOM.render()
将此组件呈现给DOM。有一个div
, id='challenge-node'
可供您使用。 MyComponent
类,它是React.Component
的扩展。它的渲染方法应该返回一个div
,其中包含一个h1
标签,标签文本为:My First React Component!
。请确保文本内容、大小写和标点符号正确,以及调用了组件的构造函数。
+使用ReactDOM.render()
把该组件渲染到 DOM 中。有一个id='challenge-node'
的div
可供你使用。
+MyComponent
的React组件。
testString: getUserInput => assert(getUserInput('index').replace(/\s/g, '').includes('classMyComponentextendsReact.Component{'));
- - text: MyComponent
应该包含带有文本My First React Component!
的h1
标签My First React Component!
案例和标点符号问题。
+ - text: MyComponent
应该包含一个h1
标签,标签的文本为My First React Component!
,区分大小写并有标点符号。
testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return mockedComponent.find('h1').text() === 'My First React Component!'; })());
- - text: MyComponent
应该呈现给DOM。
+ - text: MyComponent
应该渲染到 DOM。
testString: assert(document.getElementById('challenge-node').childNodes.length === 1);
```
@@ -48,8 +53,23 @@ tests:
## Solution
state
,编写设置state
方法,以及分配单击处理程序以触发这些方法。 state
,编写设置state
的方法,以及指定单击处理程序来触发这些方法。
+Counter
组件跟踪一个的count
的价值state
。有两个按钮调用方法increment()
和decrement()
。编写这些方法,以便在单击相应按钮时计数器值递增或递减1。此外,创建一个reset()
方法,以便在单击重置按钮时,计数设置为0. 注意:确保不要修改按钮的classNames
。另外,请记住在构造函数中为新创建的方法添加必要的绑定。 Counter
组件跟踪state
中的count
值。有两个按钮分别调用increment()
和decrement()
方法。编写这些方法,使计数器值在单击相应按钮时增加或减少 1。另外,创建一个reset()
方法,当单击 reset 按钮时,把计数设置为 0。
+注意: 确保你没有修改按钮的classNames
。另外,请记住在构造函数中为新创建的方法添加必要的绑定。
+Counter
应返回一个div
元素,其中包含三个按钮,文本内容按此顺序Increment!
, Decrement!
, Reset
。
+ - text: Counter
应该返回一个div
元素,它包含三个按钮,按钮内容依次是Increment!
、Decrement!
、Reset
。
testString: assert((() => { const mockedComponent = Enzyme.mount(React.createElement(Counter)); return (mockedComponent.find('.inc').text() === 'Increment!' && mockedComponent.find('.dec').text() === 'Decrement!' && mockedComponent.find('.reset').text() === 'Reset'); })());
- - text: Counter
的状态应该在count
属性设置为0
的情况下初始化。
+ - text: Counter
应该使用设置为0
的count
属性初始化 state。
testString: 'const mockedComponent = Enzyme.mount(React.createElement(Counter)); assert(mockedComponent.find("h1").text() === "Current Count: 0")'
- - text: 单击增量按钮应将计数增加1
。
+ - text: 单击 increment 按钮应将计数增加1
。
testString: 'const mockedComponent = Enzyme.mount(React.createElement(Counter)); mockedComponent.find(".inc").simulate("click"); assert(mockedComponent.find("h1").text() === "Current Count: 1")'
- - text: 单击减量按钮应将计数减1
。
+ - text: 单击 decrement 按钮应将计数减少1
。
testString: 'const mockedComponent = Enzyme.mount(React.createElement(Counter)); mockedComponent.find(".dec").simulate("click"); assert(mockedComponent.find("h1").text() === "Current Count: -1")'
- - text: 单击重置按钮应将计数重置为0
。
+ - text: 单击 reset 按钮应将计数重置为0
。
testString: 'const mockedComponent = Enzyme.mount(React.createElement(Counter)); mockedComponent.setState({ count: 5 }); const currentCountElement = mockedComponent.find("h1"); assert(currentCountElement.text() === "Current Count: 5"); mockedComponent.find(".reset").simulate("click"); assert(currentCountElement.text() === "Current Count: 0");'
```
@@ -63,7 +68,6 @@ class Counter extends React.Component {
);
}
};
-
```
@@ -73,7 +77,7 @@ class Counter extends React.Component {