From 6021a32de99616d38e0ab347ead803b73ef85b91 Mon Sep 17 00:00:00 2001 From: ZhichengChen Date: Fri, 18 Sep 2020 00:13:42 +0800 Subject: [PATCH] fix(i18n): update Chinese translation of react (#38657) Co-authored-by: Zhicheng Chen --- .../access-props-using-this.props.chinese.md | 62 +++++++-- .../react/add-comments-in-jsx.chinese.md | 41 ++++-- .../react/add-event-listeners.chinese.md | 67 +++++++-- .../add-inline-styles-in-react.chinese.md | 46 +++++-- .../bind-this-to-a-class-method.chinese.md | 59 ++++++-- ...onally-based-on-component-state.chinese.md | 63 +++++++-- .../react/compose-react-components.chinese.md | 62 +++++++-- .../create-a-complex-jsx-element.chinese.md | 56 ++++++-- ...te-a-component-with-composition.chinese.md | 62 +++++++-- .../react/create-a-controlled-form.chinese.md | 71 ++++++++-- .../create-a-controlled-input.chinese.md | 57 ++++++-- .../react/create-a-react-component.chinese.md | 57 ++++++-- .../create-a-simple-jsx-element.chinese.md | 26 ++-- .../create-a-stateful-component.chinese.md | 52 +++++-- ...-stateless-functional-component.chinese.md | 46 +++++-- .../define-an-html-class-in-jsx.chinese.md | 28 ++-- ...elements-a-unique-key-attribute.chinese.md | 63 ++++++--- .../introducing-inline-styles.chinese.md | 36 +++-- ...arn-about-self-closing-jsx-tags.chinese.md | 33 +++-- ...ders-with-shouldcomponentupdate.chinese.md | 69 ++++++++-- .../react/override-default-props.chinese.md | 44 ++++-- .../react/pass-a-callback-as-props.chinese.md | 87 ++++++++++-- .../react/pass-an-array-as-props.chinese.md | 67 ++++++--- ...-stateless-functional-component.chinese.md | 82 ++++++++--- ...te-as-props-to-child-components.chinese.md | 56 ++++++-- ...er-a-class-component-to-the-dom.chinese.md | 47 +++++-- ...render-conditionally-from-props.chinese.md | 79 ++++++++--- ...render-html-elements-to-the-dom.chinese.md | 30 ++-- ...-the-server-with-rendertostring.chinese.md | 34 +++-- ...-the-user-interface-another-way.chinese.md | 48 +++++-- ...der-state-in-the-user-interface.chinese.md | 44 ++++-- ...ender-with-an-if-else-condition.chinese.md | 59 ++++++-- ...stateless-functional-components.chinese.md | 61 ++++++-- .../set-state-with-this.setstate.chinese.md | 60 ++++++-- ...-for-a-more-concise-conditional.chinese.md | 58 ++++++-- ...ssion-for-conditional-rendering.chinese.md | 95 ++++++++++--- ...vascript-in-react-render-method.chinese.md | 93 ++++++++++--- ...-to-dynamically-filter-an-array.chinese.md | 81 +++++++++-- ...-to-dynamically-render-elements.chinese.md | 130 +++++++++++++----- .../react/use-default-props.chinese.md | 34 +++-- ...-to-define-the-props-you-expect.chinese.md | 54 ++++++-- ...act-to-render-nested-components.chinese.md | 68 +++++++-- .../use-state-to-toggle-an-element.chinese.md | 92 +++++++++++-- ...ecycle-method-componentdidmount.chinese.md | 48 +++++-- ...cycle-method-componentwillmount.chinese.md | 47 +++++-- ...-a-react-component-from-scratch.chinese.md | 36 +++-- .../react/write-a-simple-counter.chinese.md | 64 +++++++-- 47 files changed, 2153 insertions(+), 601 deletions(-) diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/access-props-using-this.props.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/access-props-using-this.props.chinese.md index 600491b9a6..9c0c15d542 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/access-props-using-this.props.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/access-props-using-this.props.chinese.md @@ -3,30 +3,35 @@ id: 5a24c314108439a4d403616e title: Access Props Using this.props challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 使用this.props访问道具 +forumTopicId: 301375 +localeTitle: 使用 this.props 访问 Props --- ## Description -
最后几个挑战涵盖了将道具传递给子组件的基本方法。但是,如果你传递道具的子组件是ES6类组件,而不是无状态功能组件呢? ES6类组件使用稍微不同的约定来访问props。无论何时在您自己引用类组件时,都使用this关键字。一类组件中访问的道具,你前言您使用与访问它的代码this 。例如,如果ES6类组件具有名为data的prop,则在JSX中编写{this.props.data}
+
+前几项挑战涵盖了将 props 传递给子组件的基本方法。但是,倘若接收 prop 的子组件不是无状态函数组件,而是一个 ES6 类组件又当如何呢?ES6 类组件访问 props 的方法略有不同。 +任何时候,只要引用类组件本身,就要使用this关键字。要访问类组件中的 props,你需要在在访问它的代码前面添加this。例如,如果 ES6 类组件有一个名为data的 prop,你可以在 JSX 中这样写:{this.props.data}。 +
## Instructions -
在父组件ResetPassword呈现ReturnTempPassword组件的实例。在这里,给ReturnTempPassword一个tempPassword的prop,并为它tempPassword一个至少8个字符长的字符串的值。在子项ReturnTempPassword ,访问strong标记内的tempPassword prop,以确保用户看到临时密码。
+
+在父组件ResetPassword中渲染ReturnTempPassword组件的一个实例。在这里,为ReturnTempPassword提供一个tempPasswordprop,并赋值给 prop 一个长度至少为 8 个字符的字符串。在子组件ReturnTempPassword中,访问strong标签中的tempPasswordprop,以确保用户看到临时密码。 +
## Tests
```yml tests: - - text: 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: ReturnTempPasswordtempPassword道具应该等于至少8字符的字符串。 + - text: ReturnTempPassword组件的tempPasswordprop 值应该是一个字符串,其长度至少为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组件应该显示你作为tempPasswordprop 创建的密码,并且密码显示在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 {
```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
@@ -93,8 +97,40 @@ console.info('after the test'); ## Solution
+ ```js -// solution required +class ReturnTempPassword extends React.Component { + constructor(props) { + super(props); + + } + render() { + return ( +
+

Your temporary password is: {this.props.tempPassword}

+
+ ); + } +}; + +class ResetPassword extends React.Component { + constructor(props) { + super(props); + + } + render() { + return ( +
+

Reset Password

+

We've generated a new temporary password for you.

+

Please reset this password from your account settings ASAP.

+ { /* change code below this line */ } + + { /* change code above this line */ } +
+ ); + } +}; ``` -/section> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/add-comments-in-jsx.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/add-comments-in-jsx.chinese.md index a542ecf42a..49c796ffe9 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/add-comments-in-jsx.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/add-comments-in-jsx.chinese.md @@ -3,15 +3,20 @@ id: 5a24bbe0dba28a8d3cbd4c5e title: Add Comments in JSX challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 在JSX中添加注释 +forumTopicId: 301376 +localeTitle: 在 JSX 中添加注释 --- ## Description -
JSX是一种可以编译成有效JavaScript的语法。有时,为了便于阅读,您可能需要在代码中添加注释。像大多数编程语言一样,JSX有自己的方法来做到这一点。要将注释放在JSX中,可以使用语法{/* */}来包含注释文本。
+
+JSX 是一种可以编译成有效 JavaScript 的语法。有时,为了便于阅读,你可能需要在代码中添加注释。像大多数编程语言一样,JSX 也有自己的方法来实现这一点。 +要将注释放在 JSX 中,可以使用{/* */}语法来包裹注释文本。 +
## Instructions -
代码编辑器的JSX元素与您在上一个挑战中创建的元素类似。在提供的div元素中的某处添加注释,而不修改现有的h1p元素。
+
+代码编辑器中的 JSX 元素与你在上一个挑战中创建的元素类似。在提供的div元素中的某处添加注释,而不修改现有的h1p元素。 +
## Tests
@@ -19,14 +24,15 @@ localeTitle: 在JSX中添加注释 ```yml tests: - text: 常量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: 当前的 h1p 元素不能被修改。 + 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(/
[\s\S]*{\s*\/\*[\s\S]*\*\/\s*}[\s\S]*<\/div>/.test(code)); ```
@@ -43,7 +49,6 @@ const JSX = (

Here's a subtitle

); - ``` @@ -53,7 +58,7 @@ const JSX = (
```js -console.info('after the test'); +ReactDOM.render(JSX, document.getElementById('root')) ```
@@ -63,8 +68,14 @@ console.info('after the test'); ## Solution
+ ```js -// solution required +const JSX = ( +
+

This is a block of JSX

+ { /* this is a JSX comment */ } +

Here's a subtitle

+
); ``` -/section> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/add-event-listeners.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/add-event-listeners.chinese.md index 81ce165cfd..16670577a1 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/add-event-listeners.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/add-event-listeners.chinese.md @@ -3,28 +3,34 @@ id: 5a24c314108439a4d403617e title: Add Event Listeners challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 添加事件监听器 +forumTopicId: 301377 +localeTitle: 添加事件侦听器 --- ## Description -
componentDidMount()方法也是附加您需要为特定功能添加的任何事件侦听器的最佳位置。 React提供了一个合成事件系统,它包装了浏览器中的本机事件系统。这意味着无论用户的浏览器如何,合成事件系统的行为都完全相同 - 即使本机事件在不同浏览器之间的行为可能不同。您已经使用了一些合成事件处理程序,如onClick() 。 React的合成事件系统非常适合用于您在DOM元素上管理的大多数交互。但是,如果要将事件处理程序附加到文档或窗口对象,则必须直接执行此操作。
+
+componentDidMount()方法也是添加特定功能所需的任何事件监听器的最佳位置。React 提供了一个合成事件系统,它将本地事件系统封装在浏览器中。这意味着,不管用户的浏览器如何,合成事件系统的行为都完全相同--即使不同浏览器之间的本地事件的行为可能不同。 +你已经使用了一些合成事件处理程序,如onClick()。React 的合成事件系统非常适合用于你在 DOM 元素上管理的大多数交互。但是,如果要将事件处理程序附加到 document 或 window 对象,则必须直接执行此操作。 +
## Instructions -
componentDidMount()方法中为keydown事件附加事件侦听器,并让这些事件触发回调handleKeyPress() 。您可以使用document.addEventListener() ,它将事件(在引号中)作为第一个参数,将回调作为第二个参数。然后,在componentWillUnmount() ,删除此相同的事件侦听器。您可以将相同的参数传递给document.removeEventListener() 。在卸载和销毁之前,使用此生命周期方法对React组件进行任何清理是一种很好的做法。删除事件侦听器就是一个这样的清理操作的示例。
+
+在componentDidMount()方法中为keydown事件添加事件监听器,并让这些事件触发回调handleKeyPress()。你可以使用document.addEventListener(),它将事件(用引号括起来)作为第一个参数,将回调作为第二个参数。 +然后,在componentWillUnmount()中移除相同的事件监听器。你可以把相同的参数传递给document.removeEventListener()。在卸载和销毁 React 组件之前,最好使用这种生命周期方法对它们进行清理。移除事件监听器就是这样一个清理操作的例子。 +
## Tests
```yml tests: - - text: 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 {
```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
@@ -92,8 +97,44 @@ console.info('after the test'); ## Solution
+ ```js -// solution required +class MyComponent extends React.Component { + constructor(props) { + super(props); + this.state = { + message: '' + }; + this.handleKeyPress = this.handleKeyPress.bind(this); + this.handleEnter = this.handleEnter.bind(this); } + componentDidMount() { + // change code below this line + document.addEventListener('keydown', this.handleKeyPress); + // change code above this line + } + componentWillUnmount() { + // change code below this line + document.removeEventListener('keydown', this.handleKeyPress); + // change code above this line + } + handleEnter() { + this.setState({ + message: this.state.message + 'You pressed the enter key! ' + }); + } + handleKeyPress(event) { + if (event.keyCode === 13) { + this.handleEnter(); + } + } + render() { + return ( +
+

{this.state.message}

+
+ ); + } +}; ``` -/section> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/add-inline-styles-in-react.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/add-inline-styles-in-react.chinese.md index 0fea96b825..a2d0ff4f0e 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/add-inline-styles-in-react.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/add-inline-styles-in-react.chinese.md @@ -3,30 +3,35 @@ id: 5a24c314108439a4d4036182 title: Add Inline Styles in React challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 在React中添加内联样式 +forumTopicId: 301378 +localeTitle: 在 React 中添加内联样式 --- ## Description -
您可能已经注意到,在上一个挑战中,除了设置为JavaScript对象的style属性之外,还有HTML内联样式的其他几种语法差异。首先,某些CSS样式属性的名称使用驼峰大小写。例如,最后一个挑战使用fontSize而不是font-size设置font-size 。像font-size这样的连字符是JavaScript对象属性的无效语法,因此React使用驼峰大小写。通常,任何带连字符的样式属性都是使用JSX中的camel case编写的。除非另有说明,否则假定所有属性值长度单位(如heightwidthfontSize )均为px 。例如,如果要使用em ,则将值和单位用引号括起来,如{fontSize: "4em"} 。除了默认为px的长度值之外,所有其他属性值都应该用引号括起来。
+
+在上一次挑战中,你可能已经注意到,除了设置为 JavaScript 对象的style属性之外,与 HTML 内联样式相比,React 的内联样式还有其他几个语法差异。首先,某些 CSS 样式属性的名称使用驼峰式命名。例如,最后一个挑战用fontSize而不是font-size来设置字体的大小。对于 JavaScript 对象属性来说,像font-size这样的连字符命名是无效的语法,所以 React 使用驼峰式命名。通常,任何连字符的 style 属性在 JSX 中都是使用驼峰式命名的。 +除非另有规定,否则所有属性值是长度的(如heightwidthfontSize)其单位都假定为px。例如,如果要使用em,可以用引号将值和单位括起来,例如{fontSize: "4em"}。除了默认为px的长度值之外,所有其他属性值都应该用引号括起来。 +
## Instructions -
如果您有大量样式,则可以将样式object分配给常量以保持代码的有序性。取消注释styles常量并声明具有三个样式属性及其值的object 。给div一个颜色为"purple" ,字体大小为40 ,边框为"2px solid purple" 。然后将style属性设置为等于styles常量。
+
+如果你有大量样式,你可以将 style对象分配给一个常量,以保持代码的组织有序。取消对styles常量的注释,并声明具有三个样式属性及对应值的对象。使div的文字颜色为"purple"、字号为40、边框为"2px solid purple"。然后设置style属性,使其等于styles常量。 +
## Tests
```yml tests: - - text: 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:
```jsx + // const styles = // change code above this line class Colorful extends React.Component { @@ -62,7 +68,7 @@ class Colorful extends React.Component {
```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
@@ -72,8 +78,24 @@ console.info('after the test'); ## Solution
+ ```js -// solution required +const styles = { + color: "purple", + fontSize: 40, + border: "2px solid purple" +}; +// change code above this line +class Colorful extends React.Component { + render() { + // change code below this line + return ( +
Style Me!
+ // change code above this line + ); + } +}; + ``` -/section> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/bind-this-to-a-class-method.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/bind-this-to-a-class-method.chinese.md index 4c1c69fb4e..80e06b823a 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/bind-this-to-a-class-method.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/bind-this-to-a-class-method.chinese.md @@ -3,26 +3,34 @@ id: 5a24c314108439a4d4036174 title: Bind 'this' to a Class Method challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 将'this'绑定到类方法 +forumTopicId: 301379 +localeTitle: 将 this 绑定到 Class 方法 --- ## Description -
除了设置和更新state ,您还可以为组件类定义方法。类方法通常需要使用this关键字,以便它可以访问方法范围内的类(例如stateprops )上的属性。有几种方法可以让您的类方法访问this 。一个常用的方法是显式绑定this所以在构造this组件已初始化变为绑定到类方法。您可能已经注意到最后一个挑战使用this.handleClick = this.handleClick.bind(this)作为构造函数中的handleClick方法。然后,当您在类方法中调用类似this.setState()的函数时, this引用该类,并且不会被undefined注意: this关键字是JavaScript中最令人困惑的方面之一,但它在React中起着重要作用。虽然这里的行为是完全正常的,但这些课程并不是this进行深入审查的地方,所以如果上述内容令人困惑,请参考其他课程!
+
+除了设置和更新state之外,你还可以为组件类定义方法。类方法通常需要使用this关键字,以便它可以访问方法中类的属性(例如stateprops )。有几种方法可以让你的类方法访问this。 +一种常见的方法是在构造函数中显式地绑定this,这样当组件初始化时,this就会绑定到类方法。你可能已经注意到上一个挑战使用了this.handleClick = this.handleClick.bind(this)用于其在构造函数中的handleClick方法。然后,当你在类方法中调用像this.setState()这样的函数时,this指的是这个类,而不是undefined。 +注意: this关键字是 JavaScript 中最令人困惑的方面之一,但它在 React 中扮演着重要的角色。虽然它的行为在这里是完全正常的,但是这些课程并不深入研究this,所以如果以上内容令你感到困惑,请参考其他课程! +
## Instructions -
代码编辑器具有与组件state保持跟踪的项目计数。它还有一个方法,允许您增加此项目计数。但是,该方法不起作用,因为它使用未定义的this关键字。通过明确地结合修复它thisaddItem()在组件的构造方法。接下来,将单击处理程序添加到render方法中的button元素。当按钮收到click事件时,它应该触发addItem()方法。请记住,传递给onClick处理程序的方法需要花括号,因为它应该直接解释为JavaScript。完成上述步骤后,您应该可以单击按钮并查看HTML中的项目计数增量。
+
+代码编辑器有一个带有state的组件,用于跟踪项目计数。它还有一个方法,允许你增加此项目计数。但是,该方法不起作用,因为它使用了未定义的this关键字。可以通过将this显式绑定到组件构造函数中的addItem()方法来修复它。 +接下来,向 render 方法中的button元素添加一个单击处理程序。当按钮接收到单击事件时,它应该触发addItem()方法。记住,传递给onClick处理程序的方法需要使用花括号,因为它应该直接被解释为 JavaScript。 +完成上述步骤后,你应该可以单击按钮并查看 HTML 中的项目计数增量。 +
## Tests
```yml tests: - - text: 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 */ } -

Current Item Count: {this.state.itemCount}

+

{this.state.text}

); } }; - ``` @@ -71,7 +78,7 @@ class MyComponent extends React.Component {
```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
@@ -81,8 +88,30 @@ console.info('after the test'); ## Solution
+ ```js -// solution required +class MyComponent extends React.Component { + constructor(props) { + super(props); + this.state = { + text: "Hello" + }; + this.handleClick = this.handleClick.bind(this); + } + handleClick() { + this.setState({ + text: "You clicked!" + }); + } + render() { + return ( +
+ +

{this.state.text}

+
+ ); + } +}; ``` -/section> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/change-inline-css-conditionally-based-on-component-state.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/change-inline-css-conditionally-based-on-component-state.chinese.md index a38ab365b5..786b36ca4d 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/change-inline-css-conditionally-based-on-component-state.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/change-inline-css-conditionally-based-on-component-state.chinese.md @@ -3,30 +3,35 @@ id: 5a24c314108439a4d4036189 title: Change Inline CSS Conditionally Based on Component State challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 有条件地改变内联CSS基于组件状态 +forumTopicId: 301380 +localeTitle: 根据组件状态有条件地更改内联 CSS --- ## Description -
此时,您已经看到了条件渲染的几个应用程序以及内联样式的使用。这是另外一个结合了这两个主题的例子。您还可以根据React组件的状态有条件地呈现CSS。要执行此操作,请检查条件,如果满足该条件,则修改在render方法中分配给JSX元素的样式对象。这个范例很重要,因为它是通过直接修改DOM元素来应用样式的更传统方法的一种戏剧性转变(例如,jQuery非常常见)。在该方法中,您必须跟踪元素何时更改并直接处理实际操作。跟踪更改可能变得很困难,可能会使您的UI无法预测。根据条件设置样式对象时,您将描述UI应如何看作应用程序状态的函数。有明确的信息流只能向一个方向移动。使用React编写应用程序时,这是首选方法。
+
+此时,你已经看到了一些条件渲染的应用程序和内联样式的使用。这里还有一个将这两个主题结合在一起的例子。你也可以根据 React 组件的 state 有条件地渲染 CSS。要执行此操作,请检查条件,如果满足该条件,则修改在 render 方法中分配给 JSX 元素的样式对象。 +这个范例对于更加容易理解,因为相比传统的通过直接修改 DOM 元素来应用样式的方法(这在 jQuery 中非常常见),这种方法是一个戏剧性的转变。在传统方法中,你必须跟踪元素何时发生变化,并直接处理实际操作,这使得跟踪变化变得很困难,也可能会让你的用户界面变得不可预测。当你根据一个条件设置一个样式对象时,你描述了 UI 作为应用程序的状态函数应当如何展现。如此便有一个清晰的单向流动的信息流。这是使用 React 编写应用程序时的首选方法。 +
## Instructions -
代码编辑器有一个简单的受控输入组件,带有样式边框。如果用户在输入框中键入超过15个字符的文本,则希望将此边框设置为红色。添加条件以检查此情况,如果条件有效,则将输入边框样式设置为3px solid red 。您可以通过在输入中输入文本来尝试。
+
+代码编辑器有一个简单的带有边框样式的受控 input 组件。如果用户在输入框中键入超过 15 个字符的文本,你希望将此边框变成红色。添加一个条件来检查这一点,如果条件有效,则将 input 的边框样式设置为3px solid red。你可以通过在 input 中输入文本来尝试。 +
## Tests
```yml tests: - - text: 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:
```jsx + class GateKeeper extends React.Component { constructor(props) { super(props); this.state = { - input: " + input: '' }; this.handleChange = this.handleChange.bind(this); } @@ -69,7 +75,6 @@ class GateKeeper extends React.Component { ); } }; - ```
@@ -79,7 +84,7 @@ class GateKeeper extends React.Component {
```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
@@ -89,8 +94,38 @@ console.info('after the test'); ## Solution
+ ```js -// solution required +class GateKeeper extends React.Component { + constructor(props) { + super(props); + this.state = { + input: '' + }; + this.handleChange = this.handleChange.bind(this); + } + handleChange(event) { + this.setState({ input: event.target.value }) + } + render() { + let inputStyle = { + border: '1px solid black' + }; + if (this.state.input.length > 15) { + inputStyle.border = '3px solid red'; + }; + return ( +
+

Don't Type Too Much:

+ +
+ ); + } +}; ``` -/section> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/compose-react-components.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/compose-react-components.chinese.md index 722b00f9ba..2465c2bd99 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/compose-react-components.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/compose-react-components.chinese.md @@ -3,28 +3,33 @@ id: 5a24c314108439a4d4036166 title: Compose React Components challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 撰写反应组件 +forumTopicId: 301381 +localeTitle: 组合 React 组件 --- ## Description -
随着挑战继续使用更复杂的组合与React组件和JSX,有一点需要注意。在其他组件中渲染ES6样式类组件与渲染您在最后几个挑战中使用的简单组件没有什么不同。您可以在其他组件中呈现JSX元素,无状态功能组件和ES6类组件。
+
+随着挑战继续,我们将组合使用更复杂的 React 组件和 JSX,有一点需要注意。在其他组件中渲染 ES6 风格的类组件和渲染你在过去几个挑战中使用的简单组件没有什么不同。你可以在其他组件中渲染 JSX 元素、无状态功能组件和 ES6 类组件。 +
## Instructions -
在代码编辑器中, TypesOfFood组件已经在呈现一个名为Vegetables的组件。此外,还有最后一项挑战中的Fruits成分。将两种成分NonCitrus Fruits - 首先是NonCitrus ,然后是Citrus 。这两个组件都是在后台为您提供的。接下来,将Fruits类组件嵌入到TypesOfFood组件中,位于h1标题下方和Vegetables上方。结果应该是一系列嵌套组件,它们使用两种不同的组件类型。
+
+在代码编辑器中,TypesOfFood组件已经渲染了一个名为Vegetables的组件。此外,还有上次挑战中的Fruits组件。 +在Fruits中嵌套两个组件,首先NonCitrus,然后是Citrus,这两个组件都是在后台为你提供的。接下来,将Fruits类组件嵌到TypesOfFood组件中,位于h1标题下方和Vegetables上方。结果应该是一系列嵌套的组件,它们使用两种不同的组件类型。 +
## Tests
```yml tests: - - text: 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 {
```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
@@ -141,8 +144,41 @@ console.info('after the test'); ## Solution
+ ```js -// solution required +class Fruits extends React.Component { + constructor(props) { + super(props); + } + render() { + return ( +
+

Fruits:

+ { /* change code below this line */ } + + + { /* change code above this line */ } +
+ ) + } +} + +class TypesOfFood extends React.Component { + constructor(props) { + super(props); + } + render() { + return ( +
+

Types of Food:

+ { /* change code below this line */ } + + { /* change code above this line */ } + +
+ ); + } +}; ``` -/section> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-complex-jsx-element.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-complex-jsx-element.chinese.md index a60a8f95f4..67f38c8caa 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-complex-jsx-element.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-complex-jsx-element.chinese.md @@ -3,15 +3,43 @@ id: 5a24bbe0dba28a8d3cbd4c5d title: Create a Complex JSX Element challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 创建一个复杂的JSX元素 +forumTopicId: 301382 +localeTitle: 创建一个复杂的 JSX 元素 --- ## Description -
最后一个挑战是JSX的一个简单示例,但JSX也可以代表更复杂的HTML。关于嵌套JSX的一个重要事项是它必须返回一个元素。这个父元素将包装所有其他级别的嵌套元素。例如,编写为没有父包装元素的兄弟姐妹的几个JSX元素将不会转换。这是一个例子: 有效的JSX:
<DIV>
<p>第一段</ p>
<p>第二段</ p>
<p>第3段</ p>
</ DIV>
JSX无效:
<p>第一段</ p>
<p>第二段</ p>
<p>第3段</ p>
+
+上一个挑战是 JSX 的一个简单示例,但 JSX 也可以表示更复杂的 HTML。 +关于嵌套的 JSX,你需要知道的一件重要的事情,那就是它必须返回单个元素。 +这个父元素将包裹所有其他级别的嵌套元素。 +例如,几个作为兄弟元素而编写的JSX元素没有父元素包裹将不会被转换。 +这里是一个示例: +有效的 JSX: + +```jsx +
+

Paragraph One

+

Paragraph Two

+

Paragraph Three

+
+``` + +无效的 JSX: + +```jsx +

Paragraph One

+

Paragraph Two

+

Paragraph Three

+``` + +
## Instructions -
定义一个新的常量JSX ,它呈现一个按顺序包含以下元素的div :一个h1 ,一个p和一个包含三个li项的无序列表。您可以在每个元素中包含所需的任何文本。 注意:渲染多个这样的元素时,可以将它们全部括在括号中,但并不是严格要求的。另请注意,此挑战使用div标记将所有子元素包装在单个父元素中。如果删除div ,JSX将不再转换。请记住这一点,因为当您在React组件中返回JSX元素时它也将适用。
+
+定义一个新的常量JSX,渲染一个div,其中依次包含以下元素: +一个h1,一个p,一个包含三个li项的无序列表。你可以在每个元素中包含任何你想要的文本。 +注意: 当像这样渲染多个元素时,你可以把它们都用圆括号括起来,但是这并不是必须的。还请注意,此挑战使用div标签把所有子元素包裹在里面。如果删除div,JSX 将不会编译这些元素。请记住这一点,因为当你在 React 组件中返回 JSX 元素时它也适用。 +
## Tests
@@ -22,9 +50,9 @@ tests: testString: assert(JSX.type === 'div'); - text: 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:
```js -console.info('after the test'); +ReactDOM.render(JSX, document.getElementById('root')) ```
@@ -60,8 +88,18 @@ console.info('after the test'); ## Solution
+ ```js -// solution required +const JSX = ( +
+

Hello JSX!

+

Some info

+
    +
  • An item
  • +
  • Another item
  • +
  • A third item
  • +
+
); ``` -/section> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-component-with-composition.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-component-with-composition.chinese.md index 2124711b56..8db9c681a9 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-component-with-composition.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-component-with-composition.chinese.md @@ -3,26 +3,44 @@ id: 5a24c314108439a4d4036164 title: Create a Component with Composition challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 使用Composition创建一个Component +forumTopicId: 301383 +localeTitle: 用组合的方式创建一个 React 组件 --- ## Description -
现在我们来看看如何组合多个React组件。想象一下,您正在构建一个应用程序并创建了三个组件,一个NavbarDashboardFooter 。要将这些组件组合在一起,您可以创建一个App 组件,它将这三个组件中的每一个都呈现为组件。要在React组件中将组件呈现为子组件,请在JSX中包含作为自定义HTML标记编写的组件名称。例如,在render方法中,您可以编写:
回来(
<应用>
<Navbar />
<仪表板/>
<页脚/>
</应用>
当React遇到引用另一个组件的自定义HTML标记(在此示例中包含在< />的组件名称)时,它会在标记的位置呈现该组件的标记。这应该说明App组件与NavbarDashboardFooter之间的父/子关系。
+
+现在我们来看看如何组合多个 React 组件。想象一下,你正在构建一个应用程序,并创建了三个组件:NavbarDashboardFooter。 +要将这些组件组合在一起,你可以创建一个App父组件,将这三个组件分别渲染成为子组件。要在 React 组件中渲染一个子组件,你需要在 JSX 中包含作为自定义 HTML 标签编写的组件名称。例如,在render方法中,你可以这样编写: + +```jsx +return ( + + + +
+ +) +``` + +当 React 遇到引用另一个组件的自定义 HTML 标签时(如本例所示,组件名称包含在< />中),它在标签的位置渲染该组件的标签。这可以说明App组件和NavbarDashboard以及Footer之间的父子关系。 +
## Instructions -
在代码编辑器中,有一个名为ChildComponent的简单功能组件和一个名为ParentComponent的React组件。通过在ParentComponent呈现ChildComponent将两者组合在一起。确保使用正斜杠关闭ChildComponent标记。 注意: ChildComponent是使用ES6箭头函数定义的,因为这是使用React时非常常见的做法。但是,要知道这只是一个功能。如果您不熟悉箭头函数语法,请参阅JavaScript部分。
+
+在代码编辑器中,有一个名为ChildComponent的简单功能组件和一个名为ParentComponent的 React 组件。通过在ParentComponent中渲染ChildComponent来将两者组合在一起。确保使用正斜杠关闭ChildComponent标签。 +注意: ChildComponent是使用 ES6 的箭头函数定义的,因为这是使用 React 时非常常见的做法。但是,要知道这只是一个函数。如果你不熟悉箭头函数语法,请参阅 JavaScript 部分。 +
## Tests
```yml tests: - - text: React组件应返回单个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 {
```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
@@ -79,8 +96,31 @@ console.info('after the test'); ## Solution
+ ```js -// solution required +const ChildComponent = () => { + return ( +
+

I am the child

+
+ ); +}; + +class ParentComponent extends React.Component { + constructor(props) { + super(props); + } + render() { + return ( +
+

I am the parent

+ { /* change code below this line */ } + + { /* change code above this line */ } +
+ ); + } +}; ``` -/section> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-controlled-form.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-controlled-form.chinese.md index 89a17bfaf5..b1a5ad8e9d 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-controlled-form.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-controlled-form.chinese.md @@ -3,30 +3,37 @@ id: 5a24c314108439a4d4036179 title: Create a Controlled Form challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 创建受控表格 +forumTopicId: 301384 +localeTitle: 创建一个可以控制的表单 --- ## Description -
最后一个挑战表明,React可以控制inputtextarea等某些元素的内部状态,这使得它们成为受控组件。这也适用于其他表单元素,包括常规HTML form元素。
+
+上一个挑战展示了 React 能控制某些元素的内部 state,比如inputtextarea,这使得这些元素成为受控组件。这也适用于其他表单元素,包括常规的 HTML 表单form元素。 +
## Instructions -
MyForm组件设置为带有提交处理程序的空form 。提交表单时将调用提交处理程序。我们添加了一个提交表单的按钮。您可以看到它的type设置为submit表明它是控制表单的按钮。在form添加input元素并设置其valueonChange()属性, onChange()一个挑战。然后,您应该完成handleSubmit方法,以便将组件状态属性submit设置为本地state的当前输入值。 注意:您还必须在提交处理程序中调用event.preventDefault() ,以防止将刷新网页的默认表单提交行为。最后,在form之后创建一个h1标记,该form从组件的state呈现submit值。然后,您可以键入表单并单击按钮(或按Enter键),您应该看到您的输入呈现给页面。
+
+MyForm组件中是一个带有提交处理程序的空form元素,提交处理程序将在提交表单时被调用。 +我们增加了一个提交表单的按钮。你可以看到它的type被设置为submit,表明它是控制表单的按钮。在表单中添加input元素,并像上次挑战一样设置其valueonChange()属性。然后,你应该完成handleSubmit方法,以便将组件 state 属性submit设置为本地state下的当前输入值。 +注意:  你还必须在提交处理程序中调用event.preventDefault(),以防止默认的表单提交行为刷新网页。 +最后,在form元素之后创建一个h1标签,该标签从组件的state渲染submit的值。然后,你可以在表单中键入任何内容,然后单击按钮(或按 enter 键),你的输入会渲染到页面上。 +
## Tests
```yml tests: - - text: MyForm应该返回一个包含formh1标记的div元素。表单应包含inputbutton 。 + - text: MyForm应该返回一个包含formh1标签的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的状态应该使用inputsubmit属性初始化,两者都设置为空字符串。 + - text: MyForm的 state 应该用inputsubmit属性初始化,且两者都为空字符串。 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 {
```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
@@ -95,8 +101,43 @@ console.info('after the test'); ## Solution
+ ```js -// solution required +class MyForm extends React.Component { + constructor(props) { + super(props); + this.state = { + input: '', + submit: '' + }; + this.handleChange = this.handleChange.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); + } + handleChange(event) { + this.setState({ + input: event.target.value + }); + } + handleSubmit(event) { + event.preventDefault() + this.setState({ + submit: this.state.input + }); + } + render() { + return ( +
+
+ + +
+

{this.state.submit}

+
+ ); + } +}; ``` -/section> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-controlled-input.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-controlled-input.chinese.md index 069c3bdaae..0351bbb85b 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-controlled-input.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-controlled-input.chinese.md @@ -3,26 +3,34 @@ id: 5a24c314108439a4d4036178 title: Create a Controlled Input challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 创建受控输入 +forumTopicId: 301385 +localeTitle: 创建一个可以控制的输入框 --- ## Description -
您的应用程序可能在state和呈现的UI之间进行更复杂的交互。例如,用于文本输入的表单控件元素(例如inputtextarea在用户键入时在DOM中维护它们自己的状态。使用React,您可以将此可变状态移动到React组件的state 。用户的输入成为应用程序state一部分,因此React控制该输入字段的值。通常,如果React组件具有用户可以键入的输入字段,则它将是受控输入表单。
+
+你的应用程序可能在state和渲染的 UI 之间有更复杂的交互。例如,用于文本输入的表单控件元素(如inputtextarea)在用户键入时在 DOM 中维护自己的 state。通过 React,你可以将这种可变 state 转移到 React 组件的state中。用户的输入变成了应用程序state的一部分,因此 React 控制该输入字段的值。通常,如果你的 React 组件具有用户可以键入的输入字段,那么它将是一个受控的输入表单。 +
## Instructions -
代码编辑器具有名为ControlledInput的组件的骨架,以创建受控input元素。组件的state已经使用包含空字符串的input属性进行初始化。此值表示用户在input字段中键入的文本。首先,创建一个名为handleChange()的方法,该方法具有一个名为event的参数。调用该方法时,它会接收一个event对象,该对象包含input元素中的一串文本。您可以使用方法内的event.target.value访问此字符串。使用此新字符串更新组件stateinput属性。在render方法中,在h4标记上方创建input元素。添加一个value属性,该属性等于组件stateinput属性。然后将onChange()事件处理程序集添加到handleChange()方法。当您在输入框中键入时,该文本由handleChange()方法处理,设置为本地stateinput属性,并在页面的input框中呈现为值。组件state是关于输入数据的单一事实来源。最后但并非最不重要的是,不要忘记在构造函数中添加必要的绑定。
+
+代码编辑器具有一个名为ControlledInput的组件框架,用于创建受控的input元素。组件的state已经被包含空字符串的input属性初始化。此值表示用户在input字段中键入的文本。 +首先,创建一个名为handleChange()的方法,该方法具有一个名为event的参数。方法被调用时,它接收一个event对象,该对象包含一个来自input元素的字符串文本。你可以使用方法内的event.target.value来访问这个字符串。用这个新字符串更新组件的stateinput属性。 +在 render 方法中,在h4标签之上创建input元素。添加一个value属性,它等于组件的stateinput属性。然后将onChange()事件处理程序设置到handleChange()方法。 +在输入框中键入时,该文本由handleChange()方法处理,该文本被设置为本地state中的input属性,并渲染在页面上的input框中。组件state是输入数据的唯一真实来源。 +最后也是最重要的,不要忘记在构造函数中添加必要的绑定。 +
## Tests
```yml tests: - - text: ControlledInput应返回包含inputp标记的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 {
```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
@@ -80,8 +87,34 @@ console.info('after the test'); ## Solution
+ ```js -// solution required +class ControlledInput extends React.Component { + constructor(props) { + super(props); + this.state = { + input: '' + }; + this.handleChange = this.handleChange.bind(this); + } + handleChange(event) { + this.setState({ + input: event.target.value + }); + } + render() { + return ( +
+ +

Controlled Input:

+ +

{this.state.input}

+
+ ); + } +}; ``` -/section> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-react-component.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-react-component.chinese.md index e31229fd85..552fc55893 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-react-component.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-react-component.chinese.md @@ -3,26 +3,47 @@ id: 5a24c314108439a4d4036163 title: Create a React Component challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 创建一个React组件 +forumTopicId: 301386 +localeTitle: 创建一个 React 组件 --- ## Description -
定义React组件的另一种方法是使用ES6 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其他用途。
+
+定义 React 组件的另一种方法是使用 ES6 的class语法。在以下示例中,Kitten扩展了React.Component: + +```jsx +class Kitten extends React.Component { + constructor(props) { + super(props); + } + + render() { + return ( +

Hi

+ ); + } +} +``` + +这将创建一个 ES6 类Kitten,它扩展了React.Component类。因此,Kitten类现在可以访问许多有用的 React 功能,例如本地状态和生命周期钩子。如果你还不熟悉这些术语,请不要担心,在以后的挑战中我们将更详细地介绍它们。 +另请注意,Kitten类中定义了一个调用super()方法的constructor。它使用super()调用父类的构造函数,即本例中的React.Component。构造函数是使用class关键字创建的特殊方法,它用在实例初始化之前。最佳做法是在组件的constructor里调用super,并将props传递给它们,这样可以保证组件能够正确地初始化。现在,你只需要知道这是标准的做法。很快你会看到构造函数的其他用途以及props。 +
## Instructions -
MyComponent是使用类语法在代码编辑器中定义的。完成编写render方法,以便返回包含带有文本Hello React!h1div元素Hello React!
+
+MyComponent是使用类语法在代码编辑器中定义的。完成render方法的编写,使其返回div元素,其中包含文本内容为Hello React!h1元素。 +
## Tests
```yml tests: - - text: React组件应返回div元素。 + - text: 该 React 组件应该返回一个div元素。 testString: assert(Enzyme.shallow(React.createElement(MyComponent)).type() === 'div'); - - text: 返回的div应该在其中呈现一个h1头。 + - text: 返回的div中应该渲染一个h1标题。 testString: assert(/

.*<\/h1><\/div>/.test(Enzyme.shallow(React.createElement(MyComponent)).html())); - - text: h1标头应该包含字符串Hello React! 。 + - text: h1标题中应该包含字符串Hello React!。 testString: assert(Enzyme.shallow(React.createElement(MyComponent)).html() === '

Hello React!

'); ``` @@ -35,6 +56,7 @@ tests:
```jsx + class MyComponent extends React.Component { constructor(props) { super(props); @@ -47,7 +69,6 @@ class MyComponent extends React.Component { // change code above this line } }; - ```
@@ -57,7 +78,7 @@ class MyComponent extends React.Component {
```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
@@ -67,8 +88,22 @@ console.info('after the test'); ## Solution
+ ```js -// solution required +class MyComponent extends React.Component { + constructor(props) { + super(props); + } + render() { + // change code below this line + return ( +
+

Hello React!

+
+ ); + // change code above this line + } +}; ``` -/section> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-simple-jsx-element.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-simple-jsx-element.chinese.md index eb9d7179d2..639de1cd24 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-simple-jsx-element.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-simple-jsx-element.chinese.md @@ -3,15 +3,23 @@ id: 587d7dbc367417b2b2512bb1 title: Create a Simple JSX Element challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 创建一个简单的JSX元素 +forumTopicId: 301390 +localeTitle: 创建一个简单的 JSX 元素 --- ## Description -
简介: React是由Facebook创建和维护的开源视图库。它是渲染现代Web应用程序的用户界面(UI)的绝佳工具。 React使用名为JSX的JavaScript语法扩展,允许您直接在JavaScript中编写HTML。这有几个好处。它允许您在HTML中使用JavaScript的完整程序功能,并有助于保持代码的可读性。在大多数情况下,JSX类似于您已经学过的HTML,但是在这些挑战中将会涉及一些关键差异。例如,因为JSX是JavaScript的语法扩展,所以您实际上可以直接在JSX中编写JavaScript。要做到这一点,您只需在花括号中包含您希望被视为{ '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的特定部分。
+
+简介:React 是由 Facebook 创建和维护的开源视图库。它是渲染当代 Web 应用程序用户界面(UI)的绝佳工具。 +React 使用名为 JSX 的 JavaScript 语法扩展,允许你直接在 JavaScript 中编写 HTML。这有几个好处。它允许你在 HTML 中使用 JavaScript 的完整程序功能,并有助于保持代码的可读性。在大多数情况下,JSX 类似于你已经学过的 HTML,但是在这些挑战中将会涉及一些关键差异。 +例如,因为 JSX 是 JavaScript 的语法扩展,所以你实际上可以直接在 JSX 中编写 JavaScript。要做到这一点,你只需在花括号中包含你希望被视为 JavaScript 的代码:{“这被视为 JavaScript 代码”}。请牢记这个写法,你将会在接下来的挑战中使用。 +但是,由于浏览器不能解析 JSX,因此必须将 JSX 代码编译为 JavaScript。在这个过程中,转换器 Babel 是一个很受欢迎的工具。后续挑战已经在后台引入了 Babel,你可以直接写 JSX 代码。如果你的代码不符合 JSX 语法,那么挑战中的第一个测试就不会通过。 +值得注意的是,这些挑战在底层调用ReactDOM.render(JSX, document.getElementById('root'))。这个函数调用是将你的 JSX 置于 React 自己的轻量级 DOM 中。然后,React 使用自己的 DOM 快照来优化更新实际 DOM 的特定部分。 +
## Instructions -
说明:当前代码使用JSX将div元素分配给常量JSX 。用h1元素替换div并添加文本Hello JSX!在里面。
+
+说明:当前代码使用 JSX 将div元素赋值给常量JSX。将div替换为h1元素,并在其中添加文本Hello JSX!。 +
## Tests
@@ -20,7 +28,7 @@ localeTitle: 创建一个简单的JSX元素 tests: - text: 常量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:
```jsx + const JSX =
; ``` @@ -44,7 +53,7 @@ const JSX =
;
```js -console.info('after the test'); +ReactDOM.render(JSX, document.getElementById('root')) ```
@@ -54,8 +63,9 @@ console.info('after the test'); ## Solution
+ ```js -// solution required +const JSX =

Hello JSX!

; ``` -/section> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-stateful-component.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-stateful-component.chinese.md index ae1420b9a2..06159546df 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-stateful-component.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-stateful-component.chinese.md @@ -3,28 +3,41 @@ id: 5a24c314108439a4d4036170 title: Create a Stateful Component challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 创建一个有状态组件 +forumTopicId: 301391 +localeTitle: 创建一个有状态的组件 --- ## Description -
React最重要的主题之一是state 。 State包含应用程序需要了解的任何数据,这些数据可能会随时间而变化。您希望应用程序响应状态更改并在必要时显示更新的UI。 React为现代Web应用程序的状态管理提供了一个很好的解决方案。您可以通过在constructor声明组件类的state属性来在React组件中创建状态。这与初始化该组件state被创建时。 state属性必须设置为JavaScript object 。声明它看起来像这样:
this.state = {
//在这里描述你的州
您可以在组件的整个生命周期内访问state对象。您可以更新它,在UI中呈现它,并将其作为道具传递给子组件。 state对象可以像您需要的那样复杂或简单。请注意,您必须通过扩展React.Component来创建类组件,以便创建这样的state
+
+React中最重要的主题之一是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。 +
## Instructions -
代码编辑器中有一个组件试图从其state呈现name属性。但是,没有定义state 。初始化与组件stateconstructor ,并指定你的名字的属性name
+
+代码编辑器中有一个组件试图从其state中渲染一个name属性,但是state还没有定义。在constructor中使用state初始化组件,并将你的名字赋给name属性。 +
## Tests
```yml tests: - - text: StatefulComponent应该存在并呈现。 + - text: StatefulComponent应该存在并被渲染。 testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(StatefulComponent)); return mockedComponent.find('StatefulComponent').length === 1; })()); - - text: StatefulComponent应该呈现divh1元素。 + - 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:
```jsx + class StatefulComponent extends React.Component { constructor(props) { super(props); @@ -51,7 +65,6 @@ class StatefulComponent extends React.Component { ); } }; - ```
@@ -61,7 +74,7 @@ class StatefulComponent extends React.Component {
```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
@@ -71,8 +84,23 @@ console.info('after the test'); ## Solution
+ ```js -// solution required +class StatefulComponent extends React.Component { + constructor(props) { + super(props); + this.state = { + name: 'freeCodeCamp!' + } + } + render() { + return ( +
+

{this.state.name}

+
+ ); + } +}; ``` -/section> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-stateless-functional-component.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-stateless-functional-component.chinese.md index a0274ede2d..9cdd4c1a4d 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-stateless-functional-component.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/create-a-stateless-functional-component.chinese.md @@ -3,26 +3,44 @@ id: 5a24c314108439a4d4036162 title: Create a Stateless Functional Component challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 创建无状态功能组件 +forumTopicId: 301392 +localeTitle: 创建一个无状态的函数组件 --- ## Description -
组件是React的核心。 React中的所有内容都是一个组件,在这里您将学习如何创建一个组件。有两种方法可以创建React组件。第一种方法是使用JavaScript函数。以这种方式定义组件会创建无状态功能组件 。应用程序中的状态概念将在以后的挑战中介绍。现在,将无状态组件视为可以接收数据并对其进行渲染的组件,但不管理或跟踪对该数据的更改。 (我们将介绍在下一个挑战中创建React组件的第二种方法。)要创建一个带有函数的组件,您只需编写一个返回JSX或null的JavaScript函数。需要注意的一件重要事情是,React要求您的函数名称以大写字母开头。这是一个在JSX中分配HTML类的无状态功能组件的示例:
//被转换后,<div>将有一个CSS类'customClass'
const DemoComponent = function(){
回来(
<div className ='customClass'/>
);
};
因为JSX组件代表HTML,所以您可以将几个组件放在一起以创建更复杂的HTML页面。这是React提供的组件架构的关键优势之一。它允许您从许多独立的,独立的组件中组合UI。这使得构建和维护复杂的用户界面变得更加容易。
+
+组件是 React 的核心。React 中的所有内容都是一个组件,在这里你将学习如何创建一个组件。 +有两种方法可以创建 React 组件。第一种方法是使用 JavaScript 函数。以这种方式定义组件会创建无状态功能组件。应用程序中的状态概念将在以后的挑战中介绍。目前,可以将无状态组件视为可以接收数据并对其进行渲染的组件,但是它不管理或跟踪对数据的更改,我们将在下一次挑战中介绍创建 React 组件的第二种方法。 +要用函数创建组件,只需编写一个返回 JSX 或null的 JavaScript 函数。需要注意的一点是,React 要求你的函数名以大写字母开头。下面是一个无状态功能组件的示例,该组件在 JSX 中分配一个 HTML 的 class: + +```jsx +// After being transpiled, the
will have a CSS class of 'customClass' +const DemoComponent = function() { + return ( +
+ ); +}; +``` + +因为 JSX 组件代表 HTML,所以你可以将几个组件放在一起以创建更复杂的 HTML 页面,这是 React 提供的组件架构的关键优势之一,它允许你用许多独立的组件组成 UI。这使得构建和维护复杂的用户界面变得更加容易。 +
## Instructions -
代码编辑器有一个名为MyComponent的函数。完成此函数,以便返回包含一些文本字符串的单个div元素。 注意:该文本被视为div元素的子元素,因此您将无法使用自闭合标记。
+
+代码编辑器中有一个名为MyComponent的函数。完成此函数,使其返回包含一些文本字符串的单个div元素。 +注意: 文本被视为是div的子元素,因此你将不能使用自闭合标签。 +
## Tests
```yml tests: - - text: 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 } - ```
@@ -52,7 +69,7 @@ const MyComponent = function() {
```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
@@ -62,8 +79,17 @@ console.info('after the test'); ## Solution
+ ```js -// solution required +const MyComponent = function() { + // change code below this line + return ( +
+ Demo Solution +
+ ); + // change code above this line +} ``` -/section> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/define-an-html-class-in-jsx.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/define-an-html-class-in-jsx.chinese.md index 6ea1340ae6..db81f1f309 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/define-an-html-class-in-jsx.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/define-an-html-class-in-jsx.chinese.md @@ -3,15 +3,22 @@ id: 5a24c314108439a4d4036160 title: Define an HTML Class in JSX challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 在JSX中定义HTML类 +forumTopicId: 301393 +localeTitle: 在 JSX 中定义一个 HTML Class --- ## Description -
现在您已经开始编写JSX了,您可能想知道它与HTML的区别。到目前为止,似乎HTML和JSX完全相同。 JSX的一个关键区别是你不能再使用单词class来定义HTML类。这是因为class是JavaScript中的保留字。相反,JSX使用className 。事实上,JSX中所有HTML属性和事件引用的命名约定都变成了camelCase。例如,JSX中的单击事件是onClick ,而不是onclick 。同样, onchange变为onChange 。虽然这是一个微妙的差异,但重要的是要记住前进。
+
+现在你已经习惯了编写 JSX,你可能想知道它与 HTML 有什么不同。 +到目前为止,HTML 和 JSX 似乎完全相同。 +JSX 的一个关键区别是你不能再使用class这个单词来定义 HTML 的 class 名。这是因为class是 JavaScript 中的关键字。JSX 使用className代替。 +事实上,JSX 中所有 HTML 属性和事件引用的命名约定都变成了驼峰式。例如,JSX 中的单击事件是 onClick,而不是 onclick。同样,onchange变成了onChange。虽然这是一个微妙的差异,但请你一定要记住。 +
## Instructions -
将一个myDivmyDiv JSX代码中提供的div
+
+将 classmyDiv 应用于 JSX 提供的div上。 +
## Tests
@@ -20,7 +27,7 @@ localeTitle: 在JSX中定义HTML类 tests: - text: 常量JSX应该返回一个div元素。 testString: assert.strictEqual(JSX.type, 'div'); - - text: div有一类myDiv 。 + - text: div有一个myDivclass。 testString: assert.strictEqual(JSX.props.className, 'myDiv'); ``` @@ -38,7 +45,6 @@ const JSX = (

Add a class to this div

); - ``` @@ -48,7 +54,7 @@ const JSX = (
```js -console.info('after the test'); +ReactDOM.render(JSX, document.getElementById('root')) ```
@@ -58,8 +64,12 @@ console.info('after the test'); ## Solution
+ ```js -// solution required +const JSX = ( +
+

Add a class to this div

+
); ``` -/section> +
diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/give-sibling-elements-a-unique-key-attribute.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/give-sibling-elements-a-unique-key-attribute.chinese.md index 8b0cc6e42a..bba7a998e9 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/give-sibling-elements-a-unique-key-attribute.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/give-sibling-elements-a-unique-key-attribute.chinese.md @@ -3,31 +3,38 @@ id: 5a24c314108439a4d403618b title: Give Sibling Elements a Unique Key Attribute challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 为兄弟元素提供唯一的键属性 +forumTopicId: 301394 +localeTitle: 给同级元素一个唯一的键属性 --- ## Description -
最后一项挑战展示了如何使用map方法根据用户输入动态呈现多个元素。但是,这个例子中缺少一个重要的部分。创建元素数组时,每个元素都需要将key属性设置为唯一值。 React使用这些键来跟踪添加,更改或删除的项目。当以任何方式修改列表时,这有助于使重新呈现过程更有效。请注意,键只需要在兄弟元素之间是唯一的,它们在您的应用程序中不需要是全局唯一的。
+
+上一个挑战展示了如何使用map方法根据用户输入动态渲染多个元素。然而,这个例子中缺少一个重要的部分。创建元素数组时,每个元素都需要一个设置为唯一值的key属性。React 使用这些键来跟踪哪些项目被添加、更改或删除。这有助于在以任何方式修改列表时提高重新渲染过程的效率。请注意,键只需要在同级元素之间是唯一的,它们不需要在应用程序中是全局唯一的。 +
## Instructions -
代码编辑器有一个包含一些前端框架的数组和一个名为Frameworks()的无状态功能组件。 Frameworks()需要将数组映射到无序列表,就像上一次挑战一样。完成编写map回调以返回frontEndFrameworks数组中每个框架的li元素。这一次,请确保为每个li一个key属性,设置为唯一值。通常,您希望使键成为唯一标识要呈现的元素的键。作为最后的手段,可以使用数组索引,但通常您应该尝试使用唯一标识。
+
+代码编辑器有一个数组,它包含一些前端框架和一个名为Frameworks()的无状态函数组件。Frameworks()需要将数组映射到无序列表,就像上一个挑战一样。完成map回调,为frontEndFrameworks数组中的每个框架返回一个li元素。这次,确保给每个likey属性设置一个唯一的值。 +通常,你希望使 key 能唯一标识要渲染的元素。作为最后的手段,可以使用数组索引,但通常你应该尝试使用唯一标识。 +
## Tests
```yml tests: - - text: 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:
```jsx + const frontEndFrameworks = [ 'React', 'Angular', @@ -59,7 +67,6 @@ function Frameworks() {
); }; - ``` @@ -69,7 +76,7 @@ function Frameworks() {
```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
@@ -79,8 +86,28 @@ console.info('after the test'); ## Solution
+ ```js -// solution required +const frontEndFrameworks = [ + 'React', + 'Angular', + 'Ember', + 'Knockout', + 'Backbone', + 'Vue' +]; + +function Frameworks() { + const renderFrameworks = frontEndFrameworks.map((fw, i) =>
  • {fw}
  • ); + return ( +
    +

    Popular Front End JavaScript Frameworks

    +
      + {renderFrameworks} +
    +
    + ); +}; ``` -/section> +
    diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/introducing-inline-styles.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/introducing-inline-styles.chinese.md index 3f5af1811f..4a7f85ebc6 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/introducing-inline-styles.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/introducing-inline-styles.chinese.md @@ -3,24 +3,35 @@ id: 5a24c314108439a4d4036181 title: Introducing Inline Styles challengeType: 6 isRequired: false -videoUrl: '' +forumTopicId: 301395 localeTitle: 介绍内联样式 --- ## Description -
    还有其他复杂的概念可以为您的React代码添加强大的功能。但是你可能想知道如何设置你在React中创建的那些JSX元素的更简单的问题。您可能知道它与使用HTML完全不同,因为您将类应用于JSX元素的方式 。如果从样式表导入样式,它就没有太大的不同。使用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中为我们应用正确的属性名称。
    +
    +还有其他复杂的概念可以为你的 React 代码增加强大的功能。但是,你可能会想知道更简单的问题,比如:如何对在 React 中创建的 JSX 元素进行风格化。你可能知道,由于将 class 应用于 JSX 元素的方式与 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 中为我们应用正确的属性名称。 +
    ## Instructions -
    在代码编辑器中为div添加style属性,为文本提供红色和字体大小为72px的颜色。请注意,您可以选择将字体大小设置为数字,省略单位“px”,或将其写为“72px”。
    +
    +在代码编辑器的div中添加一个style属性,使文本颜色为红色,字体大小为 72px。 +请注意,你可以选择将字体大小设置为数字,省略单位 "px",或者将其写为 "72px"。 +
    ## Tests
    ```yml tests: - - text: 该组件应呈现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:
    ```jsx + class Colorful extends React.Component { render() { return ( @@ -52,7 +64,7 @@ class Colorful extends React.Component {
    ```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
    @@ -62,8 +74,16 @@ console.info('after the test'); ## Solution
    + ```js -// solution required +class Colorful extends React.Component { + render() { + return ( +
    Big Red
    + ); + } +}; + ``` -/section> +
    diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/learn-about-self-closing-jsx-tags.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/learn-about-self-closing-jsx-tags.chinese.md index 56f411850b..50b7cc94fa 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/learn-about-self-closing-jsx-tags.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/learn-about-self-closing-jsx-tags.chinese.md @@ -3,15 +3,23 @@ id: 5a24c314108439a4d4036161 title: Learn About Self-Closing JSX Tags challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 了解自我关闭JSX标签 +forumTopicId: 301396 +localeTitle: 了解自动闭合的 JSX 标记 --- ## Description -
    到目前为止,您已经看到JSX与HTML的不同之处在于使用classNameclass来定义HTML类。 JSX与HTML的另一个重要方式是自闭标签。在HTML中,几乎所有标签都有开始和结束标签: <div></div> ;结束标记在您要关闭的标记名称之前始终具有正斜杠。但是,HTML中有一些称为“自闭标签”的特殊实例,或者在另一个标签可以启动之前不需要开始和结束标签的标签。例如,换行标记可以写成<br><br /> ,但不应该写为<br></br> ,因为它不包含任何内容。在JSX中,规则略有不同。任何JSX元素都可以使用自闭合标记编写,并且必须关闭每个元素。例如,换行标记必须始终写为<br />才能成为可以转换的有效JSX。另一方面, <div>可以写为<div /><div></div> 。不同之处在于,在第一个语法版本中,无法在<div />包含任何内容。您将在以后的挑战中看到,在呈现React组件时,此语法非常有用。
    +
    +到目前为止,你已经看到 JSX 与 HTML 的不同之处在于使用className和使用class来定义 HTML 的 class。 +JSX 不同于 HTML 的另一个重要方面是自闭合标签。 +在HTML中,几乎所有的标签都有一个开始和结束标签:<div></div>,结束标签在你要关闭的标签名之前始终具有正斜杠。但是,HTML 中有一些称为“自闭合标签”的特殊实例,它们在另一个标签开始之前,不需要开始和结束标签都存在。 +例如,换行标签可以写成<br>或者<br />,但是不应该写成<br></br>,因为它不包含任何内容。 +在 JSX 中,规则略有不同。任何 JSX 元素都可以使用自闭合标签编写,并且每个元素都必须关闭。例如,换行标签必须始终编写为<br />。另一方面<div>可以写成<div />或者<div></div>。不同之处在于,在第一个语法版本中,无法在<div />中包含任何内容。在后面的挑战中你会发现,这种语法在渲染 React 组件时非常有用。 +
    ## Instructions -
    修复代码编辑器中的错误,使其成为有效的JSX并成功转换。确保您不更改任何内容 - 您只需要在需要的地方关闭标签。
    +
    +修复代码编辑器中的错误,使其成为有效的 JSX 并成功转换。确保你不更改任何内容--你只需要在需要的地方关闭标签。 +
    ## Tests
    @@ -22,7 +30,7 @@ tests: testString: assert.strictEqual(JSX.type, 'div'); - text: 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 = (
    ```js -console.info('after the test'); +ReactDOM.render(JSX, document.getElementById('root')) ```
    @@ -64,8 +72,17 @@ console.info('after the test'); ## Solution
    + ```js -// solution required +const JSX = ( +
    + {/* change code below this line */} +

    Welcome to React!


    +

    Be sure to close all tags!

    +
    + {/* change code above this line */} +
    +); ``` -/section> +
    diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/optimize-re-renders-with-shouldcomponentupdate.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/optimize-re-renders-with-shouldcomponentupdate.chinese.md index 95eec6271a..d5450be658 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/optimize-re-renders-with-shouldcomponentupdate.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/optimize-re-renders-with-shouldcomponentupdate.chinese.md @@ -3,28 +3,33 @@ id: 5a24c314108439a4d4036180 title: Optimize Re-Renders with shouldComponentUpdate challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 使用shouldComponentUpdate优化重新渲染 +forumTopicId: 301398 +localeTitle: 使用 shouldComponentUpdate 优化重新渲染 --- ## Description -
    到目前为止,如果任何组件接收到新的state或新的props ,它会重新呈现自己及其所有子项。这通常没问题。但是React提供了一个生命周期方法,您可以在子组件接收新stateprops时调用它,并特别声明组件是否应该更新。方法是shouldComponentUpdate() ,它将nextPropsnextState作为参数。此方法是优化性能的有用方法。例如,默认行为是您的组件在收到新props时重新渲染,即使props未更改。您可以使用shouldComponentUpdate()通过比较props来防止这种情况。该方法必须返回一个boolean值,告诉React是否更新组件。您可以将当前道具( this.props )与下一个道具( nextProps )进行比较,以确定是否需要更新,并相应地返回truefalse
    +
    +到目前为止,如果任何组件接收到新的state或新的props,它会重新渲染自己及其所有子组件。这通常是好的。但是 React 提供了一种生命周期方法,当子组件接收到新的stateprops时,你可以调用该方法,并特别声明组件是否应该更新。方法是shouldComponentUpdate(),它将nextPropsnextState作为参数。 +这种方法是优化性能的有效方法。例如,默认行为是,当组件接收到新的props时,即使props没有改变,它也会重新渲染。你可以通过使用shouldComponentUpdate()比较props来防止这种情况。该方法必须返回一个布尔值,该值告诉 React 是否更新组件。你可以比较当前的 props(this.props)和下一个 props(nextProps),以确定你是否需要更新,并相应地返回truefalse。 +
    ## Instructions -
    shouldComponentUpdate()方法添加到名为OnlyEvens的组件中。目前,此方法返回true因此每次收到新props时, OnlyEvens重新渲染。修改方法,以便OnlyEvens更新仅当value的新道具的是偶数。单击“ Add按钮,在触发其他生命周期挂钩时,在浏览器控制台中查看事件的顺序。
    +
    +shouldComponentUpdate()方法添加到名为OnlyEvens的组件中。目前,该方法返回true,因此每次收到新的props时,OnlyEvens都会重新渲染。修改该方法,以便OnlyEvens仅在其新 props 的value为偶数时更新。单击Add按钮,在触发其他生命周期钩子时,在浏览器控制台中查看事件的顺序。 +
    ## Tests
    ```yml tests: - - text: 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 === ''

    1000

    '' && secondValue === ''

    10

    ''); }; ' - - text: 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 { ); } }; - ```
    @@ -90,7 +91,7 @@ class Controller extends React.Component {
    ```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
    @@ -100,8 +101,48 @@ console.info('after the test'); ## Solution
    + ```js -// solution required +class OnlyEvens extends React.Component { + constructor(props) { + super(props); + } + shouldComponentUpdate(nextProps, nextState) { + console.log('Should I update?'); + // change code below this line + return nextProps.value % 2 === 0; + // change code above this line + } + componentDidUpdate() { + console.log('Component re-rendered.'); + } + render() { + return

    {this.props.value}

    + } +}; + +class Controller extends React.Component { + constructor(props) { + super(props); + this.state = { + value: 0 + }; + this.addValue = this.addValue.bind(this); + } + addValue() { + this.setState({ + value: this.state.value + 1 + }); + } + render() { + return ( +
    + + +
    + ); + } +}; ``` -/section> +
    diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/override-default-props.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/override-default-props.chinese.md index 9200dfd5b0..84014d09ba 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/override-default-props.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/override-default-props.chinese.md @@ -3,26 +3,31 @@ id: 5a24c314108439a4d403616c title: Override Default Props challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 覆盖默认道具 +forumTopicId: 301399 +localeTitle: 覆盖默认的 Props --- ## Description -
    设置默认道具的能力是React中的一个有用功能。覆盖默认道具的方法是显式设置组件的prop值。
    +
    +在 React 中,设置默认的 props 是一个很有用的特性,显式设置组件的 prop 值即可覆盖默认 props。 +
    ## Instructions -
    ShoppingCart组件现在呈现子组件Items 。此Items组件的默认prop quantity设置为整数0 。通过为quantity传递值10来覆盖默认支柱。 注意:请记住,向组件添加prop的语法与添加HTML属性的方式类似。但是,由于quantity的值是一个整数,因此它不会引用引号,但应该用大括号括起来。例如, {100} 。此语法告诉JSX将大括号内的值直接解释为JavaScript。
    +
    +ShoppingCart组件现在渲染了一个子组件Items。该Items组件有一个默认quantityprop,其值被设置为整数0。通过传入数值10来覆盖quantity的默认 prop。 +注意: 请记住,向组件添加 prop 的语法与添加 HTML 属性类似。但是,由于quantity的值是整数,所以它不会加引号,但应该用花括号括起来,例如{100}。这个语法告诉 JSX 直接将花括号中的值解释为 JavaScript。 +
    ## Tests
    ```yml tests: - - text: 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(''); })());" ``` @@ -53,7 +58,6 @@ class ShoppingCart extends React.Component { { /* change code above this line */ } } }; - ``` @@ -63,7 +67,7 @@ class ShoppingCart extends React.Component {
    ```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
    @@ -73,8 +77,26 @@ console.info('after the test'); ## Solution
    + ```js -// solution required +const Items = (props) => { + return

    Current Quantity of Items in Cart: {props.quantity}

    +} + +Items.defaultProps = { + quantity: 0 +} + +class ShoppingCart extends React.Component { + constructor(props) { + super(props); + } + render() { + { /* change code below this line */ } + return + { /* change code above this line */ } + } +}; ``` -/section> +
    diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/pass-a-callback-as-props.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/pass-a-callback-as-props.chinese.md index c59029cad2..e3a012e3ab 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/pass-a-callback-as-props.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/pass-a-callback-as-props.chinese.md @@ -3,30 +3,35 @@ id: 5a24c314108439a4d403617b title: Pass a Callback as Props challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 将回调作为道具传递 +forumTopicId: 301400 +localeTitle: 传递回调作为 Props --- ## Description -
    您可以将state作为道具传递给子组件,但您不仅限于传递数据。您还可以将处理函数或在React组件上定义的任何方法传递给子组件。这是允许子组件与其父组件交互的方式。您可以将方法传递给孩子,就像常规道具一样。它被分配了一个名称,您可以在子组件中的this.props下访问该方法名称。
    +
    +你可以将state作为 props 传递给子组件,但不仅限于传递数据。你也可以将处理函数或在 React 组件中定义的任何方法传递给子组件。这就是允许子组件与父组件交互的方式。你可以把方法像普通 prop 一样传递给子组件,它会被分配一个名字,你可以在子组件中的this.props下访问该方法的名字。 +
    ## Instructions -
    代码编辑器中列出了三个组件。 MyApp组件是将呈现GetInputRenderInput子组件的父组件。将GetInput组件添加到MyApp的render方法,然后从MyAppstate向它传递一个名为input的prop,该input分配给inputValue 。还要创建一个名为handleChange的prop,并将输入处理程序handleChange给它。接下来,将RenderInput添加到MyApp的render方法,然后创建一个名为input的prop,并将inputValuestate传递给它。完成后,您将能够在GetInput组件中input字段,然后通过props调用其父级中的处理程序方法。这将更新父级state的输入,该输入作为props传递给两个子级。观察数据如何在组件之间流动以及单个事实源如何保持父组件的state 。不可否认,这个例子有点人为,但应该用来说明如何在React组件之间传递数据和回调。
    +
    +代码编辑器中列出了三个组件。MyApp是父组件,GetInputRenderInput是它的子组件。将GetInput组件添加到MyApp的 render 方法,然后将MyAppstate中的inputValue传入名为input的 prop。还要创建一个名为handleChange的 prop,并将输入处理程序handleChange传递给它。 +接下来,将RenderInput添加到MyApp中的 render 方法中,然后创建一个名为input的 prop,并将state中的inputValue传递给它。完成后,你将能够在GetInput组件中的input字段中键入内容,然后该组件通过 props 调用其父组件中的处理函数方法。这将更新处于父组件state中的 input,该 input 将作为 props 传递给两个子组件。观察数据如何在组件之间流动,以及单一数据源如何保持父组件state。诚然,这个示例有点做作,但是应该能用来说明数据和回调是如何在 React 组件之间传递的。 +
    ## Tests
    ```yml tests: - - text: 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,并包含一个修改MyAppstate 的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组件应该接收MyAppstate 属性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 {
    ```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
    @@ -112,8 +116,63 @@ console.info('after the test'); ## Solution
    + ```js -// solution required +class MyApp extends React.Component { + constructor(props) { + super(props); + this.state = { + inputValue: '' + } + this.handleChange = this.handleChange.bind(this); + } + handleChange(event) { + this.setState({ + inputValue: event.target.value + }); + } + render() { + return ( +
    + + +
    + ); + } +}; + +class GetInput extends React.Component { + constructor(props) { + super(props); + } + render() { + return ( +
    +

    Get Input:

    + +
    + ); + } +}; + +class RenderInput extends React.Component { + constructor(props) { + super(props); + } + render() { + return ( +
    +

    Input Render:

    +

    {this.props.input}

    +
    + ); + } +}; ``` -/section> +
    diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/pass-an-array-as-props.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/pass-an-array-as-props.chinese.md index 119f1edb02..ca712c8262 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/pass-an-array-as-props.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/pass-an-array-as-props.chinese.md @@ -3,34 +3,50 @@ id: 5a24c314108439a4d403616a title: Pass an Array as Props challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 将数组作为道具传递 +forumTopicId: 301401 +localeTitle: 传递一个数组作为 Props --- ## Description -
    最后一项挑战演示了如何将信息从父组件传递到子组件作为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 中渲染数组数据的其他常用方法。 +
    ## Instructions -
    代码编辑器中有ListToDo组件。从ToDo组件渲染每个List ,传入分配给待办任务数组的tasks属性,例如["walk dog", "workout"] 。然后在List组件中访问此tasks数组,在p元素中显示其值。使用join(", ")以逗号分隔列表的形式显示p元素中的props.tasks数组。今天的列表应该至少有2个任务,明天应该至少有3个任务。
    +
    +代码编辑器中有ListToDo组件。在ToDo组件中渲染每个List时,传入tasks属性并将其分配给待办任务数组,例如["walk dog", "workout"]。然后访问List组件中的tasks数组,在p元素中显示其值。使用join(", ")props.tasks数组作为逗号分隔列表显示在p元素中。今天的列表应该至少有 2 个任务,明天应该至少有 3 个任务。 +
    ## Tests
    ```yml tests: - - text: 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:
    ```jsx -const List= (props) => { +const List = (props) => { { /* change code below this line */ } return

    {}

    { /* change code above this line */ } @@ -67,7 +83,6 @@ class ToDo extends React.Component { ); } }; - ```
    @@ -77,7 +92,7 @@ class ToDo extends React.Component {
    ```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
    @@ -87,8 +102,28 @@ console.info('after the test'); ## Solution
    + ```js -// solution required +const List= (props) => { + return

    {props.tasks.join(', ')}

    +}; + +class ToDo extends React.Component { + constructor(props) { + super(props); + } + render() { + return ( +
    +

    To Do Lists

    +

    Today

    + +

    Tomorrow

    + +
    + ); + } +}; ``` -/section> +
    diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/pass-props-to-a-stateless-functional-component.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/pass-props-to-a-stateless-functional-component.chinese.md index b75afa1402..107898c623 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/pass-props-to-a-stateless-functional-component.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/pass-props-to-a-stateless-functional-component.chinese.md @@ -3,31 +3,52 @@ id: 5a24c314108439a4d4036169 title: Pass Props to a Stateless Functional Component challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 将道具传递给无状态功能组件 +forumTopicId: 301402 +localeTitle: 将 Props 传递给无状态函数组件 --- ## Description -
    之前的挑战涉及在React中创建和组合JSX元素,功能组件和ES6样式类组件的很多内容。有了这个基础,现在是时候看看React中常见的另一个特性: 道具 。在React中,您可以将props或属性传递给子组件。假设您有一个App组件,它呈现一个名为Welcome的子组件,它是一个无状态功能组件。您可以通过编写以下方式传递Welcome user属性:
    <应用>
    <Welcome user ='Mark'/>
    </应用>
    您使用React提供支持的自定义HTML属性 ,以将属性user传递给组件Welcome 。由于Welcome是一个无状态功能组件,因此它可以访问此值,如下所示:
    const Welcome =(props)=> <h1> Hello,{props.user}!</ h1>
    调用此值props是标准的,在处理无状态函数组件时,您基本上将其视为返回JSX的函数的参数。您可以在函数体中访问参数的值。使用类组件,您会发现这有点不同。
    +
    +之前的挑战涵盖了关于在 React 中创建和组合 JSX 元素、函数组件和 ES6 风格的类组件的很多内容。有了这个基础,现在是时候看看 React 中的另一个常见特性 props 了。在 React 中,你可以将属性传递给子组件。假设你有一个App组件,该组件渲染了一个名为Welcome的子组件,它是一个无状态函数组件。你可以通过以下方式给Welcome传递一个user属性: + +```jsx + + + +``` + +使用自定义 HTML 属性,React 支持将属性user传递给组件Welcome。由于Welcome是一个无状态函数组件,它可以像这样访问该值: + +```jsx +const Welcome = (props) =>

    Hello, {props.user}!

    +``` + +调用props这个值是常见做法,当处理无状态函数组件时,你基本上可以将其视为返回 JSX 的函数的参数。这样,你就可以在函数体中访问该值。但对于类组件,访问方式会略有不同。 +
    ## Instructions -
    代码编辑器中有CalendarCurrentDate组件。当渲染CurrentDateCalendar组件,通过在属性date分配给从JavaScript的当前日期Date对象。然后在CurrentDate组件中访问此prop ,在p标签中显示其值。请注意,对于要作为JavaScript计算的prop值,它们必须用大括号括起来,例如date={Date()}
    +
    +代码编辑器中有CalendarCurrentDate组件。从Calendar组件渲染CurrentDate时,从 JavaScript 的Date对象分配当前日期,并将其作为date属性传入。然后访问CurrentDate组件的prop,并在p标签中显示其值。请注意,要将prop的值视为 JavaScript,必须将它们括在花括号中,例如date={Date()}。 +
    ## Tests
    ```yml tests: - - text: 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: CurrentDatedate道具应包含一串文本。 - 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: CurrentDatedate属性应该包含一段文本字符串。 + 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(//.test(code.replace(/\s/g, ''))) + - text: 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:
    ```jsx + const CurrentDate = (props) => { return (
    @@ -64,7 +86,6 @@ class Calendar extends React.Component { ); } }; - ```
    @@ -74,7 +95,7 @@ class Calendar extends React.Component {
    ```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
    @@ -84,8 +105,33 @@ console.info('after the test'); ## Solution
    + ```js -// solution required +const CurrentDate = (props) => { + return ( +
    + { /* change code below this line */ } +

    The current date is: {props.date}

    + { /* change code above this line */ } +
    + ); +}; + +class Calendar extends React.Component { + constructor(props) { + super(props); + } + render() { + return ( +
    +

    What date is it?

    + { /* change code below this line */ } + + { /* change code above this line */ } +
    + ); + } +}; ``` -/section> +
    diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/pass-state-as-props-to-child-components.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/pass-state-as-props-to-child-components.chinese.md index 8ba19224d4..42bc1ff405 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/pass-state-as-props-to-child-components.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/pass-state-as-props-to-child-components.chinese.md @@ -3,26 +3,32 @@ id: 5a24c314108439a4d403617a title: Pass State as Props to Child Components challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 将状态作为道具传递给子组件 +forumTopicId: 301403 +localeTitle: 将 State 作为 Props 传递给子组件 --- ## Description -
    您看到了许多在先前的挑战中将道具传递给子JSX元素和子React组件的示例。您可能想知道这些道具来自哪里。一种常见的模式是让一个有状态的组件包含对您的应用程序很重要的state ,然后呈现子组件。您希望这些组件可以访问该state某些部分,这些部分作为props传递。例如,您可能有一个App组件可以呈现Navbar以及其他组件。在您的App ,您的state包含大量用户信息,但Navbar只需要访问用户的用户名,以便显示它。您将该state作为prop传递给Navbar组件。这种模式说明了React中的一些重要范例。第一种是单向数据流 。状态沿着应用程序组件树的一个方向流动,从有状态父组件到子组件。子组件仅接收所需的状态数据。第二,复杂的有状态应用程序可以分解为几个或者一个有状态的组件。其余组件只是从父级接收状态作为props,并从该状态呈现UI。它开始创建一个分离,其中状态管理在代码的一部分中处理,而UI在另一部分中呈现。将状态逻辑与UI逻辑分离的原则是React的关键原则之一。当它被正确使用时,它使复杂的有状态应用程序的设计更容易管理。
    +
    +在之前的挑战中,你看到了很多将 props 传递给子 JSX 元素和子 React 组件的例子。你可能想知道那些 props 是从哪里来的。一个常见的模式是:有状态组件中包含对应用程序很重要的state,然后用它渲染子组件。你希望这些组件能够访问该state的某些部分,就把这些部分作为 props 传入。 +例如,也许你有一个App组件可以渲染Navbar以及其他组件。在你的App中,你的state中包含大量用户信息,但是Navbar只需要访问用户的用户名就可以显示出来,这时你将该state作为一个 prop 传递给Navbar组件即可。 +这个模式说明了 React 中的一些重要范例。第一个是单向数据流,state 沿着应用程序组件树的一个方向流动,从有状态父组件到子组件,子组件只接收它们需要的 state 数据。第二,复杂的有状态应用程序可以分解成几个,或者可能是一个单一的有状态组件。其余组件只是从父组件简单的接收 state 作为 props,并从该 state 渲染 UI。它开始创建一种分离,在这种分离中,state 管理在代码的一部分中处理,而 UI 渲染在另一部分中处理。将 state 逻辑与 UI 逻辑分离是 React 的关键原则之一。当它被正确使用时,它使得复杂的、有状态的应用程序的设计变得更容易管理。 +
    ## Instructions -
    MyApp组件是有状态的,并将Navbar组件呈现为子组件。将name属性的state向下传递给子组件,然后在h1标记中显示该name ,该nameNavbar render方法的一部分。
    +
    +MyApp组件是有状态的,它将Navbar组件渲染成它的为子组件。将MyApp组件state中的name属性向下传递给子组件,然后在h1标签中显示namenameNavbarrender 方法的一部分。 +
    ## Tests
    ```yml tests: - - text: 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: Navbarh1元素应该呈现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 (
    -

    Hello, my name is: /* your code here */

    +

    Hello, my name is: {/* your code here */}

    ); } }; - ```
    @@ -73,7 +78,7 @@ class Navbar extends React.Component {
    ```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
    @@ -83,8 +88,35 @@ console.info('after the test'); ## Solution
    + ```js -// solution required +class MyApp extends React.Component { + constructor(props) { + super(props); + this.state = { + name: 'CamperBot' + } + } + render() { + return ( +
    + +
    + ); + } +}; +class Navbar extends React.Component { + constructor(props) { + super(props); + } + render() { + return ( +
    +

    Hello, my name is: {this.props.name}

    +
    + ); + } +}; ``` -/section> +
    diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/render-a-class-component-to-the-dom.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/render-a-class-component-to-the-dom.chinese.md index 0fded52283..66212c6627 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/render-a-class-component-to-the-dom.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/render-a-class-component-to-the-dom.chinese.md @@ -3,28 +3,34 @@ id: 5a24c314108439a4d4036167 title: Render a Class Component to the DOM challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 将类组件渲染到DOM +forumTopicId: 301404 +localeTitle: 渲染 class 组件为 Dom 树 --- ## Description -
    您可能还记得在早期挑战中使用ReactDOM API将JSX元素呈现给DOM。渲染React组件的过程看起来非常相似。过去的几个挑战集中在组件和组合上,因此渲染是在幕后为您完成的。但是,您编写的React代码都不会在不调用ReactDOM API的情况下呈现给DOM。这是对语法的更新: ReactDOM.render(componentToRender, targetNode) 。第一个参数是要呈现的React组件。第二个参数是要在其中呈现该组件的DOM节点。 React组件传递到ReactDOM.render()与JSX元素略有不同。对于JSX元素,您传入要呈现的元素的名称。但是,对于React组件,您需要使用与渲染嵌套组件相同的语法,例如ReactDOM.render(<ComponentToRender />, targetNode) 。您可以将此语法用于ES6类组件和功能组件。
    +
    +你可能还记得在早期挑战中使用 ReactDOM API 将 JSX 元素渲染到 DOM,这与渲染 React 组件的过程十分相似。过去的几个挑战主要针对组件和组合,因此渲染是在幕后为你完成的。但是,如果不调用 ReactDOM API,你编写的任何 React 代码都不会渲染到 DOM。 +以下是语法的复习:ReactDOM.render(componentToRender, targetNode)。第一个参数是要渲染的 React 组件。第二个参数是要在其中渲染该组件的 DOM 节点。 +React 组件传递到ReactDOM.render()与 JSX 元素略有不同。对于 JSX 元素,你传入的是要渲染的元素的名称。但是,对于 React 组件,你需要使用与渲染嵌套组件相同的语法,例如ReactDOM.render(<ComponentToRender />, targetNode)。你可以将此语法用于ES6类组件和函数组件。 +
    ## Instructions -
    FruitsVegetables组件都是在幕后为您定义的。将两个组件渲染为TypesOfFood组件的TypesOfFood组件,然后将TypesOfFood呈现给DOM。有一个divid='challenge-node'可供您使用。
    +
    +在后台为你定义了FruitsVegetables组件。将两个组件渲染为TypesOfFood组件的子组件,然后将TypesOfFood渲染到 DOM 节点,在这个挑战中,请渲染到 id 为challenge-nodediv中。 +
    ## Tests
    ```yml tests: - - text: 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-nodediv中。 testString: assert((function() { const html = document.getElementById('challenge-node').childNodes[0].innerHTML; return html.includes('

    Fruits:

    Non-Citrus:

    • Apples
    • Blueberries
    • Strawberries
    • Bananas

    Citrus:

    • Lemon
    • Lime
    • Orange
    • Grapefruit
    ') && html.includes('

    Vegetables:

    • Brussel Sprouts
    • Broccoli
    • Squash
    '); })()); ``` @@ -37,6 +43,7 @@ tests:
    ```jsx + class TypesOfFood extends React.Component { constructor(props) { super(props); @@ -63,6 +70,7 @@ class TypesOfFood extends React.Component {
    ```jsx + const Fruits = () => { return (
    @@ -107,8 +115,27 @@ const Vegetables = () => { ## Solution
    + ```js -// solution required +class TypesOfFood extends React.Component { + constructor(props) { + super(props); + } + render() { + return ( +
    +

    Types of Food:

    + {/* change code below this line */} + + + {/* change code above this line */} +
    + ); + } +}; + +// change code below this line +ReactDOM.render(, document.getElementById('challenge-node')); ``` -/section> +
    diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/render-conditionally-from-props.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/render-conditionally-from-props.chinese.md index f64fbf4174..47cb4f41d2 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/render-conditionally-from-props.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/render-conditionally-from-props.chinese.md @@ -3,34 +3,41 @@ id: 5a24c314108439a4d4036188 title: Render Conditionally from Props challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 从道具有条理地渲染 +forumTopicId: 301405 +localeTitle: 根据 Props 有条件地渲染 --- ## Description -
    到目前为止,您已经了解了如何使用if/else&&, null和三元运算符( condition ? expressionIfTrue : expressionIfFalse )来做出有关呈现内容和何时呈现的条件决策。但是,还有一个重要的话题要讨论,它可以让你将这些概念中的任何一个或全部与另一个强大的React功能结合起来:道具。使用props来有条件地呈现代码对于React开发人员来说非常普遍 - 也就是说,他们使用给定prop的值来自动决定渲染内容。在此挑战中,您将设置子组件以根据道具进行渲染决策。您还将使用三元运算符,但您可以看到在最后几个挑战中涵盖的其他几个概念在此上下文中可能同样有用。
    +
    +到目前为止,你已经看到了如何使用if/else&&,null和三元运算符(condition ? expressionIfTrue : expressionIfFalse)对渲染什么和何时渲染做出有条件的判定。然而,还有一个重要的话题需要讨论,让你将这些概念中的任何一个或所有概念与另一个强大的 React 功能结合起来:props。使用 props 有条件地渲染代码在 React 开发人员中很常见--也就是说:他们使用给定 prop 的值来自动决定渲染什么。 +在这个挑战中,你将设置一个子组件来根据 props 做出渲染决定。你可以使用三元运算符,但是你可以看到过去几个挑战中涵盖的其他几个概念在这种情况下可能同样有用。 +
    ## Instructions -
    代码编辑器有两个部分为您定义的组件:名为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()方法正确计算每个回合,以便用户知道他们玩过多少次。这也可以让用户知道组件实际上已经更新,以防他们连续赢两次或输两次时自己不知道。 +
    ## Tests
    ```yml tests: - - text: 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 应该使用值为1counter属性来初始化。 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 (
    @@ -84,7 +91,6 @@ class GameOfChance extends React.Component { ); } }; - ```
    @@ -94,7 +100,7 @@ class GameOfChance extends React.Component {
    ```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
    @@ -104,8 +110,49 @@ console.info('after the test'); ## Solution
    + ```js -// solution required +class Results extends React.Component { + constructor(props) { + super(props); + } + render() { + return ( +

    + { + this.props.fiftyFifty ? + 'You Win!' : + 'You Lose!' + } +

    + ) + }; +}; + +class GameOfChance extends React.Component { + constructor(props) { + super(props); + this.state = { + counter: 1 + } + this.handleClick = this.handleClick.bind(this); + } + handleClick() { + this.setState({ + counter: this.state.counter + 1 + }); + } + render() { + const expression = Math.random() >= .5; + return ( +
    + + +

    {'Turn: ' + this.state.counter}

    +
    + ); + } +}; ``` -/section> +
    diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/render-html-elements-to-the-dom.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/render-html-elements-to-the-dom.chinese.md index db439c8190..7d4e0fc0af 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/render-html-elements-to-the-dom.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/render-html-elements-to-the-dom.chinese.md @@ -3,15 +3,21 @@ id: 5a24bbe0dba28a8d3cbd4c5f title: Render HTML Elements to the DOM challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 将HTML元素渲染到DOM +forumTopicId: 301406 +localeTitle: 渲染 HTML 元素为 DOM 树 --- ## Description -
    到目前为止,您已经了解到JSX是一种在JavaScript中编写可读HTML的便捷工具。使用React,我们可以使用React的渲染API(称为ReactDOM)将此JSX直接渲染到HTML DOM。 ReactDOM提供了一种简单的方法来将React元素呈现给DOM,如下所示: ReactDOM.render(componentToRender, targetNode) ,其中第一个参数是要呈现的React元素或组件,第二个参数是DOM节点您想要将组件渲染到。正如您所料,必须在JSX元素声明之后调用ReactDOM.render() ,就像在使用它们之前必须声明变量一样。
    +
    +到目前为止,你已经了解到 JSX 是一种在 JavaScript 中编写可读 HTML 的便捷工具。在 React 中,我们可以使用它的的渲染 API(ReactDOM)将此 JSX 直接渲染到 HTML DOM。 +ReactDOM 提供了一个简单的方法来将 React 元素呈现给 DOM,如下所示:ReactDOM.render(componentToRender, targetNode),其中第一个参数是要渲染的 React 元素或组件,第二个参数是要将组件渲染到的 DOM 节点。 +如你所料,必须在 JSX 元素声明之后调用ReactDOM.render(),就像你在使用变量之前必须声明它一样。 +
    ## Instructions -
    代码编辑器有一个简单的JSX组件。使用ReactDOM.render()方法将此组件呈现给页面。您可以直接将定义的JSX元素作为第一个参数传递,并使用document.getElementById()来选择要将其渲染到的DOM节点。有一个divid='challenge-node'可供您使用。确保不要更改JSX常量。
    +
    +代码编辑器有一个简单的 JSX 组件。使用ReactDOM.render()方法将该组件渲染到页面。可以将定义好的 JSX 元素直接作为第一个参数传入,并使用document.getElementById()来选择要渲染到的 DOM 节点,在这个挑战中,请渲染到 id 为challenge-nodediv中。 +
    ## Tests
    @@ -20,11 +26,11 @@ localeTitle: 将HTML元素渲染到DOM tests: - text: 常量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 === '

    Hello World

    Lets render this to the DOM

    '); ``` @@ -56,8 +62,16 @@ const JSX = ( ## Solution
    + ```js -// solution required +const JSX = ( +
    +

    Hello World

    +

    Lets render this to the DOM

    +
    +); +// change code below this line +ReactDOM.render(JSX, document.getElementById('challenge-node')); ``` -/section> +
    diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/render-react-on-the-server-with-rendertostring.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/render-react-on-the-server-with-rendertostring.chinese.md index 174d4eaf1d..5aff452845 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/render-react-on-the-server-with-rendertostring.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/render-react-on-the-server-with-rendertostring.chinese.md @@ -3,22 +3,27 @@ id: 5a24c314108439a4d403618d title: Render React on the Server with renderToString challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 使用renderToString在服务器上渲染React +forumTopicId: 301407 +localeTitle: 用 renderToString 在服务器上渲染 React --- ## Description -
    到目前为止,您已在客户端上呈现React组件。通常,这是你将永远做的。但是,在某些用例中,在服务器上呈现React组件是有意义的。由于React是一个JavaScript视图库,您可以使用Node在服务器上运行JavaScript,这是可能的。实际上,React提供了一个renderToString()方法,您可以将其用于此目的。有两个关键原因可以解释为什么服务器上的渲染可能会在真实世界的应用程序中使用。首先,如果不这样做,你的React应用程序将包含一个相对空的HTML文件和一大堆JavaScript,当它最初加载到浏览器时。对于试图索引页面内容以便人们可以找到您的搜索引擎而言,这可能并不理想。如果在服务器上呈现初始HTML标记并将其发送到客户端,则初始页面加载包含搜索引擎可以抓取的所有页面标记。其次,这会创建更快的初始页面加载体验,因为呈现的HTML小于整个应用程序的JavaScript代码。 React仍然能够识别您的应用并在初始加载后进行管理。
    +
    +到目前为止,你已经能够在客户端上渲染 React 组件,一般来说我们都是这么做的。然而,在一些用例中,在服务器上渲染一个 React 组件是有意义的。由于 React 是一个 JavaScript 视图库,所以使用 Node 让 JavaScript 运行在服务器上是可行的。事实上,React 提供了一个可用于此目的的renderToString()方法。 +有两个关键原因可以解释为什么服务器上的渲染可能会在真实世界的应用程序中使用。首先,如果不这样做,你的 React 应用程序将包含一个代码量很少的 HTML 文件和一大堆 JavaScript,当它最初加载到浏览器时。这对于搜索引擎来说可能不太理想,因为它们试图为你的网页内容生成索引,以便人们可以找到你。如果在服务器上渲染初始 HTML 标记并将其发送到客户端,则初始页面加载的内容包含搜索引擎可以抓取的所有页面标记。其次,这创造了更快的初始页面加载体验,因为渲染的 HTML 代码量要比整个应用程序的 JavaScript 代码小。React 仍然能够识别你的应用并在初始加载后进行管理。 +
    ## Instructions -
    renderToString()方法在ReactDOMServer上提供,在此处可用作全局对象。该方法采用一个参数,它是一个React元素。使用此选项将App呈现为字符串。
    +
    +renderToString()方法由ReactDOMServer提供,在这里已为你定义成全局变量。这个方法接受一个 React 元素作为参数。用它来把App渲染成字符串。 +
    ## Tests
    ```yml tests: - - text: App组件应使用ReactDOMServer.renderToString呈现为字符串。 + - text: App组件应该使用ReactDOMServer.renderToString渲染一个字符串。 testString: getUserInput => assert(getUserInput('index').replace(/ /g,'').includes('ReactDOMServer.renderToString()') && Enzyme.mount(React.createElement(App)).children().name() === 'div'); ``` @@ -31,6 +36,7 @@ tests:
    ```jsx + class App extends React.Component { constructor(props) { super(props); @@ -51,7 +57,6 @@ class App extends React.Component { ```jsx var ReactDOMServer = { renderToString(x) { return null; } }; - ```
    @@ -60,7 +65,7 @@ var ReactDOMServer = { renderToString(x) { return null; } };
    ```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
    @@ -70,8 +75,19 @@ console.info('after the test'); ## Solution
    + ```js -// solution required +class App extends React.Component { + constructor(props) { + super(props); + } + render() { + return
    + } +}; + +// change code below this line +ReactDOMServer.renderToString(); ``` -/section> +
    diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/render-state-in-the-user-interface-another-way.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/render-state-in-the-user-interface-another-way.chinese.md index 11dd28b9cb..d8ff2f120b 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/render-state-in-the-user-interface-another-way.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/render-state-in-the-user-interface-another-way.chinese.md @@ -3,28 +3,33 @@ id: 5a24c314108439a4d4036172 title: Render State in the User Interface Another Way challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 用户界面中的渲染状态另一种方式 +forumTopicId: 301408 +localeTitle: 以另一种方式在用户界面中渲染状态 --- ## Description -
    还有另一种访问组件中state方法。在render()方法中,在return语句之前,您可以直接编写JavaScript。例如,您可以声明函数,从stateprops访问数据,对此数据执行计算,等等。然后,您可以将任何数据分配给您可以在return语句中访问的变量。
    +
    +还有另一种方法可以访问组件中的state。在render()方法中,在return语句之前,你可以直接编写 JavaScript。例如,你可以声明函数、从stateprops访问数据、对此数据执行计算等。然后,你可以将任何数据赋值给你在return语句中可以访问的变量。 +
    ## Instructions -
    MyComponent render方法中,定义一个名为nameconst ,并将其设置为等于组件state的name值。因为您可以直接在代码的这一部分编写JavaScript,所以您不必将此引用括在花括号中。接下来,在return语句中,使用变量nameh1标记中呈现此值。请记住,您需要在return语句中使用JSX语法(JavaScript的大括号)。
    +
    +在MyComponent的 render 方法中,定义一个名为name常量,并将其设置为组件state中的 name 值。因为可以直接在代码部分编写 JavaScript,所以不需要用大括号括起来。 +接下来,在 return 语句中,在h1标签中渲染变量name的值。记住,在 return 语句中需要使用 JSX 语法(用到 JavaScript 的花括号)。 +
    ## Tests
    ```yml tests: - - text: MyComponent应该有一个键name ,其freeCodeCamp值存储在其状态中。 + - text: MyComponent应该有一个键name,其值freeCodeCamp存储在其 state 中。 testString: assert(Enzyme.mount(React.createElement(MyComponent)).state('name') === 'freeCodeCamp'); - - text: MyComponent应该渲染一个包含在单个divh1标头。 + - text: MyComponent应该在div中渲染一个h1标题。 testString: assert(/

    .*<\/h1><\/div>/.test(Enzyme.mount(React.createElement(MyComponent)).html())); - - text: '渲染的h1标记应包含对{name}的引用。' + - text: 渲染的h1标签应该包含{name}的引用。 testString: getUserInput => assert(/

    \n*\s*\{\s*name\s*\}\s*\n*<\/h1>/.test(getUserInput('index'))); - - 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(firstValue === ''

    TestName

    ''); };' ``` @@ -57,7 +62,6 @@ class MyComponent extends React.Component { ); } }; - ```

    @@ -67,7 +71,7 @@ class MyComponent extends React.Component {
    ```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
    @@ -77,8 +81,28 @@ console.info('after the test'); ## Solution
    + ```js -// solution required +class MyComponent extends React.Component { + constructor(props) { + super(props); + this.state = { + name: 'freeCodeCamp' + } + } + render() { + // change code below this line + const name = this.state.name; + // change code above this line + return ( +
    + { /* change code below this line */ } +

    {name}

    + { /* change code above this line */ } +
    + ); + } +}; ``` -/section> +
    diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/render-state-in-the-user-interface.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/render-state-in-the-user-interface.chinese.md index 4a50285fcd..3ada3ddf4e 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/render-state-in-the-user-interface.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/render-state-in-the-user-interface.chinese.md @@ -3,26 +3,34 @@ id: 5a24c314108439a4d4036171 title: Render State in the User Interface challengeType: 6 isRequired: false -videoUrl: '' +forumTopicId: 301409 localeTitle: 在用户界面中渲染状态 --- ## Description -
    定义组件的初始状态后,可以在呈现的UI中显示它的任何部分。如果组件是有状态的,它将始终可以访问其render()方法中的state数据。您可以使用this.state访问数据。如果要在render方法的return中访问状态值,则必须将值括在花括号中。 State是React中组件最强大的功能之一。它允许您跟踪应用程序中的重要数据并呈现UI以响应此数据中的更改。如果您的数据发生变化,您的UI将会发生变化React使用所谓的虚拟DOM来跟踪幕后的变化。当状态数据更新时,它会触发使用该数据重新呈现组件 - 包括作为道具接收数据的子组件。 React更新实际的DOM,但仅在必要时更新。这意味着您不必担心更改DOM。您只需声明UI应该是什么样子。请注意,如果使组件有状态,则其他组件不会知道其state 。它的state是完全封装的,或者是该组件的本地状态,除非您将状态数据作为props传递给子组件。这种封装state概念非常重要,因为它允许您编写某些逻辑,然后在代码中的某个位置包含和隔离该逻辑。
    +
    +一旦定义了组件的初始 state,你就可以在要渲染的 UI 中显示它的任何部分。如果组件是有状态的,它将始终可以访问render()方法中state的数据。你就可以使用this.state访问数据。 +如果你想在 render 方法的return中访问 state 值,你必须把这个值用花括号括起来。 +state是 React 组件中最强大的特性之一,它允许你跟踪应用程序中的重要数据,并根据数据的变化渲染 UI。如果你的数据发生变化,你的 UI 也会随之改变。React 使用所谓的虚拟 DOM 来跟踪幕后的变化。当 state 数据更新时,它会使用该数据触发组件的重新渲染--包括接收 prop 数据的子组件。React 只在必要的时候更新实际的DOM,这意味着你不必担心 DOM 的变更,只需声明 UI 的外观即可。 +注意,如果组件有状态,则没有其他组件知道它的state。它的state是完全封装的,或者是局限于组件本身的,除非你将 state 数据作为props传递给子组件。封装state的概念非常重要,因为它允许你编写特定的逻辑,然后将该逻辑包含并隔离在代码中的某个位置。 +
    ## Instructions -
    在代码编辑器中, MyComponent已经是有状态的。在组件的render方法中定义h1标记,该方法从组件的状态呈现name的值。 注意: h1应该只从state呈现值而不是其他内容。在JSX中,您使用花括号{ }编写的任何代码都将被视为JavaScript。因此,要从state访问值,只需将引用括在花括号中。
    +
    +在代码编辑器中,MyComponent是一个有状态组件,在组件的 render 方法中定义一个h1标签,该方法从组件的 state 渲染name的值。 +注意: h1应该只渲染来自state的值。在 JSX 中,使用花括号{ }编写的任何代码都将被视为 JavaScript。因此,要访问state中的值,只需将引用括在花括号中即可。 +
    ## Tests
    ```yml tests: - - text: MyComponent应该有一个键name ,其freeCodeCamp值存储在其状态中。 + - text: MyComponent应该有一个键name,其值freeCodeCamp存储在其 state 中。 testString: assert(Enzyme.mount(React.createElement(MyComponent)).state('name') === 'freeCodeCamp'); - - text: MyComponent应该渲染一个包含在单个divh1标头。 + - text: MyComponent应该在div中渲染一个h1标题。 testString: assert(/

    .*<\/h1><\/div>/.test(Enzyme.mount(React.createElement(MyComponent)).html())); - - 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(firstValue === ''

    TestName

    '');};' ``` @@ -52,7 +60,6 @@ class MyComponent extends React.Component { ); } }; - ```

    @@ -62,7 +69,7 @@ class MyComponent extends React.Component {
    ```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
    @@ -72,8 +79,25 @@ console.info('after the test'); ## Solution
    + ```js -// solution required +class MyComponent extends React.Component { + constructor(props) { + super(props); + this.state = { + name: 'freeCodeCamp' + } + } + render() { + return ( +
    + { /* change code below this line */ } +

    {this.state.name}

    + { /* change code above this line */ } +
    + ); + } +}; ``` -/section> +
    diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/render-with-an-if-else-condition.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/render-with-an-if-else-condition.chinese.md index a5c3f9e61f..c51a600153 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/render-with-an-if-else-condition.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/render-with-an-if-else-condition.chinese.md @@ -3,28 +3,33 @@ id: 5a24c314108439a4d4036184 title: Render with an If-Else Condition challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 使用If-Else条件渲染 +forumTopicId: 301410 +localeTitle: 使用 If-Else 条件进行渲染 --- ## Description -
    使用JavaScript控制渲染视图的另一个应用是将呈现的元素绑定到条件。当条件为真时,一个视图呈现。当它是假的时,它是一个不同的观点。您可以使用React组件的render()方法中的标准if/else语句执行此操作。
    +
    +使用 JavaScript 控制渲染视图的另一个应用是将渲染的元素绑定到一个条件。当条件为真时,将呈现一个视图,反之,则呈现另一种视图。你可以在 React 组件的render()方法中使用的标准if/else语句来实现这一点。 +
    ## Instructions -
    MyComponent在其状态中包含一个boolean ,用于跟踪是否要在UI中显示某个元素。该button切换此值的状态。目前,它每次都呈现相同的UI。使用if/else语句重写render()方法,以便如果displaytrue ,则返回当前标记。否则,返回没有h1元素的标记。 注意:您必须编写if/else以传递测试。使用三元运算符不会通过此处。
    +
    +MyComponent 的 state 中包含一个布尔值,用于跟踪是否要在 UI 中显示某个元素。按钮切换此值的状态。目前,它每次都呈现相同的 UI。用if/else语句重写render()方法,如果displaytrue则返回当前标记。否则,返回不带h1元素的标记。 +注意: 写if/else语句才能通过测试,使用三元运算符是不会通过的。 +
    ## Tests
    ```yml tests: - - text: MyComponent应该存在并呈现。 + - text: MyComponent应该存在并被渲染。 testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return mockedComponent.find('MyComponent').length === 1; })()); - - text: 当display设置为true ,应该渲染divbuttonh1 。 + - text: 当display被设置为true时,divbuttonh1标签应该被渲染。 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 ,只应呈现divbutton 。 + - text: 当display被设置为false时,只有divbutton应该被渲染。 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 { ); } }; - ```
    @@ -71,7 +75,7 @@ class MyComponent extends React.Component {
    ```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
    @@ -81,8 +85,39 @@ console.info('after the test'); ## Solution
    + ```js -// solution required +class MyComponent extends React.Component { + constructor(props) { + super(props); + this.state = { + display: true + } + this.toggleDisplay = this.toggleDisplay.bind(this); + } + toggleDisplay() { + this.setState({ + display: !this.state.display + }); + } + render() { + // change code below this line + if (this.state.display) { + return ( +
    + +

    Displayed!

    +
    + ); + } else { + return ( +
    + +
    + ); + } + } +}; ``` -/section> +
    diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/review-using-props-with-stateless-functional-components.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/review-using-props-with-stateless-functional-components.chinese.md index cf70097420..bf49ad39dd 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/review-using-props-with-stateless-functional-components.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/review-using-props-with-stateless-functional-components.chinese.md @@ -3,30 +3,36 @@ id: 5a24c314108439a4d403616f title: Review Using Props with Stateless Functional Components challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 查看使用无状态功能组件的道具 +forumTopicId: 301411 +localeTitle: 复习如何使用 Props 和无状态函数组件 --- ## Description -
    除了最后的挑战,你已经将道具传递给无状态功能组件。这些组件就像纯函数一样。他们接受道具作为输入,并在每次传递相同的道具时返回相同的视图。您可能想知道状态是什么,下一个挑战将更详细地介绍它。在此之前,这里是对组件术语的回顾。 无状态功能组件是您编写的任何接受道具并返回JSX的函数。另一方面, 无状态组件是扩展React.Component的类,但不使用内部状态(在下一个挑战中涵盖)。最后,有状态组件是保持其自身内部状态的任何组件。您可能会看到有状态组件简称为组件或React组件。一种常见的模式是尽可能地减少有状态并创建无状态功能组件。这有助于将状态管理包含到应用程序的特定区域。反过来,通过更容易地了解状态更改如何影响其行为,这可以改善应用程序的开发和维护。
    +
    +除了上一个挑战,你一直在将 props 传递给无状态的函数组件。这些组件就像纯函数,它们接收 props 作为输入,并在每次传递相同 props 时返回相同的视图。你可能会想知道什么是状态,下一个挑战将会更详细地讲述它。在此之前,我们先来回顾一下组件的术语。 +无状态函数组件是一个函数,它接收 props 作为输入并返回 JSX。另一方面,无状态组件是一个类,它扩展了React.Component,但是不使用内部状态(下一个挑战中讨论)。最后,状态组件是指维护其自身内部状态的组件,它简称组件或 React 组件。 +一种常见的应用模式是尽可能减少状态组件并创建无状态的函数组件。这有助于将状态管理包含到应用程序的特定区域。反过来,通过更容易地跟踪状态变化如何影响其行为,可以改进应用程序的开发和维护。 +
    ## Instructions -
    代码编辑器有一个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类型。 +
    ## Tests
    ```yml tests: - - text: 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类型的nameprop。 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 } }; - ```
    @@ -73,7 +78,7 @@ var PropTypes = {
    ```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
    @@ -83,8 +88,38 @@ console.info('after the test'); ## Solution
    + ```js -// solution required +class CampSite extends React.Component { + constructor(props) { + super(props); + } + render() { + return ( +
    + +
    + ); + } +}; +// change code below this line + +const Camper = (props) => { + return ( +
    +

    {props.name}

    +
    + ); +}; + +Camper.propTypes = { + name: PropTypes.string.isRequired +}; + +Camper.defaultProps = { + name: 'CamperBot' +}; + ``` -/section> +
    diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/set-state-with-this.setstate.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/set-state-with-this.setstate.chinese.md index bdb6fc4c60..fe179a9862 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/set-state-with-this.setstate.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/set-state-with-this.setstate.chinese.md @@ -3,28 +3,41 @@ id: 5a24c314108439a4d4036173 title: Set State with this.setState challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 使用this.setState设置State +forumTopicId: 301412 +localeTitle: 用 this.setState 设置状态 --- ## Description -
    之前的挑战包括组件state以及如何在constructor初始化状态。还有一种方法可以改变组件的state 。 React提供了一种更新名为setState组件state的方法。您可以在组件类中调用setState方法,如下所示: this.setState() ,传入一个具有键值对的对象。键是您的状态属性,值是更新的状态数据。例如,如果我们在状态中存储username并想要更新它,它将如下所示:
    this.setState({
    用户名:'Lewis'
    });
    React希望你永远不要直接修改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 文档。 +
    ## Instructions -
    代码编辑器中有一个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 的更新。如果你不完全理解单击处理程序代码在此时的工作方式,请不要担心。在接下来的挑战中会有讲述。 +
    ## Tests
    ```yml tests: - - text: '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(/

    TestName<\/h1>/.test(firstValue)); };' - - text: 在MyComponent上调用handleClick方法应该将state属性设置为等于React Rocks! 。 + - text: 调用MyComponenthandleClick方法应该将 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 { ); } }; - ```

    @@ -69,7 +81,7 @@ class MyComponent extends React.Component {
    ```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
    @@ -79,8 +91,32 @@ console.info('after the test'); ## Solution
    + ```js -// solution required +class MyComponent extends React.Component { + constructor(props) { + super(props); + this.state = { + name: 'Initial State' + }; + this.handleClick = this.handleClick.bind(this); + } + handleClick() { + // change code below this line + this.setState({ + name: 'React Rocks!' + }); + // change code above this line + } + render() { + return ( +
    + +

    {this.state.name}

    +
    + ); + } +}; ``` -/section> +
    diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/use--for-a-more-concise-conditional.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/use--for-a-more-concise-conditional.chinese.md index d8a400e71c..70fb1b95c5 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/use--for-a-more-concise-conditional.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/use--for-a-more-concise-conditional.chinese.md @@ -3,28 +3,34 @@ id: 5a24c314108439a4d4036185 title: Use && for a More Concise Conditional challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 使用&&获得更简洁的条件 +forumTopicId: 301413 +localeTitle: 使用 && 获得更简洁的条件 --- ## Description -
    if / else语句在最后一次挑战中起作用,但是有一种更简洁的方法来实现相同的结果。想象一下,您正在跟踪组件中的多个条件,并且您希望根据这些条件中的每个条件呈现不同的元素。如果你写了很多else if语句来返回略有不同的UI,你可能会重复代码,这会留下错误的余地。相反,您可以使用&& logical运算符以更简洁的方式执行条件逻辑。这是可能的,因为您要检查条件是否为true ,如果是,则返回一些标记。下面是一个示例: {condition && <p>markup</p>}如果conditiontrue ,则返回标记。如果条件为false ,则在评估condition后操作将立即返回false并且不返回任何内容。您可以直接在JSX中包含这些语句,并在每个语句之后写入&&多个条件串在一起。这允许您在render()方法中处理更复杂的条件逻辑,而无需重复大量代码。
    +
    +if/else 语句在上一次挑战中是有效的,但是有一种更简洁的方法可以达到同样的结果。假设你正在跟踪组件中的几个条件,并且希望根据这些条件中的每一个来渲染不同的元素。如果你写了很多else if语句来返回稍微不同的 UI,你可能会写很多重复代码,这就留下了出错的空间。相反,你可以使用&&逻辑运算符以更简洁的方式执行条件逻辑。这是完全可行的,因为你希望检查条件是否为真,如果为真,则返回一些标记。这里有一个例子: +{condition && <p>markup</p>} +如果condition为 true,则返回标记。如果 condition 为 false,操作将在判断condition后立即返回false,并且不返回任何内容。你可以将这些语句直接包含在 JSX 中,并通过在每个条件后面写&&来将多个条件串在一起。这允许你在render()方法中处理更复杂的条件逻辑,而无需重复大量代码。 +
    ## Instructions -
    再次解决前面的示例,因此h1仅在displaytrue呈现,但使用&& logical运算符而不是if/else语句。
    +
    +再来看看前面的示例,h1还是在displaytrue时被渲染,但使用&&逻辑运算符代替if/else语句。 +
    ## Tests
    ```yml tests: - - text: MyComponent应该存在并呈现。 + - text: MyComponent应该存在并被渲染。 testString: assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyComponent)); return mockedComponent.find('MyComponent').length; })()); - - text: 当display设置为true ,应该渲染divbuttonh1 。 + - text: 当display被设置为true时,divbuttonh1标签应该被渲染。 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 ,只应呈现divbutton 。 + - text: 当display被设置为false时,只有divbutton应该被渲染。 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 {
    ```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
    @@ -80,8 +85,31 @@ console.info('after the test'); ## Solution
    + ```js -// solution required +class MyComponent extends React.Component { + constructor(props) { + super(props); + this.state = { + display: true + } + this.toggleDisplay = this.toggleDisplay.bind(this); + } + toggleDisplay() { + this.setState(state => ({ + display: !state.display + })); + } + render() { + // change code below this line + return ( +
    + + {this.state.display &&

    Displayed!

    } +
    + ); + } +}; ``` -/section> +
    diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/use-a-ternary-expression-for-conditional-rendering.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/use-a-ternary-expression-for-conditional-rendering.chinese.md index 4f2c562f7b..e919f9324b 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/use-a-ternary-expression-for-conditional-rendering.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/use-a-ternary-expression-for-conditional-rendering.chinese.md @@ -3,34 +3,44 @@ id: 5a24c314108439a4d4036187 title: Use a Ternary Expression for Conditional Rendering challengeType: 6 isRequired: false -videoUrl: '' +forumTopicId: 301414 localeTitle: 使用三元表达式进行条件渲染 --- ## Description -
    在继续使用动态渲染技术之前,最后一种方法是使用内置的JavaScript条件来渲染你想要的东西: 三元运算符 。三元运算符通常用作JavaScript中if/else语句的快捷方式。它们不像传统的if/else语句那样健壮,但它们在React开发人员中非常受欢迎。这样做的一个原因是因为JSX是如何编译的, if/else语句不能直接插入到JSX代码中。您可能已经注意到这几个挑战 - 当需要if/else语句时,它总是在return语句之外 。如果要在JSX中实现条件逻辑,三元表达式可能是一个很好的选择。回想一下,三元运算符有三个部分,但是你可以将几个三元表达式组合在一起。这是基本语法:
    条件? expressionIfTrue:expressionIfFalse
    +
    +在继续使用动态渲染技术之前,还有最后一种方法可以渲染你想要的东西,它使用内置的 JavaScript 条件:三元运算符。三元运算符经常被用作 JavaScript 中if/else语句的缩写。它们不像传统的if/else语句那样健壮,但是在 React 开发人员中非常流行,原因之一就是 JSX 的编译原理,if/else语句不能直接插入到 JSX 代码中。你可能在前几个挑战就注意到了这一点--当需要if/else语句时,它总是在return语句外面。如果你想在 JSX 中实现条件逻辑,三元表达式是一个很好的选择。回想一下,三元运算符有三个部分,但是你可以将多个三元表达式组合在一起。以下是基本语法: + +```js +condition ? expressionIfTrue : expressionIfFalse +``` + +
    ## Instructions -
    代码编辑器在CheckUserAge组件的render()方法中定义了三个常量。它们被称为buttonOnebuttonTwobuttonThree 。每个都分配了一个表示按钮元素的简单JSX表达式。首先,使用inputuserAge初始化CheckUserAge的状态, userAge其设置为空字符串的值。一旦组件向页面呈现信息,用户就应该有办法与它进行交互。在组件的return语句中,设置一个实现以下逻辑的三元表达式:当页面首次加载时,将提交按钮buttonOne呈现给页面。然后,当用户输入他们的年龄并单击该按钮时,根据年龄呈现不同的按钮。如果用户输入的数字小于18 ,则渲染buttonThree 。如果用户输入的数字大于或等于18 ,则渲染buttonTwo
    +
    +代码编辑器在CheckUserAge组件的render()方法中定义了三个常量,它们分别是buttonOnebuttonTwobuttonThree。每个都分配了一个表示按钮元素的简单 JSX 表达式。首先,使用inputuserAge初始化CheckUserAge的 state,并将其值设置为空字符串。 +一旦组件将信息渲染给页面,用户应该有一种方法与之交互。在组件的return语句中,设置一个实现以下逻辑的三元表达式:当页面首次加载时,将提交按钮buttonOne渲染到页面。然后,当用户输入年龄并点击该按钮时,根据年龄渲染不同的按钮。如果用户输入的数字小于18,则渲染buttonThree。如果用户输入的数字大于或等于18,则渲染buttonTwo。 +
    ## Tests
    ```yml tests: - - text: 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:
    ```jsx + const inputStyle = { width: 235, margin: 5 @@ -60,13 +71,13 @@ class CheckUserAge extends React.Component { handleChange(e) { this.setState({ input: e.target.value, - userAge: " + userAge: '' }); } submit() { - this.setState({ - userAge: this.state.input - }); + this.setState(state => ({ + userAge: state.input + })); } render() { const buttonOne = ; @@ -87,7 +98,6 @@ class CheckUserAge extends React.Component { ); } }; - ```
    @@ -97,7 +107,7 @@ class CheckUserAge extends React.Component {
    ```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
    @@ -107,8 +117,57 @@ console.info('after the test'); ## Solution
    + ```js -// solution required +const inputStyle = { + width: 235, + margin: 5 +} + +class CheckUserAge extends React.Component { + constructor(props) { + super(props); + this.state = { + userAge: '', + input: '' + } + this.submit = this.submit.bind(this); + this.handleChange = this.handleChange.bind(this); + } + handleChange(e) { + this.setState({ + input: e.target.value, + userAge: '' + }); + } + submit() { + this.setState(state => ({ + userAge: state.input + })); + } + render() { + const buttonOne = ; + const buttonTwo = ; + const buttonThree = ; + return ( +
    +

    Enter Your Age to Continue

    +
    + { + this.state.userAge === '' ? + buttonOne : + this.state.userAge >= 18 ? + buttonTwo : + buttonThree + } +
    + ); + } +}; ``` -/section> +
    diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/use-advanced-javascript-in-react-render-method.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/use-advanced-javascript-in-react-render-method.chinese.md index bc1d62f40a..47a6d5a186 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/use-advanced-javascript-in-react-render-method.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/use-advanced-javascript-in-react-render-method.chinese.md @@ -3,32 +3,37 @@ id: 5a24c314108439a4d4036183 title: Use Advanced JavaScript in React Render Method challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 在React Render方法中使用高级JavaScript +forumTopicId: 301415 +localeTitle: 在 React Render 方法中使用 JavaScript --- ## Description -
    在以前的挑战中,您学习了如何使用大括号{ }将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 代码中使用变量时,可以将变量名放在大括号中。 +
    ## Instructions -
    在提供的代码中, 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常量。 +
    ## Tests
    ```yml tests: - - text: 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 {
    ```js -console.info('after the test'); +var possibleAnswers = [ 'It is certain', 'It is decidedly so', 'Without a doubt', 'Yes, definitely', 'You may rely on it', 'As I see it, yes', 'Outlook good', 'Yes', 'Signs point to yes', 'Reply hazy try again', 'Ask again later', 'Better not tell you now', 'Cannot predict now', 'Concentrate and ask again', 'Don\'t count on it', 'My reply is no', 'My sources say no', 'Outlook not so good','Very doubtful', 'Most likely' ]; +ReactDOM.render(, document.getElementById('root')) ```
    @@ -133,8 +138,62 @@ console.info('after the test'); ## Solution
    + ```js -// solution required +const inputStyle = { + width: 235, + margin: 5 +} + +class MagicEightBall extends React.Component { + constructor(props) { + super(props); + this.state = { + userInput: '', + randomIndex: '' + } + this.ask = this.ask.bind(this); + this.handleChange = this.handleChange.bind(this); + } + ask() { + if (this.state.userInput) { + this.setState({ + randomIndex: Math.floor(Math.random() * 20), + userInput: '' + }); + } + } + handleChange(event) { + this.setState({ + userInput: event.target.value + }); + } + render() { + const possibleAnswers = [ + "It is certain", "It is decidedly so", "Without a doubt", + "Yes, definitely", "You may rely on it", "As I see it, yes", + "Outlook good", "Yes", "Signs point to yes", "Reply hazy try again", + "Ask again later", "Better not tell you now", "Cannot predict now", + "Concentrate and ask again", "Don't count on it", "My reply is no", + "My sources say no", "Outlook not so good","Very doubtful", "Most likely" + ]; + const answer = possibleAnswers[this.state.randomIndex]; + return ( +
    +
    +
    +

    Answer:

    +

    + {answer} +

    +
    + ); + } +}; ``` -/section> +
    diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/use-array.filter-to-dynamically-filter-an-array.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/use-array.filter-to-dynamically-filter-an-array.chinese.md index ad10ce2268..05eff521e4 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/use-array.filter-to-dynamically-filter-an-array.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/use-array.filter-to-dynamically-filter-an-array.chinese.md @@ -3,30 +3,35 @@ id: 5a24c314108439a4d403618c title: Use Array.filter() to Dynamically Filter an Array challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 使用Array.filter()动态过滤数组 +forumTopicId: 301416 +localeTitle: 使用 Array.Filter() 动态过滤数组 --- ## Description -
    map数组方法是一个强大的工具,在使用React时经常会使用它。另一种与map相关的方法是filter ,它根据条件过滤数组的内容,然后返回一个新数组。例如,如果您的所有用户都具有online属性(可以设置为truefalse ,则可以通过以下方式仅过滤那些在线用户: let onlineUsers = users.filter(user => user.online);
    +
    +map数组方法是一个强大的工具,在使用 React 时经常使用。与map相关的另一种方法是filter,它根据条件过滤数组的内容,然后返回一个新数组。例如,如果你有一个 users 数组,每个数组元素都有一个可以设置为truefalseonline属性,你可以只过滤那些在线的用户: +let onlineUsers = users.filter(user => user.online); +
    ## Instructions -
    在代码编辑器中, MyComponentstate是用一组用户初始化的。有些用户在线,有些则没有。过滤数组,以便只查看在线用户。要执行此操作,请首先使用filter返回仅包含online属性为true的用户的新数组。然后,在renderOnline变量中,映射已过滤的数组,并为包含其username文本的每个用户返回li元素。确保包括一个独特的key ,就像在最后的挑战中一样。
    +
    +在代码编辑器中,MyComponentstate由一个 users 数组初始化。有些用户在线,有些则不在线。过滤数组,以便你只看到在线用户。为此,首先使用filter返回一个新数组,该数组只包含online属性为true的用户。然后,在renderOnline变量中,映射经过过滤的数组,并为每个用户返回一个包含它们username文本的li元素。确保像上一个挑战一样包含一个独特的key。 +
    ## Tests
    ```yml tests: - - text: 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 === ''
    • Jeff
    • Alan
    • Mary
    • Jim
    • Laura
    ''); }; ' - - text: 每个列表项元素都应具有唯一的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 {
    ```js -console.info('after the test'); +ReactDOM.render(, document.getElementById('root')) ```
    @@ -104,8 +108,59 @@ console.info('after the test'); ## Solution
    + ```js -// solution required +class MyComponent extends React.Component { + constructor(props) { + super(props); + this.state = { + users: [ + { + username: 'Jeff', + online: true + }, + { + username: 'Alan', + online: false + }, + { + username: 'Mary', + online: true + }, + { + username: 'Jim', + online: false + }, + { + username: 'Sara', + online: true + }, + { + username: 'Laura', + online: true + } + ] + } + } + render() { + const usersOnline = this.state.users.filter(user => { + return user.online; + }); + const renderOnlineUsers = usersOnline.map(user => { + return ( +
  • {user.username}
  • + ); + }); + return ( +
    +

    Current Online Users:

    +
      + {renderOnlineUsers} +
    +
    + ); + } +}; ``` -/section> +
    diff --git a/curriculum/challenges/chinese/03-front-end-libraries/react/use-array.map-to-dynamically-render-elements.chinese.md b/curriculum/challenges/chinese/03-front-end-libraries/react/use-array.map-to-dynamically-render-elements.chinese.md index 1de16d961e..dffff66b70 100644 --- a/curriculum/challenges/chinese/03-front-end-libraries/react/use-array.map-to-dynamically-render-elements.chinese.md +++ b/curriculum/challenges/chinese/03-front-end-libraries/react/use-array.map-to-dynamically-render-elements.chinese.md @@ -3,33 +3,42 @@ id: 5a24c314108439a4d403618a title: Use Array.map() to Dynamically Render Elements challengeType: 6 isRequired: false -videoUrl: '' -localeTitle: 使用Array.map()动态渲染元素 +forumTopicId: 301417 +localeTitle: 使用 Array.map() 动态渲染元素 --- ## Description -
    条件渲染很有用,但您可能需要组件渲染未知数量的元素。通常在反应式编程中,程序员无法知道应用程序的状态直到运行时,因为这很大程度上取决于用户与该程序的交互。程序员需要编写代码以提前正确处理未知状态。在React中使用Array.map()说明了这个概念。例如,您创建一个简单的“待办事项列表”应用程序。作为程序员,您无法知道用户可能在其列表中有多少项。您需要设置组件,以便在使用该程序的人决定今天是洗衣日之前动态呈现正确数量的列表元素。
    +
    +条件渲染很有用,但是你可能需要组件来渲染未知数量的元素。通常在响应式编程中,程序员在应用程序运行时之前无法知道其 state,因为这在很大程度上取决于用户与该程序的交互。程序员需要提前编写代码来正确处理未知状态。在 React 中使用Array.map()阐明了这个概念。 +例如,你创建一个简单的“To Do List”应用程序。作为程序员,你无法知道用户可能在其列表中有多少项。你需要设置组件,以便在使用该程序的人决定今天今日待办事项之前动态渲染正确数量的列表元素。 +
    ## Instructions -
    代码编辑器设置了大部分MyToDoList组件。如果您完成了受控制的表单质询,那么这些代您会注意到一个textarea和一个button ,以及一些跟踪其状态的方法,但是还没有任何内容呈现给页面。在constructor内部,创建一个this.state对象并定义两个状态: userInput应初始化为空字符串, toDoList应初始化为空数组。接下来,删除items变量旁边的render()方法中的注释。取而代之,映射存储在组件内部状态中的toDoList数组,并为每个项目动态呈现li 。尝试输入字符串eat, code, sleep, repeattextarea ,然后单击按钮,看看会发生什么。 注意:您可能知道由这样的映射操作创建的所有兄弟子元素都需要提供唯一的key属性。别担心,这是下一个挑战的主题。
    +
    +代码编辑器完成了MyToDoList组件的大部分设置。如果你完成了受控表单挑战,这些代码中的一些应该看起来很熟悉。你会注意到一个textarea和一个button,以及一些跟踪它们状态的方法,但是页面当前还没有任何东西被渲染。 +在constructor中,创建一个this.state对象并定义两个 state:userInput应该初始化为空字符串,toDoList应该初始化为空数组。接下来,删除items变量旁边render()方法中的注释。取而代之的是,将存储在组件内部 state 中的toDoList数组一一映射并相应的动态呈现li元素。尝试在textarea中输入eat, code, sleep, repeat,然后点击按钮,看看会发生什么。 +注意: 你可能知道,像这样的映射操作创建的所有兄弟子元素都需要提供唯一的key属性。别担心,这是下一个挑战的主题。 +
    ## Tests
    ```yml tests: - - text: MyToDoList组件应该存在并呈现给页面。 - testString: 'assert((function() { const mockedComponent = Enzyme.mount(React.createElement(MyToDoList)); return mockedComponent.find("MyToDoList").length === 1; })(), "The MyToDoList component should exist and render to the page.");' - - text: 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: 应使用toDoListMyToDoList的状态初始化为空数组。 - 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: 应使用userInputMyToDoList的状态初始化为空字符串。 - 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 ( +
    +