前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >freeCodeCamp | Front End Development Libraries | 笔记

freeCodeCamp | Front End Development Libraries | 笔记

作者头像
yiyun
发布2023-07-17 13:56:14
3930
发布2023-07-17 13:56:14
举报
文章被收录于专栏:yiyun 的专栏yiyun 的专栏

引言

现在你已经熟悉了 HTML、CSS 和 JavaScript,通过学习业内一些最流行的前端库来提高你的技能。 在前端开发库认证中,你将学习如何使用 Bootstrap 快速设置网站样式。你还将学习如何向 CSS 样式添加逻辑并使用 Sass 扩展它们。 稍后,你将构建购物车和其他应用程序,以学习如何使用 React 和 Redux 创建功能强大的单页应用程序 (SPA)。

SASS

sass 是 scss 升级版 sass 可以没有花括号 {}**, 也无需分号结尾** ; , 这个特性后续不再说明 注意: 发现很多其实是 SCSS, 教程不太严谨 Sass, 是 CSS 的语言扩展。它添加了基本 CSS 中不可用的功能,使你可以更轻松地简化和维护项目的样式表。 如何将数据存储在变量中、嵌套 CSS、使用 mixins 创建可重用的样式、为样式添加逻辑和循环等等。

使用 Sass 变量存储数据

Sass 与 CSS 不同的一个功能是它使用变量。 它们被声明并设置为存储数据,类似于 JavaScript。 在 JavaScript 中,变量是使用 letconst 关键字定义的。 在 Sass 中,变量以 $ 开头,后跟变量名称。 申明变量 变量赋值用冒号 :

代码语言:javascript
复制
/* 注意: 这里中间间隔用的是逗号 , 而不是空格 */
/* 变量值 也无需加双引号/单引号, 就像是正常的 css 属性值一样 */
$main-fonts: Arial, sans-serif;
$headings-color: green;

使用变量

代码语言:javascript
复制
h1 {
  /* 一般 css 这里多个属性值使用空格间隔 */
  font-family: $main-fonts;
  color: $headings-color;
}

变量有用的一个例子是当许多元素需要相同的颜色时。 如果更改了该颜色,则编辑代码的唯一位置是变量值。 创建一个变量 $text-color 并将其设置为 red 。 然后将 .blog-posth2color 属性的值更改为 $text-color 变量。

代码语言:javascript
复制
<style type='text/scss'>
  $text-color: red;

  .header{
    text-align: center;
  }
  .blog-post, h2 {
    color: $text-color;
  }
</style>

<h1 class="header">Learn Sass</h1>
<div class="blog-post">
  <h2>Some random title</h2>
  <p>This is a paragraph with some random text in it</p>
</div>
<div class="blog-post">
  <h2>Header #2</h2>
  <p>Here is some more random text.</p>
</div>
<div class="blog-post">
  <h2>Here is another header</h2>
  <p>Even more random text within a paragraph</p>
</div>

嵌套 CSS

传统 CSS 中, 通常,每个元素都针对不同的行来设置其样式,如下所示:

代码语言:javascript
复制
nav {
  background-color: red;
}

nav ul {
  list-style: none;
}

nav ul li {
  display: inline-block;
}

对于大型项目,CSS 文件将有许多行和规则。 这就是嵌套可以通过在相应的父元素中放置子样式规则来帮助组织代码的地方:

代码语言:javascript
复制
nav {
  background-color: red;

  ul {
    list-style: none;

    li {
      display: inline-block;
    }
  }
}

实践

代码语言:javascript
复制
<style type='text/scss'>
  .blog-post {
    h1 {
      text-align: center;
      color: blue;
    }
    p {
      font-size: 20px;
    }
  }
</style>

<div class="blog-post">
  <h1>Blog Title</h1>
  <p>This is a paragraph</p>
</div>

使用 Mixins 创建可重用的 CSS

在 Sass 中,mixin 是一组可以在整个样式表中重用的 CSS 声明。 较新的 CSS 功能需要时间才能完全采用并准备好在所有浏览器中使用。 随着功能添加到浏览器中,使用它们的 CSS 规则可能需要供应商前缀。 考虑 box-shadow

代码语言:javascript
复制
div {
  -webkit-box-shadow: 0px 0px 4px #fff;
  -moz-box-shadow: 0px 0px 4px #fff;
  -ms-box-shadow: 0px 0px 4px #fff;
  box-shadow: 0px 0px 4px #fff;
}

为所有具有 box-shadow 的元素重写此规则或更改每个值以测试不同的效果需要大量键入。 Mixins 就像 CSS 的函数。 如下所示:

代码语言:javascript
复制
@mixin box-shadow($x, $y, $blur, $c){ 
  -webkit-box-shadow: $x $y $blur $c;
  -moz-box-shadow: $x $y $blur $c;
  -ms-box-shadow: $x $y $blur $c;
  box-shadow: $x $y $blur $c;
}

定义以 @mixin 开头,后跟自定义名称。 参数(上例中的 $x$y$blur$c )是可选的。 现在,每当需要 box-shadow 规则时, 只需一行调用 mixin 即可取代键入所有 vendor 前缀的行。 使用 @include 指令调用 mixin

代码语言:javascript
复制
div {
  @include box-shadow(0px, 0px, 4px, #fff);
}

实践 为 border-radius 编写一个 mixin 并为其指定一个 $radius 参数。 它应使用示例中的所有 vendor 前缀。 然后使用 border-radius mixin 使 #awesome 元素的边框半径为 15px

代码语言:javascript
复制
<style type='text/scss'>

  @mixin border-radius($radius) {
    -webkit-border-radius: $radius;
    -moz-border-radius: $radius;
    -ms-border-radius: $radius;
    border-radius: $radius;
  }

  #awesome {
    width: 150px;
    height: 150px;
    background-color: green;
    @include border-radius(15px);
  }
</style>

<div id="awesome"></div>

补充 参考:Sass: @mixin and @include (sass-lang.com)

代码语言:javascript
复制
/* 定义函数 */
@mixin custom-mixin-name($param1, $param2, ....) {
    // CSS Properties Here...
}
代码语言:javascript
复制
/* 使用函数 */
element {
    @include custom-mixin-name(value1, value2, ....);
}

一个官网示例: Sass: @mixin and @include 可选参数 CSS

代码语言:javascript
复制
.mail-icon {
  text-indent: -99999em;
  overflow: hidden;
  text-align: left;
  background-image: url("/images/mail.svg");
  background-repeat: no-repeat;
  background-position: 0 50%;
}

SCSS

代码语言:javascript
复制
@mixin replace-text($image, $x: 50%, $y: 50%) {
  text-indent: -99999em;
  overflow: hidden;
  text-align: left;

  background: {
    image: $image;
    repeat: no-repeat;
    position: $x $y;
  }
}

.mail-icon {
  @include replace-text(url("/images/mail.svg"), 0);
}

Sass

代码语言:javascript
复制
@mixin replace-text($image, $x: 50%, $y: 50%)
  text-indent: -99999em
  overflow: hidden
  text-align: left

  background:
    image: $image
    repeat: no-repeat
    position: $x $y

.mail-icon
  @include replace-text(url("/images/mail.svg"), 0)

使用 @if @else 为样式添加逻辑

Sass 中的 @if 指令对于测试特定情况很有用 它的工作方式就像 JavaScript 中的 if 语句一样。

代码语言:javascript
复制
@mixin make-bold($bool) {
  @if $bool == true {
    font-weight: bold;
  }
}

就像在 JavaScript 中一样, @else if@else 指令测试更多条件:

代码语言:javascript
复制
@mixin text-effect($val) {
  @if $val == danger {
    color: red;
  }
  @else if $val == alert {
    color: yellow;
  }
  @else if $val == success {
    color: green;
  }
  @else {
    color: black;
  }
}

实践 创建一个名为 border-stroke 的 mixin,该 mixin 采用参数 $val 。 mixin 应使用 @if@else if@else 指令检查以下条件:

代码语言:javascript
复制
light - 1px solid black
medium - 3px solid black
heavy - 6px solid black

如果 $val 参数值不是 lightmediumheavy , 则 border 属性应设置为 none

代码语言:javascript
复制
<style type='text/scss'>

  @mixin border-stroke($val) {
    @if $val == light {
      border: 1px solid black;
    }
    @else if $val == medium {
      border: 3px solid black;
    }
    @else if $val == heavy {
      border: 6px solid black;
    }
    @else {
      border: none;
    }
  }

  #box {
    width: 150px;
    height: 150px;
    background-color: red;
    @include border-stroke(medium);
  }
</style>

<div id="box"></div>

使用 @for 创建 Sass 循环

@for 有两种方式:

  1. from A through B
    • [A, B]
  2. from A to B
    • [A, b)
代码语言:javascript
复制
@for $i from 1 through 3 {
  // some CSS
}

// 1 2 3
代码语言:javascript
复制
@for $i from 1 to 3 {
  // some CSS
}

// 1 2

PS:可以直接带上单位 (%, px 等) 进行计算

代码语言:javascript
复制
@for $i from 1 through 12 {
  .col-#{$i} { width: 100%/12 * $i; }
}

@for $i from 1 to 12 {
  .col-#{$i} { width: 100%/12 * $i; }
}

#{$i} 部分是将变量 ( i ) 与文本组合以生成字符串 的语法。 当 Sass 文件转换为 CSS 时,它看起来像这样:

代码语言:javascript
复制
.col-1 {
  width: 8.33333%;
}

.col-2 {
  width: 16.66667%;
}

...

.col-12 {
  width: 100%;
}

这是一个创建 grid 布局的好方法, 实践 使用 @for 指令用 $j16 (不包括 6), 创建 5 个类,从 .text-1.text-5 , 每个都有 font-size 值为 15px*$j

代码语言:javascript
复制
<style type='text/scss'>

@for $j from 1 to 6 {
  .text-#{$j} {
    font-size: 15px * $j;
  }
}

</style>

<p class="text-1">Hello</p>
<p class="text-2">Hello</p>
<p class="text-3">Hello</p>
<p class="text-4">Hello</p>
<p class="text-5">Hello</p>

使用 @each 映射列表中的项

Sass 还提供了 @each 循环遍历列表 或 映射中每个项 的指令。 在每次迭代中,变量被分配给列表或映射中的当前值。

代码语言:javascript
复制
@each $color in blue, red, green {
  .#{$color}-text {color: $color;}
}

映射 的语法略有不同。 一个例子:

代码语言:javascript
复制
$colors: (color1: blue, color2: red, color3: green);

@each $key, $color in $colors {
  .#{$color}-text {color: $color;}
}

请注意,需要该 $key 变量来引用映射中的键。 否则,编译后的 CSS 将包含 color1, color2...。 以上两个代码示例都转换为以下 CSS:

代码语言:javascript
复制
.blue-text {
  color: blue;
}

.red-text {
  color: red;
}

.green-text {
  color: green;
}

实践 编写一个 @each 遍历列表的指令: blue, black, red 并将每个变量分配给一个 .color-bg 类, 其中 color 每个项目的部分都会发生变化。 每个类都应该设置 background-color 各自的颜色。

代码语言:javascript
复制
<style type='text/scss'>

  $colors: blue, black, red;

  @each $color in $colors {
    .#{$color}-bg {
      background-color: $color;
    }
  }

  div {
    height: 200px;
    width: 200px;
  }
</style>

<div class="blue-bg"></div>
<div class="black-bg"></div>
<div class="red-bg"></div>

image-20230618223804420

应用样式直到条件满足 @while

@while 指令是一个选项,具有与 JavaScript while 循环类似的功能。 它会创建 CSS 规则,直到满足条件为止。 简单网格系统

代码语言:javascript
复制
$x: 1;
@while $x < 13 {
  .col-#{$x} { width: 100%/12 * $x;}
  $x: $x + 1;
}

首先,定义一个变量$x并将其设置为 1。 接下来,使用 @while 指令创建网格系统 while $x小于 13。 在为 设置 CSS 规则后 width$x 递增 1 以避免无限循环。 实践 用于 @while 创建一系列具有不同 font-sizes. text-1text-5。 然后设置 font-size15px 乘以当前索引号。 确保避免无限循环!

代码语言:javascript
复制
<style type='text/scss'>

$x: 1;
@while $x <= 5 {
  .text-#{$x} {
    font-size: 15px * $x;
  }
  $x: $x + 1;
}

</style>

<p class="text-1">Hello</p>
<p class="text-2">Hello</p>
<p class="text-3">Hello</p>
<p class="text-4">Hello</p>
<p class="text-5">Hello</p>

使用 Partials 将样式拆分为更小的块

Sass 中的 Partials 是包含 CSS 代码段的单独文件。 这些被导入并在其他 Sass 文件中使用。 这是将类似代码分组到一个模块中以保持其组织性的好方法。 partials 的名称以下划线 ( _) 字符开头,告诉 Sass 这是 CSS 的一小段,不要将其转换为 CSS 文件。 此外,Sass 文件以文件扩展名结尾 .scss。 要将部分代码放入另一个 Sass 文件,请使用该 @import 指令。 例如, 如果你所有的 mixins 都保存在名为 “_mixins.scss” 的部分中, 并且在 “main.scss” 文件中需要它们, 那么在主文件中使用它们的方法如下:

代码语言:javascript
复制
@import 'mixins'

请注意,语句中不需要下划线和文件扩展名 import Sass 理解它是 partials 的。 一旦将 partials 导入到文件中,所有变量、mixin 和其他代码都可以使用。 实践 编写一条 @import语句,将 partial named 导入 _variables.scssmain.scss 文件中。

代码语言:javascript
复制
<!-- The main.scss file -->
@import 'variables'

将一组 CSS 样式扩展 (**@extend**) 到另一个元素

个人理解: @extend 更像是继承 Sass 有一个特性 extend,可以很容易地从一个元素借用 CSS 规则并在另一个元素的基础上构建。 例如, 下面的 CSS 规则块为一个 .panel 类设置样式。 它有一个 background-colorheightborder

代码语言:javascript
复制
.panel {
  background-color: red;
  height: 70px;
  border: 2px solid green;
}

现在您想要另一个的面板名为 .big-panel。 它具有与 相同的基本属性 .panel ,但还需要一个 widthfont-size。 可以从 复制和粘贴初始 CSS 规则 .panel,但是随着您添加更多类型的面板,代码会变得重复。 该 extend 指令是重用为一个元素编写的规则,然后为另一个元素添加更多规则的简单方法:

代码语言:javascript
复制
.big-panel {
  @extend .panel;
  width: 150px;
  font-size: 2em;
}

除了新样式之外,.big-panel.panel具有相同的属性 实践 创建一个 CSS 类名为 .info-important, 扩展自 .info, 并有 background-color: magenta

代码语言:javascript
复制
<style type='text/scss'>
  h3{
    text-align: center;
  }
  .info{
    width: 200px;
    border: 1px solid black;
    margin: 0 auto;
  }

  /* 使用 @extend */
  .info-important {
    @extend .info;
    background-color: magenta;
  }

</style>
<h3>Posts</h3>
<div class="info-important">
  <p>This is an important post. It should extend the class ".info" and have its own CSS styles.</p>
</div>

<div class="info">
  <p>This is a simple post. It has basic styling and can be extended for other uses.</p>
</div>

image-20230618231007145

React.js

注释: { /* 我是注释 */ }

要将注释放在 JSX 中,请使用语法 {/* */} 环绕注释文本。

代码语言:javascript
复制
const JSX = (
  <div>
    <h1>This is a block of JSX</h1>
    <p>Here's a subtitle</p>
    { /* 我是 JSX 中的注释 */ }
  </div>
);

标签必须闭合

在 JSX 中, 任何 JSX 元素都可以使用自闭合标记编写,并且每个元素都必须闭合。 例如,换行标记必须始终写为 <br /> ,才能成为可以转译的有效 JSX。 而在 HTML 中, 例如,换行标记可以写为 <br><br /> ,但绝不应写为 <br></br> ,因为它不包含任何内容。

组件

有两种方法可以创建 React 组件。第一种方法是使用 JavaScript 函数。 以这种方式定义组件会创建一个无状态的功能组件。 现在,将无状态组件视为可以接收数据并呈现数据,但不管理或跟踪对该数据的更改的组件。

要创建带有函数的组件,您只需编写一个返回 JSX 或 null 的 JavaScript 函数。需要注意的重要一点是,React 要求你的函数名称以大写字母开头。

代码语言:javascript
复制
const DemoComponent = function() {
  return (
    <div className='customClass' />
  );
};

定义 React 组件的另一种方法是使用 ES6 class 语法。在以下示例中, Kitten 扩展 React.Component

代码语言:javascript
复制
class Kitten extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <h1>Hi</h1>
    );
  }
}

默认 props 值: MyComponent.defaultProps

默认 props

代码语言:javascript
复制
MyComponent.defaultProps = { location: 'San Francisco' }

定义 location 默认值为 'San Francisco',

React 分配默认值为: undefined, 但若指定分配 null, 则会保持 null

代码语言:javascript
复制
const ShoppingCart = (props) => {
  return (
    <div>
      <h1>Shopping Cart Component</h1>
      <p>{props.items}</p>
    </div>
  )
};
// Change code below this line
ShoppingCart.defaultProps = {
  items: 0
}

重写 默认 props

代码语言:javascript
复制
const Items = (props) => {
  return <h1>Current Quantity of Items in Cart: {props.quantity}</h1>
}

Items.defaultProps = {
  quantity: 0
}

class ShoppingCart extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    { /* Change code below this line */ }
    return <Items quantity={10} />
    { /* Change code above this line */ }
  }
};

通过 propTypes 限制 props

通过 propTypes 限制 props 参考:Typechecking With PropTypes – React

代码语言:javascript
复制
import PropTypes from 'prop-types';

const Items = (props) => {
  return <h1>Current Quantity of Items in Cart: {props.quantity}</h1>
};

Items.propTypes = {
  quantity: PropTypes.number.isRequired
};

Items.defaultProps = {
  quantity: 0
};

使用 this.props 访问 props

代码语言:javascript
复制
class App extends React.Component {
  constructor(props) {
    super(props);

  }
  render() {
    return (
        <div>
            { /* Change code below this line */ }
            <Welcome name="world"/>
            { /* Change code above this line */ }
        </div>
    );
  }
};

class Welcome extends React.Component {
  constructor(props) {
    super(props);

  }
  render() {
    return (
        <div>
          { /* Change code below this line */ }
          <p>Hello, <strong>{this.props.name}</strong>!</p>
          { /* Change code above this line */ }
        </div>
    );
  }
};

综合示例: propTypes, defaultProps

代码语言:javascript
复制
const Camper = props => <p>{props.name}</p>;

Camper.propTypes = {
  name: PropTypes.string.isRequired
};

Camper.defaultProps = {
  name: "CamperBot"
};

class CampSite extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <div>
        <Camper name="test"/>
      </div>
    );
  }
};

ReactDOM.render(<CampSite />, document.getElementById("challenge-node"));

Create a Stateful Component

代码语言:javascript
复制
class StatefulComponent extends React.Component {
  constructor(props) {
    super(props);
    // Only change code below this line
    this.state = {
      firstName: "Hello"
    };
    // Only change code above this line
  }
  render() {
    return (
      <div>
        <h1>{this.state.firstName}</h1>
      </div>
    );
  }
};

使用 setState 更新 state

代码语言:javascript
复制
this.setState({
  username: 'Lewis'
});
代码语言:javascript
复制
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      text: "Hello"
    };
    // Change code below this line
    this.handleClick = this.handleClick.bind(this);
    // Change code above this line
  }
  handleClick() {
    this.setState({
      text: "You clicked!"
    });
  }
  render() {
    return (
      <div>
        { /* Change code below this line */ }
        <button onClick={this.handleClick}>Click Me</button>
        { /* Change code above this line */ }
        <h1>{this.state.text}</h1>
      </div>
    );
  }
};

有时候,需要访问之前的 state 用于更新现在的 state , 然而,state 更新可能是异步的,这意味着 React 可能批量多个 setState() 呼叫在单个更新,这意味着你不能依赖于之前的值: this.statethis.props 当用于计算下一个值时, 因此你不能下面这样写:

代码语言:javascript
复制
// 错误 示范
this.setState({
  counter: this.state.counter + this.props.increment
});
代码语言:javascript
复制
// 正确 示范
this.setState((state, props) => ({
  counter: state.counter + props.increment
}));

也可以如下

代码语言:javascript
复制
this.setState(state => ({
  counter: state.counter + 1
}));

注意:必须将函数返回值(对象)包裹在 ()

代码语言:javascript
复制
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      visibility: false
    };
    // Change code below this line
    this.toggleVisibility = this.toggleVisibility.bind(this);
    // Change code above this line
  }
  // Change code below this line
  toggleVisibility() {
    this.setState(state => ({
      visibility: !state.visibility
    }));
  }
  // Change code above this line
  render() {
    if (this.state.visibility) {
      return (
        <div>
          <button onClick={this.toggleVisibility}>Click Me</button>
          <h1>Now you see me!</h1>
        </div>
      );
    } else {
      return (
        <div>
          <button onClick={this.toggleVisibility}>Click Me</button>
        </div>
      );
    }
  }
}

综合示例

代码语言:javascript
复制
class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
    // Change code below this line
    this.increment = this.increment.bind(this);
    this.decrement = this.decrement.bind(this);
    this.reset = this.reset.bind(this);
    // Change code above this line
  }
  // Change code below this line
  increment() {
    this.setState(state => ({
      count: state.count + 1
    }));
  }
  decrement() {
    this.setState(state => ({
      count: state.count - 1
    }));
  }
  reset() {
    this.setState(state => ({
      count: 0
    }))
  }
  // Change code above this line
  render() {
    return (
      <div>
        <button className='inc' onClick={this.increment}>Increment!</button>
        <button className='dec' onClick={this.decrement}>Decrement!</button>
        <button className='reset' onClick={this.reset}>Reset</button>
        <h1>Current Count: {this.state.count}</h1>
      </div>
    );
  }
};

可控的 input

代码语言:javascript
复制
class ControlledInput extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      input: ''
    };
    // Change code below this line
    this.handleChange = this.handleChange.bind(this);
    // Change code above this line
  }
  // Change code below this line
  handleChange(event) {
    this.setState(state => ({
      input: event.target.value
    }));
  }
  // Change code above this line
  render() {
    return (
      <div>
        { /* Change code below this line */}
        <input value={this.state.input} onChange={this.handleChange} />
        { /* Change code above this line */}
        <h4>Controlled Input:</h4>
        <p>{this.state.input}</p>
      </div>
    );
  }
};

可控的 Form

代码语言:javascript
复制
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) {
    // Change code below this line
    event.preventDefault();
    this.setState(state => ({
      submit: state.input
    }));
    // Change code above this line
  }
  render() {
    return (
      <div>
        <form onSubmit={this.handleSubmit}>
          {/* Change code below this line */}
          <input value={this.state.input} onChange={this.handleChange} />
          {/* Change code above this line */}
          <button type='submit'>Submit!</button>
        </form>
        {/* Change code below this line */}
        <h1>{this.state.submit}</h1>
        {/* Change code above this line */}
      </div>
    );
  }
}

Pass State as Props to Child Components

代码语言:javascript
复制
class MyApp extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: 'CamperBot'
    }
  }
  render() {
    return (
       <div>
         {/* Change code below this line */}
         <Navbar name={this.state.name} />
         {/* Change code above this line */}
       </div>
    );
  }
};

class Navbar extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
    <div>
      {/* Change code below this line */}
      <h1>Hello, my name is: {this.props.name}</h1>
      {/* Change code above this line */}
    </div>
    );
  }
};

Pass a Callback as Props

代码语言:javascript
复制
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 (
       <div>
        { /* Change code below this line */ }
        <GetInput input={this.state.inputValue} handleChange={this.handleChange} />
        <RenderInput input={this.state.inputValue} />
        { /* Change code above this line */ }
       </div>
    );
  }
};

class GetInput extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <div>
        <h3>Get Input:</h3>
        <input
          value={this.props.input}
          onChange={this.props.handleChange}/>
      </div>
    );
  }
};

class RenderInput extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <div>
        <h3>Input Render:</h3>
        <p>{this.props.input}</p>
      </div>
    );
  }
};

生命周期: componentWillMount

Note: The componentWillMount Lifecycle method will be deprecated in a future version of 16.X and removed in version 17. Learn more in this article

componentWillMount()render() 之前被调用

生命周期: componentDidMount

组件被挂载到 DOM 后 componentDidMount() 被调用, 任何对 setState() 都会触发组件的重新渲染, 可以在这里获取异步 API 的数据

代码语言:javascript
复制
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      activeUsers: null
    };
  }
  componentDidMount() {
    setTimeout(() => {
      this.setState({
        activeUsers: 1273
      });
    }, 2500);
  }
  render() {
    return (
      <div>
        {/* Change code below this line */}
        <h1>Active Users: {this.state.activeUsers}</h1>
        {/* Change code above this line */}
      </div>
    );
  }
}

添加事件侦听器

代码语言:javascript
复制
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      message: ''
    };
    this.handleEnter = this.handleEnter.bind(this);
    this.handleKeyPress = this.handleKeyPress.bind(this);
  }
  // Change code below this line
  componentDidMount() {
    document.addEventListener("keydown", this.handleKeyPress);
  }
  componentWillUnmount() {
    document.removeEventListener("keydown", this.handleKeyPress);
  }
  // Change code above this line
  handleEnter() {
    this.setState((state) => ({
      message: state.message + 'You pressed the enter key! '
    }));
  }
  handleKeyPress(event) {
    if (event.keyCode === 13) {
      this.handleEnter();
    }
  }
  render() {
    return (
      <div>
        <h1>{this.state.message}</h1>
      </div>
    );
  }
};

使用 shouldComponentUpdate 优化重新渲染

此方法是优化性能的有用方法。 例如,默认行为是组件在收到新的 props 时重新渲染,即使 props 没有更改也是如此。 你可以使用 shouldComponentUpdate() 通过比较 props 来防止这种情况。 该方法必须返回一个 boolean 值,告诉 React 是否更新组件。 你可以将当前 props( this.props )与下一个props ( nextProps ) 进行比较, 以确定是否需要更新,并相应地返回 truefalse 。 使 OnlyEvens 仅在其新 propsvalue 为偶数时才更新

代码语言:javascript
复制
class OnlyEvens extends React.Component {
  constructor(props) {
    super(props);
  }
  shouldComponentUpdate(nextProps, nextState) {
    console.log('Should I update?');
    // Change code below this line
    if (nextProps.value %2 == 0) {
      return true;
    }
    return false;
    // Change code above this line
  }
  componentDidUpdate() {
    console.log('Component re-rendered.');
  }
  render() {
    return <h1>{this.props.value}</h1>;
  }
}

class Controller extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: 0
    };
    this.addValue = this.addValue.bind(this);
  }
  addValue() {
    this.setState(state => ({
      value: state.value + 1
    }));
  }
  render() {
    return (
      <div>
        <button onClick={this.addValue}>Add</button>
        <OnlyEvens value={this.state.value} />
      </div>
    );
  }
}

不会再显示奇数, 点击 2 次, 从 20 到 22

内联样式(**Inline Styles**)

HTML 中内联样式的示例

代码语言:javascript
复制
<div style="color: yellow; font-size: 16px">Mellow Yellow</div>

JSX 元素使用 style 属性,但由于 JSX 的转译方式,你无法将该值设置为 string 。 相反,你把它设置为等于JavaScript object

代码语言:javascript
复制
<div style={{color: "yellow", fontSize: 16}}>Mellow Yellow</div>

请注意,你可以选择将字体大小设置为数字,省略单位 px ,或将其写为 72px

代码语言:javascript
复制
// Change code above this line
// 需要写在外部
const styles = {
  color: 'purple',
  fontSize: 40,
  border: "2px solid purple",
};

class Colorful extends React.Component {
  render() {
    // Change code below this line

    // 写在这里: undefined, 无法在下面找到, 说明下面 JSX 作用域并不在此方法内, 但很奇怪, 在后面的例子中, answer 又可以直接使用
    // const styles = {
    //   color: 'purple',
    //   fontSize: 40,
    //   border: "2px solid purple",
    // };
    
    return (
      <div style={styles}>Style Me!</div>
    );
    // Change code above this line
  }
};

在 React 渲染方法中使用高级 JavaScript

代码语言:javascript
复制
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',
      'Most likely',
      'Outlook not so good',
      'Very doubtful'
    ];
    const answer = possibleAnswers[this.state.randomIndex]; // Change this line
    // 很奇怪, 为什么这里又可以直接使用 answer
    return (
      <div>
        <input
          type='text'
          value={this.state.userInput}
          onChange={this.handleChange}
          style={inputStyle}
        />
        <br />
        <button onClick={this.ask}>Ask the Magic Eight Ball!</button>
        <br />
        <h3>Answer:</h3>
        <p>
          {/* Change code below this line */}
          {answer}
          {/* Change code above this line */}
        </p>
      </div>
    );
  }
}

使用 if-else 条件渲染

代码语言:javascript
复制
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
    if (this.state.display) {
      return (
       <div>
         <button onClick={this.toggleDisplay}>Toggle Display</button>
         <h1>Displayed!</h1>
       </div>
    );
    } else {
      return (
        <div>
          <button onClick={this.toggleDisplay}>Toggle Display</button>
        </div>
      )
    }
  }
};

使用 && 简化条件

代码语言:javascript
复制
{condition && <p>markup</p>}

如果 conditiontrue ,则将返回标记。 如果条件为 false ,则操作将在计算 condition 后立即返回 false ,并且不返回任何内容。

代码语言:javascript
复制
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 (
       <div>
         <button onClick={this.toggleDisplay}>Toggle Display</button>
         {this.state.display && <h1>Displayed!</h1>}
       </div>
    );
  }
};

使用 三元表达式 进行条件渲染

代码语言:javascript
复制
condition ? expressionIfTrue : expressionIfFalse;
代码语言:javascript
复制
const inputStyle = {
  width: 235,
  margin: 5
};

class CheckUserAge extends React.Component {
  constructor(props) {
    super(props);
    // Change code below this line
    this.state = {
      input: '',
      userAge: ''
    };
    // Change code above this line
    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 = <button onClick={this.submit}>Submit</button>;
    const buttonTwo = <button>You May Enter</button>;
    const buttonThree = <button>You Shall Not Pass</button>;
    return (
      <div>
        <h3>Enter Your Age to Continue</h3>
        <input
          style={inputStyle}
          type='number'
          value={this.state.input}
          onChange={this.handleChange}
        />
        <br />
        {/* Change code below this line */}
        { /* 注意: 可以直接使用 buttonOne, 而不是 <buttonOne /> */ }
        {
          this.state.userAge === ''
            ? buttonOne
            : this.state.userAge >= 18
              ? buttonTwo
              : buttonThree
        }
        {/* Change code above this line */}
      </div>
    );
  }
}

props 有条件地渲染

代码语言:javascript
复制
class Results extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    {/* Change code below this line */}
    return (
      <h1>
        {this.props.fiftyFifty ? "You Win!" : "You Lose!"}
      </h1>
    );
    {/* Change code above this line */}
  }
}

class GameOfChance extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      counter: 1
    };
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    this.setState(prevState => {
      // Complete the return statement:
      return {
        counter: prevState.counter + 1
      }
    });
  }
  render() {
    const expression = Math.random() >= 0.5; // Change this line
    return (
      <div>
        <button onClick={this.handleClick}>Play Again</button>
        {/* Change code below this line */}
        <Results fiftyFifty={expression} />
        {/* Change code above this line */}
        <p>{'Turn: ' + this.state.counter}</p>
      </div>
    );
  }
}

根据组件状态(state)有条件地更改内联 CSS

输入框中键入的文本超过 15 个字符,则将此边框设置为红色样式。

代码语言:javascript
复制
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'
    };
    // Change code below this line
    inputStyle = this.state.input.length > 15 ? { border: '3px solid red' } : inputStyle;
    // Change code above this line
    return (
      <div>
        <h3>Don't Type Too Much:</h3>
        <input
          type="text"
          style={inputStyle}
          value={this.state.input}
          onChange={this.handleChange} />
      </div>
    );
  }
};

使用 Array.map() 动态渲染元素

代码语言:javascript
复制
const textAreaStyles = {
  width: 235,
  margin: 5
};

class MyToDoList extends React.Component {
  constructor(props) {
    super(props);
    // Change code below this line
    this.state = {
      userInput: "",
      toDoList: []
    };
    // Change code above this line
    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleChange = this.handleChange.bind(this);
  }
  handleSubmit() {
    const itemsArray = this.state.userInput.split(',');
    this.setState({
      toDoList: itemsArray
    });
  }
  handleChange(e) {
    this.setState({
      userInput: e.target.value
    });
  }
  render() {
    const items = this.state.toDoList.map(item => <li>{item}</li>); // Change this line
    return (
      <div>
        <textarea
          onChange={this.handleChange}
          value={this.state.userInput}
          style={textAreaStyles}
          placeholder='Separate Items With Commas'
        />
        <br />
        <button onClick={this.handleSubmit}>Create List</button>
        <h1>My "To Do" List:</h1>
        <ul>{items}</ul>
      </div>
    );
  }
}

为同级元素提供唯一的 key 属性

创建元素数组时,每个元素都需要将 key 属性设置为唯一值。 React 使用这些键来跟踪添加、更改或删除了哪些项。 这有助于在以任何方式修改列表时使重新渲染过程更高效。 注意:key 只需要在同级元素之间是唯一的,它们在应用程序中不需要全局唯一。 通常,你希望使 key 能唯一标识正在呈现的元素。 作为最后的手段,可以使用数组索引,但通常应尝试使用唯一标识。

代码语言:javascript
复制
const frontEndFrameworks = [
  'React',
  'Angular',
  'Ember',
  'Knockout',
  'Backbone',
  'Vue'
];

function Frameworks() {
  const renderFrameworks = frontEndFrameworks.map((item, index) => 
    <li key={index}>{item}</li>
  ); // Change this line
  return (
    <div>
      <h1>Popular Front End JavaScript Frameworks</h1>
      <ul>
        {renderFrameworks}
      </ul>
    </div>
  );
};

使用 Array.filter() 动态过滤数组

代码语言:javascript
复制
// 取出 users 中所有在线用户: 
// online: Boolean
let onlineUsers = users.filter(user => user.online);
代码语言:javascript
复制
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 => user.online); // Change this line
    const renderOnline = usersOnline.map((item, index) => <li key={index}>{item.username}</li>); // Change this line
    return (
      <div>
        <h1>Current Online Users:</h1>
        <ul>{renderOnline}</ul>
      </div>
    );
  }
}

使用 renderToString 在服务端渲染 React

至此, 渲染 React 组件都是在客户端(客户浏览器), 通常来说你完全可以这么做, 然而,有些情况需要渲染 React 组件在服务端, 因为 React 是一个 JavaScript 视图库,并且你可以使用 Node.js 在服务端运行 JavaScript, 事实上, React 提供了 renderToString() 方法用于此种情况。

代码语言:javascript
复制
class App extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return <div/>
  }
};

// Change code below this line
ReactDOMServer.renderToString(<App />);

Redux

创建 Redux Store

Redux 是一个状态管理框架,可以与许多不同的 Web 技术一起使用,包括 React 在 Redux 中,有一个状态对象负责应用程序的整个状态。 这意味着, 如果你有一个包含十个组件的 React 应用程序, 并且每个组件都有自己的本地状态, 则应用程序的整个状态将由 Redux store 中的单个状态对象定义。 这也意味着, 每当应用的任何部分想要更新状态时, 它都必须通过 Redux store 来实现。 通过单向数据流,可以更轻松地跟踪应用中的状态管理。 Redux store 是保存和管理应用程序 state 的对象。 Redux 对象上有一个名为 createStore() 的方法,用于创建 Redux store 。 此方法将 reducer 函数作为必需参数。 reducer 函数将在后面的挑战中介绍,并且已经在代码编辑器中为您定义。 它只是将 state 作为参数并返回 state 。 声明一个 store 变量并将其分配给 createStore() 方法, 将 reducer 作为参数传入。

代码语言:javascript
复制
// ES6 默认参数语法: 将 state 初始化为 5
const reducer = (state = 5) => {
  return state;
}

// Redux methods are available from a Redux object
// For example: Redux.createStore()
// Define the store here:
const store = Redux.createStore(reducer)

Redux Store 中获取状态

使用 getState() 方法获取 Redux store 对象中保存的当前 state 使用 store.getState()store 中获取 state ,并将其分配给新的变量 currentState

代码语言:javascript
复制
const store = Redux.createStore(
  (state = 5) => state
);

// Change code below this line
let currentState = store.getState();

定义 Redux Action

由于 Redux 是一个状态管理框架,因此更新状态是其核心任务之一。 在 Redux 中,所有状态更新都由调度操作(dispatching actions)触发。 Action 只是一个 JavaScript 对象,其中包含有关已发生的 action 事件的信息。 Redux store 接收这些 action 对象,然后相应地更新其状态。 有时 Redux action 也会携带一些数据。 例如,该 action 在用户登录后携带用户名。 虽然数据是可选的,但 action 必须带有指定所发生 action 的 'type' 的 type 属性。 将 Redux action 视为将有关应用中发生的事件的信息传递到 Redux store 的信使。 然后,store 根据发生的 action 执行更新状态的业务。 编写 Redux action 就像使用类型属性声明对象一样简单。 声明一个对象 action 并为其指定一个设置为字符串 'LOGIN' 的属性 type

代码语言:javascript
复制
// Define an action here:
let action = {
  type: 'LOGIN'
};

定义 Action 创建者

创建 Action 后,下一步是将操作发送到 Redux store,以便它可以更新其状态。 在 Redux 中,您可以定义 Action 创建者来完成此操作。 Action 创建者只是一个返回 Action 的 JavaScript 函数。 换句话说,Action 创建者创建表示 Action 事件的对象。 定义一个名为 actionCreator() 的函数,该函数在调用时返回 action 对象。

代码语言:javascript
复制
const action = {
  type: 'LOGIN'
}
// Define an action creator here:
function actionCreator() {
  return action;
}

Dispatch an Action Event

dispatch 方法是用于将 Action 调度到 Redux store 的方法。 调用 store.dispatch() 并传递从 Action 创建者返回的值会将操作发送回 store。 回想一下,Action 创建者返回一个对象,该对象具有指定已发生的 Action 的类型属性。 然后,该方法将操作对象调度到 Redux 存储区。 根据前面挑战的示例,以下行是等效的,并且都调度类型 LOGIN 的 Action:

代码语言:javascript
复制
store.dispatch(actionCreator());
store.dispatch({ type: 'LOGIN' });

代码编辑器中的 Redux store 具有初始化状态, 该状态是包含当前设置为 falselogin 属性的对象。 还有一个名为 loginAction() 的 action 创建器,它返回类型 LOGIN 的 action。 通过调用 dispatch 方法将 LOGIN action 调度到 Redux store, 并传入 loginAction() 创建的 action。

代码语言:javascript
复制
const store = Redux.createStore(
  (state = {login: false}) => state
);

const loginAction = () => {
  return {
    type: 'LOGIN'
  }
};

// Dispatch the action here:
store.dispatch(loginAction());

处理 Store 中的 Action

创建 action 并 dispatch 后, Redux store 需要知道如何响应 action。 这是 reducer 函数的工作, Redux 中的 reducer 负责为响应 action 而发生的状态修改。 reducer state action 作为参数,并且始终返回新的 state Redux 中的另一个关键原则是 state 是只读的。 换句话说, reducer 函数必须始终返回 state 的新副本,并且从不直接修改状态。 Redux 不强制要求状态不可变性,但是,你负责在 reducer 函数的代码中强制执行它。

代码语言:javascript
复制
const defaultState = {
  login: false
};

const reducer = (state = defaultState, action) => {
  // Change code below this line
  if (action.type === "LOGIN") {
    return {
      login: true
    };
  } else {
    return state;
  }
  // Change code above this line
};

const store = Redux.createStore(reducer);

const loginAction = () => {
  return {
    type: 'LOGIN'
  }
};

使用 switch 语句处理多个 Action

代码语言:javascript
复制
const defaultState = {
  authenticated: false
};

const authReducer = (state = defaultState, action) => {
  // Change code below this line
  switch (action.type) {
    case "LOGIN":
      return {
        authenticated: true
      };
    case "LOGOUT":
      return {
        authenticated: false
      };
    default:
      return defaultState;
  }
  // Change code above this line
};

const store = Redux.createStore(authReducer);

const loginUser = () => {
  return {
    type: 'LOGIN'
  }
};

const logoutUser = () => {
  return {
    type: 'LOGOUT'
  }
};

将 const 用于 Action 类型

使用 Redux 时的常见做法是将 Action 类型分配为只读常量, 然后在使用这些常量的位置引用这些常量。 可以重构正在使用的代码,以将 Action 类型编写为 const 声明。 将 LOGINLOGOUT 声明为 const 值, 并分别将它们分配给字符串 'LOGIN''LOGOUT' 。 然后,编辑 authReducer() 和 Action 创建者以引用这些常量而不是字符串值。 注意:通常约定以 全大写 形式写入常量,这也是 Redux 中的标准做法。

代码语言:javascript
复制
const LOGIN = "LOGIN";
const LOGOUT = "LOGOUT";

const defaultState = {
  authenticated: false
};

const authReducer = (state = defaultState, action) => {

  switch (action.type) {
    case LOGIN: 
      return {
        authenticated: true
      }
    case LOGOUT: 
      return {
        authenticated: false
      }

    default:
      return state;

  }

};

const store = Redux.createStore(authReducer);

const loginUser = () => {
  return {
    type: LOGIN
  }
};

const logoutUser = () => {
  return {
    type: LOGOUT
  }
};

注册 store 侦听器

你有权访问 Redux store 对象的另一个方法是 store.subscribe() 。 这允许你将侦听器函数订阅到 store, 每当针对 store dispatch action 时都会调用这些函数。 此方法的一个简单用途是向您的 store 订阅一个函数, 该函数仅在每次收到 action 并更新 store 时记录一条消息。 编写一个回调函数,每次 store 收到 action 时递增全局变量 count , 并将此函数传递给 store.subscribe() 方法。 你将看到连续调用 store.dispatch() 三次, 每次都直接传入一个 action 对象。 观察 action dispatch 之间的控制台输出,以查看更新的发生情况。

代码语言:javascript
复制
const ADD = 'ADD';

const reducer = (state = 0, action) => {
  switch(action.type) {
    case ADD:
      return state + 1;
    default:
      return state;
  }
};

const store = Redux.createStore(reducer);

// Global count variable:
let count = 0;

// Change code below this line
store.subscribe(() => count++);
// Change code above this line

store.dispatch({type: ADD});
console.log(count);
store.dispatch({type: ADD});
console.log(count);
store.dispatch({type: ADD});
console.log(count);

组合多个 reducer

当应用的状态开始变得更加复杂时,可能很容易将状态划分为多个部分。 相反,请记住 Redux 的第一个原则: 所有应用状态都保存在 store 中的单个状态对象中。 因此,Redux 提供了 reducer 组合作为复杂状态模型的解决方案。 定义多个 reducer 来处理应用程序状态的不同部分, 然后将这些 reducer 组合成一个根 reducer (root reducer)。 然后将根 reducer 传递到 Redux createStore() 方法中。 为了让我们将多个 reducer 组合在一起,Redux 提供了 combineReducers() 方法。 此函数接受对象作为参数,您可以在其中定义将键关联到特定 reducer 函数的属性。 例如,在具有用户身份验证的笔记应用中, 一个 reducer 可以处理身份验证, 而另一个 reducer 可以处理用户正在提交的文本和笔记。

代码语言:javascript
复制
const rootReducer = Redux.combineReducers({
  auth: authenticationReducer,
  notes: notesReducer
});
代码语言:javascript
复制
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';

const counterReducer = (state = 0, action) => {
  switch(action.type) {
    case INCREMENT:
      return state + 1;
    case DECREMENT:
      return state - 1;
    default:
      return state;
  }
};

const LOGIN = 'LOGIN';
const LOGOUT = 'LOGOUT';

const authReducer = (state = {authenticated: false}, action) => {
  switch(action.type) {
    case LOGIN:
      return {
        authenticated: true
      }
    case LOGOUT:
      return {
        authenticated: false
      }
    default:
      return state;
  }
};

const rootReducer = Redux.combineReducers({
  count: counterReducer,
  auth: authReducer
}); // Define the root reducer here

const store = Redux.createStore(rootReducer);

发送 Action 数据到 Store

代码语言:javascript
复制
const ADD_NOTE = 'ADD_NOTE';

const notesReducer = (state = 'Initial State', action) => {
  switch(action.type) {
    // Change code below this line
    case ADD_NOTE:
      return action.text;
    // Change code above this line
    default:
      return state;
  }
};

const addNoteText = (note) => {
  // Change code below this line
  return {
    type: ADD_NOTE,
    text: note
  };
  // Change code above this line
};

const store = Redux.createStore(notesReducer);

console.log(store.getState());
store.dispatch(addNoteText('Hello!'));
console.log(store.getState());

使用中间件处理异步 Action

代码语言:javascript
复制
const REQUESTING_DATA = 'REQUESTING_DATA'
const RECEIVED_DATA = 'RECEIVED_DATA'

const requestingData = () => { return {type: REQUESTING_DATA} }
const receivedData = (data) => { return {type: RECEIVED_DATA, users: data.users} }

const handleAsync = () => {
  return function(dispatch) {
    // Dispatch request action here
    dispatch(requestingData());
    setTimeout(function() {
      let data = {
        users: ['Jeff', 'William', 'Alice']
      }
      // Dispatch received data action here
      dispatch(receivedData(data));
    }, 2500);
  }
};

const defaultState = {
  fetching: false,
  users: []
};

const asyncDataReducer = (state = defaultState, action) => {
  switch(action.type) {
    case REQUESTING_DATA:
      return {
        fetching: true,
        users: []
      }
    case RECEIVED_DATA:
      return {
        fetching: false,
        users: action.users
      }
    default:
      return state;
  }
};

const store = Redux.createStore(
  asyncDataReducer,
  Redux.applyMiddleware(ReduxThunk.default)
);

用 Redux 写一个计数器

代码语言:javascript
复制
// Define a constant for increment action types
const INCREMENT = "INCREMENT"; 
// Define a constant for decrement action types
const DECREMENT = "DECREMENT"; 

// Define the counter reducer which will increment or decrement the state based on the action it receives
const counterReducer = (state = 0, action) => {
  switch(action.type) {
    case INCREMENT:
      return state + 1;
    case DECREMENT:
      return state - 1;
    default:
      return state;
  }
}; 

// Define an action creator for incrementing
const incAction = () => {
  return {
    type: INCREMENT
  };
}; 

// Define an action creator for decrementing
const decAction = () => {
  return {
    type: DECREMENT
  };
}; 

// Define the Redux store here, passing in your reducers
const store = Redux.createStore(counterReducer); 

不可变 state

不可变状态意味着你从不直接修改状态,而是返回一个新的状态副本。 Redux 不会主动在其 store 或 reducer 中强制执行状态不变性, 该责任落在程序员身上。 代码编辑器中有一个 storereducer 用于管理待办事项。 完成在 reducer 中写入案例 ADD_TO_DO 以将新的待办事项附加到状态。 有几种方法可以使用标准 JavaScript 或 ES6 来完成此操作。 看看你是否可以找到一种方法来返回一个新数组, 其中的项目 action.todo 附加到末尾。 由于 Redux 中的状态不变性, 此挑战的目标是在 reducer 函数中返回一个新的状态副本。

代码语言:javascript
复制
const ADD_TO_DO = 'ADD_TO_DO';

// A list of strings representing tasks to do:
const todos = [
  'Go to the store',
  'Clean the house',
  'Cook dinner',
  'Learn to code',
];

const immutableReducer = (state = todos, action) => {
  switch(action.type) {
    case ADD_TO_DO:
      // Don't mutate state here or the tests will fail
      // 拼接, 加在数组最后
      // return state.concat(action.todo);
      // 或 ES6 展开运算符
      return [...state, action.todo]
    default:
      return state;
  }
};

const addToDo = (todo) => {
  return {
    type: ADD_TO_DO,
    todo
  }
}

const store = Redux.createStore(immutableReducer);

在数组上使用展开运算符

... 展开运算符有多种应用,其中之一非常适合 从现有数组生成新数组

代码语言:javascript
复制
let newArray = [...myArray];

newArray 现在是克隆 myArray。 两个数组仍然单独存在于内存中。 如果你执行类似 newArray.push(5) , myArray 则不会改变。

代码语言:javascript
复制
const immutableReducer = (state = ['Do not mutate state!'], action) => {
  switch(action.type) {
    case 'ADD_TO_DO':
      // Don't mutate state here or the tests will fail
      let newArray = [...state, action.todo];
      return newArray;
    default:
      return state;
  }
};

const addToDo = (todo) => {
  return {
    type: 'ADD_TO_DO',
    todo
  }
}

const store = Redux.createStore(immutableReducer);

从数组中删除一个项

代码语言:javascript
复制
const immutableReducer = (state = [0,1,2,3,4,5], action) => {
  switch(action.type) {
    case 'REMOVE_ITEM':
      // Don't mutate state here or the tests will fail
      return [
        ...state.slice(0, action.index),
        ...state.slice(action.index + 1, state.length)
      ];
      // 或
      // return state.slice(0, action.index).concat(state.slice(action.index + 1, state.length));
      // 或
      // return state.filter((_, index) => index !== action.index);
    default:
      return state;
  }
};

const removeItem = (index) => {
  return {
    type: 'REMOVE_ITEM',
    index
  }
}

const store = Redux.createStore(immutableReducer);

使用 Object.assign 复制一个对象

Object.assign()获取目标对象和源对象并将属性从源对象映射到目标对象。 任何匹配的属性都会被源对象中的属性覆盖。 此行为通常用于通过传递一个空对象作为第一个参数, 然后传递要复制的对象来制作对象的浅表副本。 这是一个例子:

代码语言:javascript
复制
const newObject = Object.assign({}, obj1, obj2);
代码语言:javascript
复制
const defaultState = {
  user: 'CamperBot',
  status: 'offline',
  friends: '732,982',
  community: 'freeCodeCamp'
};

const immutableReducer = (state = defaultState, action) => {
  switch(action.type) {
    case 'ONLINE':
      // Don't mutate state here or the tests will fail
      return Object.assign({}, state, { status: "online" });
    default:
      return state;
  }
};

const wakeUp = () => {
  return {
    type: 'ONLINE'
  }
};

const store = Redux.createStore(immutableReducer);

将 Redux 与 React 结合使用

开始使用 React Redux

代码语言:javascript
复制
class DisplayMessages extends React.Component {
  // Change code below this line
  constructor(props) {
    super(props);
    this.state = {
      input: "",
      messages: []
    };
  }
  // Change code above this line
  render() {
    return <div />
  }
};

首先在本地管理 state

代码语言:javascript
复制
class DisplayMessages extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      input: '',
      messages: []
    };
    this.handleChange = this.handleChange.bind(this);
    this.submitMessage = this.submitMessage.bind(this);
  }
  // Add handleChange() and submitMessage() methods here
  handleChange(event) {
    // 无需更新 messages, 会自动合并更新, 不会导致 messages 丢失
    this.setState({
      input: event.target.value
    });
  }

  submitMessage() {
    this.setState(state => ({
      input: "",
      messages: [...state.messages, state.input]
    }));
  }

  render() {
    return (
      <div>
        <h2>Type in a new Message:</h2>
        { /* Render an input, button, and ul below this line */ }
        <input onChange={this.handleChange} value={this.state.input} />
        <button onClick={this.submitMessage}>Add message</button>
        <ul>
          {
            this.state.messages.map((m, i) => 
              (
                <li key={i}>{m}</li>
              )
            )
          }
        </ul>
        { /* Change code above this line */ }
      </div>
    );
  }
};

将状态(state)逻辑提取到 Redux

代码语言:javascript
复制
// Define ADD, addMessage(), messageReducer(), and store here:
const ADD = "ADD";

const addMessage = message => {
  return {
    type: ADD,
    message
  };
};

// Use ES6 default paramter to give the 'previousState' parameter an initial value.
const messageReducer = (previousState = [], action) => {
  // Use switch statement to lay out the reducer logic in response to different action type
  switch (action.type) {
    case ADD:
      // Use ES6 spread operator to return a new array where the new message is added to previousState
      return [...previousState, action.message];
    default:
      // A default case to fall back on in case if the update to Redux store is not for this specific state.
      return previousState;
  }
};

const store = Redux.createStore(messageReducer);

使用 Provider 将 Redux 连接到 React

下一步是提供对 Redux store 的 React 访问以及调度(dispatch)更新所需的 action。 React Redux 提供了它的 react-redux 包来帮助完成这些任务。 React Redux 提供了一个具有两个关键特性的小型 API: ProviderconnectProvider 需要两个 props,即 Redux store 和应用的子组件。 为 App 组件定义 Provider 可能如下所示:

代码语言:javascript
复制
<Provider store={store}>
  <App/>
</Provider>
代码语言:javascript
复制
// Redux:
const ADD = 'ADD';

const addMessage = (message) => {
  return {
    type: ADD,
    message
  }
};

const messageReducer = (state = [], action) => {
  switch (action.type) {
    case ADD:
      return [
        ...state,
        action.message
      ];
    default:
      return state;
  }
};



const store = Redux.createStore(messageReducer);

// React:

class DisplayMessages extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      input: '',
      messages: []
    }
    this.handleChange = this.handleChange.bind(this);
    this.submitMessage = this.submitMessage.bind(this);
  }
  handleChange(event) {
    this.setState({
      input: event.target.value
    });
  }
  submitMessage() {  
    this.setState((state) => {
      const currentMessage = state.input;
      return {
        input: '',
        messages: state.messages.concat(currentMessage)
      };
    });
  }
  render() {
    return (
      <div>
        <h2>Type in a new Message:</h2>
        <input
          value={this.state.input}
          onChange={this.handleChange}/><br/>
        <button onClick={this.submitMessage}>Submit</button>
        <ul>
          {this.state.messages.map( (message, idx) => {
              return (
                 <li key={idx}>{message}</li>
              )
            })
          }
        </ul>
      </div>
    );
  }
};

const Provider = ReactRedux.Provider;

class AppWrapper extends React.Component {
  // Render the Provider below this line
  render() {
    return (
      <Provider store={store}>
        <DisplayMessages />
      </Provider>
    );
  }
  // Change code above this line
};

将 state 映射到 props

Provider 组件允许您为 React 组件提供 statedispatch , 但你必须准确指定所需的 state 和 action。 这样,您可以确保每个组件只能访问它所需的 state。 你可以通过创建两个函数来实现此目的: mapStateToProps()mapDispatchToProps()

代码语言:javascript
复制
const state = [];

// Change code below this line
const mapStateToProps = (state) => {
  return {
    messages: state
  };
};

将 dispatch 映射到 props

mapDispatchToProps() 函数用于为你的 React 组件提供特定的操作(action)创建者, 以便它们可以针对 Redux store 调度(dispatch) 操作(action)。 一个示例

代码语言:javascript
复制
{
  submitLoginUser: function(username) {
    dispatch(loginUser(username));
  }
}
代码语言:javascript
复制
const addMessage = (message) => {
  return {
    type: 'ADD',
    message: message
  }
};

// Change code below this line
const mapDispatchToProps = (dispatch) => {
  return {
    submitNewMessage: (message) => {
      dispatch(addMessage(message));
    }
  };
};

将 Redux 连接到 React

代码语言:javascript
复制
connect(mapStateToProps, mapDispatchToProps)(MyComponent)
代码语言:javascript
复制
const addMessage = (message) => {
  return {
    type: 'ADD',
    message: message
  }
};

const mapStateToProps = (state) => {
  return {
    messages: state
  }
};

const mapDispatchToProps = (dispatch) => {
  return {
    submitNewMessage: (message) => {
      dispatch(addMessage(message));
    }
  }
};

class Presentational extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return <h3>This is a Presentational Component</h3>
  }
};

const connect = ReactRedux.connect;
// Change code below this line
const ConnectedComponent = connect(mapStateToProps, mapDispatchToProps)(Presentational);

将 Redux 连接到 "消息" 应用

type-in-a-new-message.png

代码语言:javascript
复制
// Redux:
const ADD = 'ADD';

const addMessage = (message) => {
  return {
    type: ADD,
    message: message
  }
};

const messageReducer = (state = [], action) => {
  switch (action.type) {
    case ADD:
      return [
        ...state,
        action.message
      ];
    default:
      return state;
  }
};

const store = Redux.createStore(messageReducer);

// React:
class Presentational extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      input: '',
      messages: []
    }
    this.handleChange = this.handleChange.bind(this);
    this.submitMessage = this.submitMessage.bind(this);
  }
  handleChange(event) {
    this.setState({
      input: event.target.value
    });
  }
  submitMessage() {
    this.setState((state) => {
      const currentMessage = state.input;
      return {
        input: '',
        messages: state.messages.concat(currentMessage)
      };
    });
  }
  render() {
    return (
      <div>
        <h2>Type in a new Message:</h2>
        <input
          value={this.state.input}
          onChange={this.handleChange}/><br/>
        <button onClick={this.submitMessage}>Submit</button>
        <ul>
          {this.state.messages.map( (message, idx) => {
              return (
                 <li key={idx}>{message}</li>
              )
            })
          }
        </ul>
      </div>
    );
  }
};

// React-Redux:
const mapStateToProps = (state) => {
  return { messages: state }
};

const mapDispatchToProps = (dispatch) => {
  return {
    submitNewMessage: (newMessage) => {
       dispatch(addMessage(newMessage))
    }
  }
};

const Provider = ReactRedux.Provider;
const connect = ReactRedux.connect;

// Define the Container component here:
const Container = connect(mapStateToProps, mapDispatchToProps)(Presentational);

class AppWrapper extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    // Complete the return statement:
    return (
      <Provider store={store}>
        <Container />
      </Provider>
    );
  }
};

将本地 state 提取到 Redux 中

现在 Redux 已连接, 你需要将状态管理从 Presentational 组件中提取到 Redux 中。 目前,你已连接 Redux, 但你正在 Presentational 组件中本地处理状态。 在 Presentational 组件中, 首先删除本地 state 中的 messages 属性。 这些消息将由 Redux 管理。 接下来,修改 submitMessage() 方法, 使其从 this.props 调度 submitNewMessage() , 并将来自本地 state 的当前消息输入作为参数传入。 由于你从本地状态中删除了 messages , 因此也在此处从对 this.setState() 的调用中删除了 messages 属性。 最后,修改 render() 方法, 使其映射从 props 而不是 state 接收的消息。 进行这些更改后,应用将继续正常运行,但 Redux 管理状态除外。 此示例还说明了组件如何具有本地 state : 你的组件仍然在其自己的 state 中本地跟踪用户输入。 你可以看到 Redux 如何在 React 之上提供一个有用的状态管理框架。 一开始,你只使用 React 的本地状态就获得了相同的结果, 这通常可以通过简单的应用程序来实现。 但是,随着你的应用程序变得更大、更复杂, 你的状态管理也会变得更复杂,这就是 Redux 解决的问题。 本地 state 管理 input, 其它交给 Redux (message 存到 Redux store), 连接 React 与 Redux: 1. 将 Redux state 映射到 React 的 props 中Redux state 存储数据 React 从 props 中访问 Redux 存储的状态数据将 Redux dispatch 映射到 React 的 props 中Redux dispatch 更新状态数据 React 从 props 中取出来更新 Redux 管理的状态数据

代码语言:javascript
复制
// Redux:
const ADD = 'ADD';

const addMessage = (message) => {
  return {
    type: ADD,
    message: message
  }
};

const messageReducer = (state = [], action) => {
  switch (action.type) {
    case ADD:
      return [
        ...state,
        action.message
      ];
    default:
      return state;
  }
};

const store = Redux.createStore(messageReducer);

// React:
const Provider = ReactRedux.Provider;
const connect = ReactRedux.connect;

// Change code below this line
class Presentational extends React.Component {
  constructor(props) {
    super(props);
    // Remove property 'messages' from Presentational's local state
    this.state = {
      input: '',
      // messages: []
    }
    this.handleChange = this.handleChange.bind(this);
    this.submitMessage = this.submitMessage.bind(this);
  }
  handleChange(event) {
    this.setState({
      input: event.target.value
    });
  }
  submitMessage() {
    // Call 'submitNewMessage', which has been mapped to Presentational's props, with a new message;
    // meanwhile, remove the 'messages' property from the object returned by this.setState().
    this.props.submitNewMessage(this.state.input);
    this.setState({
      input: ''
    });
  }
  render() {
    return (
      <div>
        <h2>Type in a new Message:</h2>
        <input
          value={this.state.input}
          onChange={this.handleChange}/><br/>
        <button onClick={this.submitMessage}>Submit</button>
        <ul>
          {/* The messages state is mapped to Presentational's props; therefore, when rendering,
               you should access the messages state through props, instead of Presentational's
               local state. */}
          {this.props.messages.map( (message, idx) => {
              return (
                 <li key={idx}>{message}</li>
              )
            })
          }
        </ul>
      </div>
    );
  }
};
// Change code above this line

const mapStateToProps = (state) => {
  return {messages: state}
};

const mapDispatchToProps = (dispatch) => {
  return {
    submitNewMessage: (message) => {
      dispatch(addMessage(message))
    }
  }
};

const Container = connect(mapStateToProps, mapDispatchToProps)(Presentational);

class AppWrapper extends React.Component {
  render() {
    return (
      <Provider store={store}>
        <Container/>
      </Provider>
    );
  }
};

Moving Forward From Here

代码语言:javascript
复制
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider, connect } from 'react-redux'
import { createStore, combineReducers, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'

import rootReducer from './redux/reducers'
import App from './components/App'

const store = createStore(
  rootReducer,
  applyMiddleware(thunk)
);

ReactDOM.render(
  <Provider store={store}>
    <App/>
  </Provider>,
  document.getElementById('root')
);


// Only change code below this line
console.log("Now I know React and Redux!");

Projects

构建随机名言机

参考:

Q&A

补充

React.js Examples

参考:

实现 Sass 注释的三种方式

参考:

1. // 注释内容

在 Sass 中,这种注释方式在 编译后不会保留下来

2. /* 注释内容 */

在 Sass 中,这种注释方式在 编译后会保留下来。 因为这种注释方式跟 CSS 注释方式是相同的,所以编译后会保留下来。

3. /*! 注释内容 */

我们都知道压缩工具会删除所有的注释, 有些时候为了保留一些版权声明的注释说明,可以采用以下方式:

代码语言:javascript
复制
/*! 注释内容 */

也就是说在注释内容前面加上一个 !,这种压缩工具就不会删除这条注释信息了。 不过这种注释方式用得很少,一般在 CSS 文件顶部为了声明版权信息才会使用。 Sass

代码语言:javascript
复制
/*! Copyright ©2023-present yiyun, All Rights Reserved */
$height: 20px;

body {
  height: $height;
  line-height: $height;
}

编译出来的 CSS 代码如下: CSS

代码语言:javascript
复制
/*! Copyright ©2023-present yiyun, All Rights Reserved */
body {
  height: 20px;
  line-height: 20px;
}

参考

感谢帮助!

嵌套 CSS

传统 CSS 中, 通常,每个元素都针对不同的行来设置其样式,如下所示:

代码语言:javascript
复制
nav {
  background-color: red;
}

nav ul {
  list-style: none;
}

nav ul li {
  display: inline-block;
}

对于大型项目,CSS 文件将有许多行和规则。 这就是嵌套可以通过在相应的父元素中放置子样式规则来帮助组织代码的地方:

代码语言:javascript
复制
nav {
  background-color: red;

  ul {
    list-style: none;

    li {
      display: inline-block;
    }
  }
}

实践

代码语言:javascript
复制
<style type='text/scss'>
  .blog-post {
    h1 {
      text-align: center;
      color: blue;
    }
    p {
      font-size: 20px;
    }
  }
</style>

<div class="blog-post">
  <h1>Blog Title</h1>
  <p>This is a paragraph</p>
</div>

使用 Mixins 创建可重用的 CSS

在 Sass 中,mixin 是一组可以在整个样式表中重用的 CSS 声明。 较新的 CSS 功能需要时间才能完全采用并准备好在所有浏览器中使用。 随着功能添加到浏览器中,使用它们的 CSS 规则可能需要供应商前缀。 考虑 box-shadow

代码语言:javascript
复制
div {
  -webkit-box-shadow: 0px 0px 4px #fff;
  -moz-box-shadow: 0px 0px 4px #fff;
  -ms-box-shadow: 0px 0px 4px #fff;
  box-shadow: 0px 0px 4px #fff;
}

为所有具有 box-shadow 的元素重写此规则或更改每个值以测试不同的效果需要大量键入。 Mixins 就像 CSS 的函数。 如下所示:

代码语言:javascript
复制
@mixin box-shadow($x, $y, $blur, $c){ 
  -webkit-box-shadow: $x $y $blur $c;
  -moz-box-shadow: $x $y $blur $c;
  -ms-box-shadow: $x $y $blur $c;
  box-shadow: $x $y $blur $c;
}

定义以 @mixin 开头,后跟自定义名称。 参数(上例中的 $x$y$blur$c )是可选的。 现在,每当需要 box-shadow 规则时, 只需一行调用 mixin 即可取代键入所有 vendor 前缀的行。 使用 @include 指令调用 mixin

代码语言:javascript
复制
div {
  @include box-shadow(0px, 0px, 4px, #fff);
}

实践 为 border-radius 编写一个 mixin 并为其指定一个 $radius 参数。 它应使用示例中的所有 vendor 前缀。 然后使用 border-radius mixin 使 #awesome 元素的边框半径为 15px

代码语言:javascript
复制
<style type='text/scss'>

  @mixin border-radius($radius) {
    -webkit-border-radius: $radius;
    -moz-border-radius: $radius;
    -ms-border-radius: $radius;
    border-radius: $radius;
  }

  #awesome {
    width: 150px;
    height: 150px;
    background-color: green;
    @include border-radius(15px);
  }
</style>

<div id="awesome"></div>

补充 参考:Sass: @mixin and @include (sass-lang.com)

代码语言:javascript
复制
/* 定义函数 */
@mixin custom-mixin-name($param1, $param2, ....) {
    // CSS Properties Here...
}
代码语言:javascript
复制
/* 使用函数 */
element {
    @include custom-mixin-name(value1, value2, ....);
}

一个官网示例: Sass: @mixin and @include 可选参数 CSS

代码语言:javascript
复制
.mail-icon {
  text-indent: -99999em;
  overflow: hidden;
  text-align: left;
  background-image: url("/images/mail.svg");
  background-repeat: no-repeat;
  background-position: 0 50%;
}

SCSS

代码语言:javascript
复制
@mixin replace-text($image, $x: 50%, $y: 50%) {
  text-indent: -99999em;
  overflow: hidden;
  text-align: left;

  background: {
    image: $image;
    repeat: no-repeat;
    position: $x $y;
  }
}

.mail-icon {
  @include replace-text(url("/images/mail.svg"), 0);
}

Sass

代码语言:javascript
复制
@mixin replace-text($image, $x: 50%, $y: 50%)
  text-indent: -99999em
  overflow: hidden
  text-align: left

  background:
    image: $image
    repeat: no-repeat
    position: $x $y

.mail-icon
  @include replace-text(url("/images/mail.svg"), 0)

使用 @if @else 为样式添加逻辑

Sass 中的 @if 指令对于测试特定情况很有用 它的工作方式就像 JavaScript 中的 if 语句一样。

代码语言:javascript
复制
@mixin make-bold($bool) {
  @if $bool == true {
    font-weight: bold;
  }
}

就像在 JavaScript 中一样, @else if@else 指令测试更多条件:

代码语言:javascript
复制
@mixin text-effect($val) {
  @if $val == danger {
    color: red;
  }
  @else if $val == alert {
    color: yellow;
  }
  @else if $val == success {
    color: green;
  }
  @else {
    color: black;
  }
}

实践 创建一个名为 border-stroke 的 mixin,该 mixin 采用参数 $val 。 mixin 应使用 @if@else if@else 指令检查以下条件:

代码语言:javascript
复制
light - 1px solid black
medium - 3px solid black
heavy - 6px solid black

如果 $val 参数值不是 lightmediumheavy , 则 border 属性应设置为 none

代码语言:javascript
复制
<style type='text/scss'>

  @mixin border-stroke($val) {
    @if $val == light {
      border: 1px solid black;
    }
    @else if $val == medium {
      border: 3px solid black;
    }
    @else if $val == heavy {
      border: 6px solid black;
    }
    @else {
      border: none;
    }
  }

  #box {
    width: 150px;
    height: 150px;
    background-color: red;
    @include border-stroke(medium);
  }
</style>

<div id="box"></div>

使用 @for 创建 Sass 循环

@for 有两种方式:

  1. from A through B
    • [A, B]
  2. from A to B
    • [A, b)
代码语言:javascript
复制
@for $i from 1 through 3 {
  // some CSS
}

// 1 2 3
代码语言:javascript
复制
@for $i from 1 to 3 {
  // some CSS
}

// 1 2

PS:可以直接带上单位 (%, px 等) 进行计算

代码语言:javascript
复制
@for $i from 1 through 12 {
  .col-#{$i} { width: 100%/12 * $i; }
}

@for $i from 1 to 12 {
  .col-#{$i} { width: 100%/12 * $i; }
}

#{$i} 部分是将变量 ( i ) 与文本组合以生成字符串 的语法。 当 Sass 文件转换为 CSS 时,它看起来像这样:

代码语言:javascript
复制
.col-1 {
  width: 8.33333%;
}

.col-2 {
  width: 16.66667%;
}

...

.col-12 {
  width: 100%;
}

这是一个创建 grid 布局的好方法, 实践 使用 @for 指令用 $j16 (不包括 6), 创建 5 个类,从 .text-1.text-5 , 每个都有 font-size 值为 15px*$j

代码语言:javascript
复制
<style type='text/scss'>

@for $j from 1 to 6 {
  .text-#{$j} {
    font-size: 15px * $j;
  }
}

</style>

<p class="text-1">Hello</p>
<p class="text-2">Hello</p>
<p class="text-3">Hello</p>
<p class="text-4">Hello</p>
<p class="text-5">Hello</p>

使用 @each 映射列表中的项

Sass 还提供了 @each 循环遍历列表 或 映射中每个项 的指令。 在每次迭代中,变量被分配给列表或映射中的当前值。

代码语言:javascript
复制
@each $color in blue, red, green {
  .#{$color}-text {color: $color;}
}

映射 的语法略有不同。 一个例子:

代码语言:javascript
复制
$colors: (color1: blue, color2: red, color3: green);

@each $key, $color in $colors {
  .#{$color}-text {color: $color;}
}

请注意,需要该 $key 变量来引用映射中的键。 否则,编译后的 CSS 将包含 color1, color2...。 以上两个代码示例都转换为以下 CSS:

代码语言:javascript
复制
.blue-text {
  color: blue;
}

.red-text {
  color: red;
}

.green-text {
  color: green;
}

实践 编写一个 @each 遍历列表的指令: blue, black, red 并将每个变量分配给一个 .color-bg 类, 其中 color 每个项目的部分都会发生变化。 每个类都应该设置 background-color 各自的颜色。

代码语言:javascript
复制
<style type='text/scss'>

  $colors: blue, black, red;

  @each $color in $colors {
    .#{$color}-bg {
      background-color: $color;
    }
  }

  div {
    height: 200px;
    width: 200px;
  }
</style>

<div class="blue-bg"></div>
<div class="black-bg"></div>
<div class="red-bg"></div>

image-20230618223804420

应用样式直到条件满足 @while

@while 指令是一个选项,具有与 JavaScript while 循环类似的功能。 它会创建 CSS 规则,直到满足条件为止。 简单网格系统

代码语言:javascript
复制
$x: 1;
@while $x < 13 {
  .col-#{$x} { width: 100%/12 * $x;}
  $x: $x + 1;
}

首先,定义一个变量$x并将其设置为 1。 接下来,使用 @while 指令创建网格系统 while $x小于 13。 在为 设置 CSS 规则后 width$x 递增 1 以避免无限循环。 实践 用于 @while 创建一系列具有不同 font-sizes. text-1text-5。 然后设置 font-size15px 乘以当前索引号。 确保避免无限循环!

代码语言:javascript
复制
<style type='text/scss'>

$x: 1;
@while $x <= 5 {
  .text-#{$x} {
    font-size: 15px * $x;
  }
  $x: $x + 1;
}

</style>

<p class="text-1">Hello</p>
<p class="text-2">Hello</p>
<p class="text-3">Hello</p>
<p class="text-4">Hello</p>
<p class="text-5">Hello</p>

使用 Partials 将样式拆分为更小的块

Sass 中的 Partials 是包含 CSS 代码段的单独文件。 这些被导入并在其他 Sass 文件中使用。 这是将类似代码分组到一个模块中以保持其组织性的好方法。 partials 的名称以下划线 ( _) 字符开头,告诉 Sass 这是 CSS 的一小段,不要将其转换为 CSS 文件。 此外,Sass 文件以文件扩展名结尾 .scss。 要将部分代码放入另一个 Sass 文件,请使用该 @import 指令。 例如, 如果你所有的 mixins 都保存在名为 “_mixins.scss” 的部分中, 并且在 “main.scss” 文件中需要它们, 那么在主文件中使用它们的方法如下:

代码语言:javascript
复制
@import 'mixins'

请注意,语句中不需要下划线和文件扩展名 import Sass 理解它是 partials 的。 一旦将 partials 导入到文件中,所有变量、mixin 和其他代码都可以使用。 实践 编写一条 @import语句,将 partial named 导入 _variables.scssmain.scss 文件中。

代码语言:javascript
复制
<!-- The main.scss file -->
@import 'variables'

将一组 CSS 样式扩展 (**@extend**) 到另一个元素

个人理解: @extend 更像是继承 Sass 有一个特性 extend,可以很容易地从一个元素借用 CSS 规则并在另一个元素的基础上构建。 例如, 下面的 CSS 规则块为一个 .panel 类设置样式。 它有一个 background-colorheightborder

代码语言:javascript
复制
.panel {
  background-color: red;
  height: 70px;
  border: 2px solid green;
}

现在您想要另一个的面板名为 .big-panel。 它具有与 相同的基本属性 .panel ,但还需要一个 widthfont-size。 可以从 复制和粘贴初始 CSS 规则 .panel,但是随着您添加更多类型的面板,代码会变得重复。 该 extend 指令是重用为一个元素编写的规则,然后为另一个元素添加更多规则的简单方法:

代码语言:javascript
复制
.big-panel {
  @extend .panel;
  width: 150px;
  font-size: 2em;
}

除了新样式之外,.big-panel.panel具有相同的属性 实践 创建一个 CSS 类名为 .info-important, 扩展自 .info, 并有 background-color: magenta

代码语言:javascript
复制
<style type='text/scss'>
  h3{
    text-align: center;
  }
  .info{
    width: 200px;
    border: 1px solid black;
    margin: 0 auto;
  }

  /* 使用 @extend */
  .info-important {
    @extend .info;
    background-color: magenta;
  }

</style>
<h3>Posts</h3>
<div class="info-important">
  <p>This is an important post. It should extend the class ".info" and have its own CSS styles.</p>
</div>

<div class="info">
  <p>This is a simple post. It has basic styling and can be extended for other uses.</p>
</div>

image-20230618231007145

React.js

注释: { /* 我是注释 */ }

要将注释放在 JSX 中,请使用语法 {/* */} 环绕注释文本。

代码语言:javascript
复制
const JSX = (
  <div>
    <h1>This is a block of JSX</h1>
    <p>Here's a subtitle</p>
    { /* 我是 JSX 中的注释 */ }
  </div>
);

标签必须闭合

在 JSX 中, 任何 JSX 元素都可以使用自闭合标记编写,并且每个元素都必须闭合。 例如,换行标记必须始终写为 <br /> ,才能成为可以转译的有效 JSX。 而在 HTML 中, 例如,换行标记可以写为 <br><br /> ,但绝不应写为 <br></br> ,因为它不包含任何内容。

组件

有两种方法可以创建 React 组件。第一种方法是使用 JavaScript 函数。 以这种方式定义组件会创建一个无状态的功能组件。 现在,将无状态组件视为可以接收数据并呈现数据,但不管理或跟踪对该数据的更改的组件。

要创建带有函数的组件,您只需编写一个返回 JSX 或 null 的 JavaScript 函数。需要注意的重要一点是,React 要求你的函数名称以大写字母开头。

代码语言:javascript
复制
const DemoComponent = function() {
  return (
    <div className='customClass' />
  );
};

定义 React 组件的另一种方法是使用 ES6 class 语法。在以下示例中, Kitten 扩展 React.Component

代码语言:javascript
复制
class Kitten extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <h1>Hi</h1>
    );
  }
}

默认 props 值: MyComponent.defaultProps

默认 props

代码语言:javascript
复制
MyComponent.defaultProps = { location: 'San Francisco' }

定义 location 默认值为 'San Francisco',

React 分配默认值为: undefined, 但若指定分配 null, 则会保持 null

代码语言:javascript
复制
const ShoppingCart = (props) => {
  return (
    <div>
      <h1>Shopping Cart Component</h1>
      <p>{props.items}</p>
    </div>
  )
};
// Change code below this line
ShoppingCart.defaultProps = {
  items: 0
}

重写 默认 props

代码语言:javascript
复制
const Items = (props) => {
  return <h1>Current Quantity of Items in Cart: {props.quantity}</h1>
}

Items.defaultProps = {
  quantity: 0
}

class ShoppingCart extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    { /* Change code below this line */ }
    return <Items quantity={10} />
    { /* Change code above this line */ }
  }
};

通过 propTypes 限制 props

通过 propTypes 限制 props 参考:Typechecking With PropTypes – React

代码语言:javascript
复制
import PropTypes from 'prop-types';

const Items = (props) => {
  return <h1>Current Quantity of Items in Cart: {props.quantity}</h1>
};

Items.propTypes = {
  quantity: PropTypes.number.isRequired
};

Items.defaultProps = {
  quantity: 0
};

使用 this.props 访问 props

代码语言:javascript
复制
class App extends React.Component {
  constructor(props) {
    super(props);

  }
  render() {
    return (
        <div>
            { /* Change code below this line */ }
            <Welcome name="world"/>
            { /* Change code above this line */ }
        </div>
    );
  }
};

class Welcome extends React.Component {
  constructor(props) {
    super(props);

  }
  render() {
    return (
        <div>
          { /* Change code below this line */ }
          <p>Hello, <strong>{this.props.name}</strong>!</p>
          { /* Change code above this line */ }
        </div>
    );
  }
};

综合示例: propTypes, defaultProps

代码语言:javascript
复制
const Camper = props => <p>{props.name}</p>;

Camper.propTypes = {
  name: PropTypes.string.isRequired
};

Camper.defaultProps = {
  name: "CamperBot"
};

class CampSite extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <div>
        <Camper name="test"/>
      </div>
    );
  }
};

ReactDOM.render(<CampSite />, document.getElementById("challenge-node"));

Create a Stateful Component

代码语言:javascript
复制
class StatefulComponent extends React.Component {
  constructor(props) {
    super(props);
    // Only change code below this line
    this.state = {
      firstName: "Hello"
    };
    // Only change code above this line
  }
  render() {
    return (
      <div>
        <h1>{this.state.firstName}</h1>
      </div>
    );
  }
};

使用 setState 更新 state

代码语言:javascript
复制
this.setState({
  username: 'Lewis'
});
代码语言:javascript
复制
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      text: "Hello"
    };
    // Change code below this line
    this.handleClick = this.handleClick.bind(this);
    // Change code above this line
  }
  handleClick() {
    this.setState({
      text: "You clicked!"
    });
  }
  render() {
    return (
      <div>
        { /* Change code below this line */ }
        <button onClick={this.handleClick}>Click Me</button>
        { /* Change code above this line */ }
        <h1>{this.state.text}</h1>
      </div>
    );
  }
};

有时候,需要访问之前的 state 用于更新现在的 state , 然而,state 更新可能是异步的,这意味着 React 可能批量多个 setState() 呼叫在单个更新,这意味着你不能依赖于之前的值: this.statethis.props 当用于计算下一个值时, 因此你不能下面这样写:

代码语言:javascript
复制
// 错误 示范
this.setState({
  counter: this.state.counter + this.props.increment
});
代码语言:javascript
复制
// 正确 示范
this.setState((state, props) => ({
  counter: state.counter + props.increment
}));

也可以如下

代码语言:javascript
复制
this.setState(state => ({
  counter: state.counter + 1
}));

注意:必须将函数返回值(对象)包裹在 ()

代码语言:javascript
复制
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      visibility: false
    };
    // Change code below this line
    this.toggleVisibility = this.toggleVisibility.bind(this);
    // Change code above this line
  }
  // Change code below this line
  toggleVisibility() {
    this.setState(state => ({
      visibility: !state.visibility
    }));
  }
  // Change code above this line
  render() {
    if (this.state.visibility) {
      return (
        <div>
          <button onClick={this.toggleVisibility}>Click Me</button>
          <h1>Now you see me!</h1>
        </div>
      );
    } else {
      return (
        <div>
          <button onClick={this.toggleVisibility}>Click Me</button>
        </div>
      );
    }
  }
}

综合示例

代码语言:javascript
复制
class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
    // Change code below this line
    this.increment = this.increment.bind(this);
    this.decrement = this.decrement.bind(this);
    this.reset = this.reset.bind(this);
    // Change code above this line
  }
  // Change code below this line
  increment() {
    this.setState(state => ({
      count: state.count + 1
    }));
  }
  decrement() {
    this.setState(state => ({
      count: state.count - 1
    }));
  }
  reset() {
    this.setState(state => ({
      count: 0
    }))
  }
  // Change code above this line
  render() {
    return (
      <div>
        <button className='inc' onClick={this.increment}>Increment!</button>
        <button className='dec' onClick={this.decrement}>Decrement!</button>
        <button className='reset' onClick={this.reset}>Reset</button>
        <h1>Current Count: {this.state.count}</h1>
      </div>
    );
  }
};

可控的 input

代码语言:javascript
复制
class ControlledInput extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      input: ''
    };
    // Change code below this line
    this.handleChange = this.handleChange.bind(this);
    // Change code above this line
  }
  // Change code below this line
  handleChange(event) {
    this.setState(state => ({
      input: event.target.value
    }));
  }
  // Change code above this line
  render() {
    return (
      <div>
        { /* Change code below this line */}
        <input value={this.state.input} onChange={this.handleChange} />
        { /* Change code above this line */}
        <h4>Controlled Input:</h4>
        <p>{this.state.input}</p>
      </div>
    );
  }
};

可控的 Form

代码语言:javascript
复制
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) {
    // Change code below this line
    event.preventDefault();
    this.setState(state => ({
      submit: state.input
    }));
    // Change code above this line
  }
  render() {
    return (
      <div>
        <form onSubmit={this.handleSubmit}>
          {/* Change code below this line */}
          <input value={this.state.input} onChange={this.handleChange} />
          {/* Change code above this line */}
          <button type='submit'>Submit!</button>
        </form>
        {/* Change code below this line */}
        <h1>{this.state.submit}</h1>
        {/* Change code above this line */}
      </div>
    );
  }
}

Pass State as Props to Child Components

代码语言:javascript
复制
class MyApp extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: 'CamperBot'
    }
  }
  render() {
    return (
       <div>
         {/* Change code below this line */}
         <Navbar name={this.state.name} />
         {/* Change code above this line */}
       </div>
    );
  }
};

class Navbar extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
    <div>
      {/* Change code below this line */}
      <h1>Hello, my name is: {this.props.name}</h1>
      {/* Change code above this line */}
    </div>
    );
  }
};

Pass a Callback as Props

代码语言:javascript
复制
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 (
       <div>
        { /* Change code below this line */ }
        <GetInput input={this.state.inputValue} handleChange={this.handleChange} />
        <RenderInput input={this.state.inputValue} />
        { /* Change code above this line */ }
       </div>
    );
  }
};

class GetInput extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <div>
        <h3>Get Input:</h3>
        <input
          value={this.props.input}
          onChange={this.props.handleChange}/>
      </div>
    );
  }
};

class RenderInput extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <div>
        <h3>Input Render:</h3>
        <p>{this.props.input}</p>
      </div>
    );
  }
};

生命周期: componentWillMount

Note: The componentWillMount Lifecycle method will be deprecated in a future version of 16.X and removed in version 17. Learn more in this article

componentWillMount()render() 之前被调用

生命周期: componentDidMount

组件被挂载到 DOM 后 componentDidMount() 被调用, 任何对 setState() 都会触发组件的重新渲染, 可以在这里获取异步 API 的数据

代码语言:javascript
复制
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      activeUsers: null
    };
  }
  componentDidMount() {
    setTimeout(() => {
      this.setState({
        activeUsers: 1273
      });
    }, 2500);
  }
  render() {
    return (
      <div>
        {/* Change code below this line */}
        <h1>Active Users: {this.state.activeUsers}</h1>
        {/* Change code above this line */}
      </div>
    );
  }
}

添加事件侦听器

代码语言:javascript
复制
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      message: ''
    };
    this.handleEnter = this.handleEnter.bind(this);
    this.handleKeyPress = this.handleKeyPress.bind(this);
  }
  // Change code below this line
  componentDidMount() {
    document.addEventListener("keydown", this.handleKeyPress);
  }
  componentWillUnmount() {
    document.removeEventListener("keydown", this.handleKeyPress);
  }
  // Change code above this line
  handleEnter() {
    this.setState((state) => ({
      message: state.message + 'You pressed the enter key! '
    }));
  }
  handleKeyPress(event) {
    if (event.keyCode === 13) {
      this.handleEnter();
    }
  }
  render() {
    return (
      <div>
        <h1>{this.state.message}</h1>
      </div>
    );
  }
};

使用 shouldComponentUpdate 优化重新渲染

此方法是优化性能的有用方法。 例如,默认行为是组件在收到新的 props 时重新渲染,即使 props 没有更改也是如此。 你可以使用 shouldComponentUpdate() 通过比较 props 来防止这种情况。 该方法必须返回一个 boolean 值,告诉 React 是否更新组件。 你可以将当前 props( this.props )与下一个props ( nextProps ) 进行比较, 以确定是否需要更新,并相应地返回 truefalse 。 使 OnlyEvens 仅在其新 propsvalue 为偶数时才更新

代码语言:javascript
复制
class OnlyEvens extends React.Component {
  constructor(props) {
    super(props);
  }
  shouldComponentUpdate(nextProps, nextState) {
    console.log('Should I update?');
    // Change code below this line
    if (nextProps.value %2 == 0) {
      return true;
    }
    return false;
    // Change code above this line
  }
  componentDidUpdate() {
    console.log('Component re-rendered.');
  }
  render() {
    return <h1>{this.props.value}</h1>;
  }
}

class Controller extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: 0
    };
    this.addValue = this.addValue.bind(this);
  }
  addValue() {
    this.setState(state => ({
      value: state.value + 1
    }));
  }
  render() {
    return (
      <div>
        <button onClick={this.addValue}>Add</button>
        <OnlyEvens value={this.state.value} />
      </div>
    );
  }
}

不会再显示奇数, 点击 2 次, 从 20 到 22

内联样式(**Inline Styles**)

HTML 中内联样式的示例

代码语言:javascript
复制
<div style="color: yellow; font-size: 16px">Mellow Yellow</div>

JSX 元素使用 style 属性,但由于 JSX 的转译方式,你无法将该值设置为 string 。 相反,你把它设置为等于JavaScript object

代码语言:javascript
复制
<div style={{color: "yellow", fontSize: 16}}>Mellow Yellow</div>

请注意,你可以选择将字体大小设置为数字,省略单位 px ,或将其写为 72px

代码语言:javascript
复制
// Change code above this line
// 需要写在外部
const styles = {
  color: 'purple',
  fontSize: 40,
  border: "2px solid purple",
};

class Colorful extends React.Component {
  render() {
    // Change code below this line

    // 写在这里: undefined, 无法在下面找到, 说明下面 JSX 作用域并不在此方法内, 但很奇怪, 在后面的例子中, answer 又可以直接使用
    // const styles = {
    //   color: 'purple',
    //   fontSize: 40,
    //   border: "2px solid purple",
    // };
    
    return (
      <div style={styles}>Style Me!</div>
    );
    // Change code above this line
  }
};

在 React 渲染方法中使用高级 JavaScript

代码语言:javascript
复制
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',
      'Most likely',
      'Outlook not so good',
      'Very doubtful'
    ];
    const answer = possibleAnswers[this.state.randomIndex]; // Change this line
    // 很奇怪, 为什么这里又可以直接使用 answer
    return (
      <div>
        <input
          type='text'
          value={this.state.userInput}
          onChange={this.handleChange}
          style={inputStyle}
        />
        <br />
        <button onClick={this.ask}>Ask the Magic Eight Ball!</button>
        <br />
        <h3>Answer:</h3>
        <p>
          {/* Change code below this line */}
          {answer}
          {/* Change code above this line */}
        </p>
      </div>
    );
  }
}

使用 if-else 条件渲染

代码语言:javascript
复制
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
    if (this.state.display) {
      return (
       <div>
         <button onClick={this.toggleDisplay}>Toggle Display</button>
         <h1>Displayed!</h1>
       </div>
    );
    } else {
      return (
        <div>
          <button onClick={this.toggleDisplay}>Toggle Display</button>
        </div>
      )
    }
  }
};

使用 && 简化条件

代码语言:javascript
复制
{condition && <p>markup</p>}

如果 conditiontrue ,则将返回标记。 如果条件为 false ,则操作将在计算 condition 后立即返回 false ,并且不返回任何内容。

代码语言:javascript
复制
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 (
       <div>
         <button onClick={this.toggleDisplay}>Toggle Display</button>
         {this.state.display && <h1>Displayed!</h1>}
       </div>
    );
  }
};

使用 三元表达式 进行条件渲染

代码语言:javascript
复制
condition ? expressionIfTrue : expressionIfFalse;
代码语言:javascript
复制
const inputStyle = {
  width: 235,
  margin: 5
};

class CheckUserAge extends React.Component {
  constructor(props) {
    super(props);
    // Change code below this line
    this.state = {
      input: '',
      userAge: ''
    };
    // Change code above this line
    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 = <button onClick={this.submit}>Submit</button>;
    const buttonTwo = <button>You May Enter</button>;
    const buttonThree = <button>You Shall Not Pass</button>;
    return (
      <div>
        <h3>Enter Your Age to Continue</h3>
        <input
          style={inputStyle}
          type='number'
          value={this.state.input}
          onChange={this.handleChange}
        />
        <br />
        {/* Change code below this line */}
        { /* 注意: 可以直接使用 buttonOne, 而不是 <buttonOne /> */ }
        {
          this.state.userAge === ''
            ? buttonOne
            : this.state.userAge >= 18
              ? buttonTwo
              : buttonThree
        }
        {/* Change code above this line */}
      </div>
    );
  }
}

props 有条件地渲染

代码语言:javascript
复制
class Results extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    {/* Change code below this line */}
    return (
      <h1>
        {this.props.fiftyFifty ? "You Win!" : "You Lose!"}
      </h1>
    );
    {/* Change code above this line */}
  }
}

class GameOfChance extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      counter: 1
    };
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    this.setState(prevState => {
      // Complete the return statement:
      return {
        counter: prevState.counter + 1
      }
    });
  }
  render() {
    const expression = Math.random() >= 0.5; // Change this line
    return (
      <div>
        <button onClick={this.handleClick}>Play Again</button>
        {/* Change code below this line */}
        <Results fiftyFifty={expression} />
        {/* Change code above this line */}
        <p>{'Turn: ' + this.state.counter}</p>
      </div>
    );
  }
}

根据组件状态(state)有条件地更改内联 CSS

输入框中键入的文本超过 15 个字符,则将此边框设置为红色样式。

代码语言:javascript
复制
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'
    };
    // Change code below this line
    inputStyle = this.state.input.length > 15 ? { border: '3px solid red' } : inputStyle;
    // Change code above this line
    return (
      <div>
        <h3>Don't Type Too Much:</h3>
        <input
          type="text"
          style={inputStyle}
          value={this.state.input}
          onChange={this.handleChange} />
      </div>
    );
  }
};

使用 Array.map() 动态渲染元素

代码语言:javascript
复制
const textAreaStyles = {
  width: 235,
  margin: 5
};

class MyToDoList extends React.Component {
  constructor(props) {
    super(props);
    // Change code below this line
    this.state = {
      userInput: "",
      toDoList: []
    };
    // Change code above this line
    this.handleSubmit = this.handleSubmit.bind(this);
    this.handleChange = this.handleChange.bind(this);
  }
  handleSubmit() {
    const itemsArray = this.state.userInput.split(',');
    this.setState({
      toDoList: itemsArray
    });
  }
  handleChange(e) {
    this.setState({
      userInput: e.target.value
    });
  }
  render() {
    const items = this.state.toDoList.map(item => <li>{item}</li>); // Change this line
    return (
      <div>
        <textarea
          onChange={this.handleChange}
          value={this.state.userInput}
          style={textAreaStyles}
          placeholder='Separate Items With Commas'
        />
        <br />
        <button onClick={this.handleSubmit}>Create List</button>
        <h1>My "To Do" List:</h1>
        <ul>{items}</ul>
      </div>
    );
  }
}

为同级元素提供唯一的 key 属性

创建元素数组时,每个元素都需要将 key 属性设置为唯一值。 React 使用这些键来跟踪添加、更改或删除了哪些项。 这有助于在以任何方式修改列表时使重新渲染过程更高效。 注意:key 只需要在同级元素之间是唯一的,它们在应用程序中不需要全局唯一。 通常,你希望使 key 能唯一标识正在呈现的元素。 作为最后的手段,可以使用数组索引,但通常应尝试使用唯一标识。

代码语言:javascript
复制
const frontEndFrameworks = [
  'React',
  'Angular',
  'Ember',
  'Knockout',
  'Backbone',
  'Vue'
];

function Frameworks() {
  const renderFrameworks = frontEndFrameworks.map((item, index) => 
    <li key={index}>{item}</li>
  ); // Change this line
  return (
    <div>
      <h1>Popular Front End JavaScript Frameworks</h1>
      <ul>
        {renderFrameworks}
      </ul>
    </div>
  );
};

使用 Array.filter() 动态过滤数组

代码语言:javascript
复制
// 取出 users 中所有在线用户: 
// online: Boolean
let onlineUsers = users.filter(user => user.online);
代码语言:javascript
复制
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 => user.online); // Change this line
    const renderOnline = usersOnline.map((item, index) => <li key={index}>{item.username}</li>); // Change this line
    return (
      <div>
        <h1>Current Online Users:</h1>
        <ul>{renderOnline}</ul>
      </div>
    );
  }
}

使用 renderToString 在服务端渲染 React

至此, 渲染 React 组件都是在客户端(客户浏览器), 通常来说你完全可以这么做, 然而,有些情况需要渲染 React 组件在服务端, 因为 React 是一个 JavaScript 视图库,并且你可以使用 Node.js 在服务端运行 JavaScript, 事实上, React 提供了 renderToString() 方法用于此种情况。

代码语言:javascript
复制
class App extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return <div/>
  }
};

// Change code below this line
ReactDOMServer.renderToString(<App />);

Redux

创建 Redux Store

Redux 是一个状态管理框架,可以与许多不同的 Web 技术一起使用,包括 React 在 Redux 中,有一个状态对象负责应用程序的整个状态。 这意味着, 如果你有一个包含十个组件的 React 应用程序, 并且每个组件都有自己的本地状态, 则应用程序的整个状态将由 Redux store 中的单个状态对象定义。 这也意味着, 每当应用的任何部分想要更新状态时, 它都必须通过 Redux store 来实现。 通过单向数据流,可以更轻松地跟踪应用中的状态管理。 Redux store 是保存和管理应用程序 state 的对象。 Redux 对象上有一个名为 createStore() 的方法,用于创建 Redux store 。 此方法将 reducer 函数作为必需参数。 reducer 函数将在后面的挑战中介绍,并且已经在代码编辑器中为您定义。 它只是将 state 作为参数并返回 state 。 声明一个 store 变量并将其分配给 createStore() 方法, 将 reducer 作为参数传入。

代码语言:javascript
复制
// ES6 默认参数语法: 将 state 初始化为 5
const reducer = (state = 5) => {
  return state;
}

// Redux methods are available from a Redux object
// For example: Redux.createStore()
// Define the store here:
const store = Redux.createStore(reducer)

Redux Store 中获取状态

使用 getState() 方法获取 Redux store 对象中保存的当前 state 使用 store.getState()store 中获取 state ,并将其分配给新的变量 currentState

代码语言:javascript
复制
const store = Redux.createStore(
  (state = 5) => state
);

// Change code below this line
let currentState = store.getState();

定义 Redux Action

由于 Redux 是一个状态管理框架,因此更新状态是其核心任务之一。 在 Redux 中,所有状态更新都由调度操作(dispatching actions)触发。 Action 只是一个 JavaScript 对象,其中包含有关已发生的 action 事件的信息。 Redux store 接收这些 action 对象,然后相应地更新其状态。 有时 Redux action 也会携带一些数据。 例如,该 action 在用户登录后携带用户名。 虽然数据是可选的,但 action 必须带有指定所发生 action 的 'type' 的 type 属性。 将 Redux action 视为将有关应用中发生的事件的信息传递到 Redux store 的信使。 然后,store 根据发生的 action 执行更新状态的业务。 编写 Redux action 就像使用类型属性声明对象一样简单。 声明一个对象 action 并为其指定一个设置为字符串 'LOGIN' 的属性 type

代码语言:javascript
复制
// Define an action here:
let action = {
  type: 'LOGIN'
};

定义 Action 创建者

创建 Action 后,下一步是将操作发送到 Redux store,以便它可以更新其状态。 在 Redux 中,您可以定义 Action 创建者来完成此操作。 Action 创建者只是一个返回 Action 的 JavaScript 函数。 换句话说,Action 创建者创建表示 Action 事件的对象。 定义一个名为 actionCreator() 的函数,该函数在调用时返回 action 对象。

代码语言:javascript
复制
const action = {
  type: 'LOGIN'
}
// Define an action creator here:
function actionCreator() {
  return action;
}

Dispatch an Action Event

dispatch 方法是用于将 Action 调度到 Redux store 的方法。 调用 store.dispatch() 并传递从 Action 创建者返回的值会将操作发送回 store。 回想一下,Action 创建者返回一个对象,该对象具有指定已发生的 Action 的类型属性。 然后,该方法将操作对象调度到 Redux 存储区。 根据前面挑战的示例,以下行是等效的,并且都调度类型 LOGIN 的 Action:

代码语言:javascript
复制
store.dispatch(actionCreator());
store.dispatch({ type: 'LOGIN' });

代码编辑器中的 Redux store 具有初始化状态, 该状态是包含当前设置为 falselogin 属性的对象。 还有一个名为 loginAction() 的 action 创建器,它返回类型 LOGIN 的 action。 通过调用 dispatch 方法将 LOGIN action 调度到 Redux store, 并传入 loginAction() 创建的 action。

代码语言:javascript
复制
const store = Redux.createStore(
  (state = {login: false}) => state
);

const loginAction = () => {
  return {
    type: 'LOGIN'
  }
};

// Dispatch the action here:
store.dispatch(loginAction());

处理 Store 中的 Action

创建 action 并 dispatch 后, Redux store 需要知道如何响应 action。 这是 reducer 函数的工作, Redux 中的 reducer 负责为响应 action 而发生的状态修改。 reducer state action 作为参数,并且始终返回新的 state Redux 中的另一个关键原则是 state 是只读的。 换句话说, reducer 函数必须始终返回 state 的新副本,并且从不直接修改状态。 Redux 不强制要求状态不可变性,但是,你负责在 reducer 函数的代码中强制执行它。

代码语言:javascript
复制
const defaultState = {
  login: false
};

const reducer = (state = defaultState, action) => {
  // Change code below this line
  if (action.type === "LOGIN") {
    return {
      login: true
    };
  } else {
    return state;
  }
  // Change code above this line
};

const store = Redux.createStore(reducer);

const loginAction = () => {
  return {
    type: 'LOGIN'
  }
};

使用 switch 语句处理多个 Action

代码语言:javascript
复制
const defaultState = {
  authenticated: false
};

const authReducer = (state = defaultState, action) => {
  // Change code below this line
  switch (action.type) {
    case "LOGIN":
      return {
        authenticated: true
      };
    case "LOGOUT":
      return {
        authenticated: false
      };
    default:
      return defaultState;
  }
  // Change code above this line
};

const store = Redux.createStore(authReducer);

const loginUser = () => {
  return {
    type: 'LOGIN'
  }
};

const logoutUser = () => {
  return {
    type: 'LOGOUT'
  }
};

将 const 用于 Action 类型

使用 Redux 时的常见做法是将 Action 类型分配为只读常量, 然后在使用这些常量的位置引用这些常量。 可以重构正在使用的代码,以将 Action 类型编写为 const 声明。 将 LOGINLOGOUT 声明为 const 值, 并分别将它们分配给字符串 'LOGIN''LOGOUT' 。 然后,编辑 authReducer() 和 Action 创建者以引用这些常量而不是字符串值。 注意:通常约定以 全大写 形式写入常量,这也是 Redux 中的标准做法。

代码语言:javascript
复制
const LOGIN = "LOGIN";
const LOGOUT = "LOGOUT";

const defaultState = {
  authenticated: false
};

const authReducer = (state = defaultState, action) => {

  switch (action.type) {
    case LOGIN: 
      return {
        authenticated: true
      }
    case LOGOUT: 
      return {
        authenticated: false
      }

    default:
      return state;

  }

};

const store = Redux.createStore(authReducer);

const loginUser = () => {
  return {
    type: LOGIN
  }
};

const logoutUser = () => {
  return {
    type: LOGOUT
  }
};

注册 store 侦听器

你有权访问 Redux store 对象的另一个方法是 store.subscribe() 。 这允许你将侦听器函数订阅到 store, 每当针对 store dispatch action 时都会调用这些函数。 此方法的一个简单用途是向您的 store 订阅一个函数, 该函数仅在每次收到 action 并更新 store 时记录一条消息。 编写一个回调函数,每次 store 收到 action 时递增全局变量 count , 并将此函数传递给 store.subscribe() 方法。 你将看到连续调用 store.dispatch() 三次, 每次都直接传入一个 action 对象。 观察 action dispatch 之间的控制台输出,以查看更新的发生情况。

代码语言:javascript
复制
const ADD = 'ADD';

const reducer = (state = 0, action) => {
  switch(action.type) {
    case ADD:
      return state + 1;
    default:
      return state;
  }
};

const store = Redux.createStore(reducer);

// Global count variable:
let count = 0;

// Change code below this line
store.subscribe(() => count++);
// Change code above this line

store.dispatch({type: ADD});
console.log(count);
store.dispatch({type: ADD});
console.log(count);
store.dispatch({type: ADD});
console.log(count);

组合多个 reducer

当应用的状态开始变得更加复杂时,可能很容易将状态划分为多个部分。 相反,请记住 Redux 的第一个原则: 所有应用状态都保存在 store 中的单个状态对象中。 因此,Redux 提供了 reducer 组合作为复杂状态模型的解决方案。 定义多个 reducer 来处理应用程序状态的不同部分, 然后将这些 reducer 组合成一个根 reducer (root reducer)。 然后将根 reducer 传递到 Redux createStore() 方法中。 为了让我们将多个 reducer 组合在一起,Redux 提供了 combineReducers() 方法。 此函数接受对象作为参数,您可以在其中定义将键关联到特定 reducer 函数的属性。 例如,在具有用户身份验证的笔记应用中, 一个 reducer 可以处理身份验证, 而另一个 reducer 可以处理用户正在提交的文本和笔记。

代码语言:javascript
复制
const rootReducer = Redux.combineReducers({
  auth: authenticationReducer,
  notes: notesReducer
});
代码语言:javascript
复制
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';

const counterReducer = (state = 0, action) => {
  switch(action.type) {
    case INCREMENT:
      return state + 1;
    case DECREMENT:
      return state - 1;
    default:
      return state;
  }
};

const LOGIN = 'LOGIN';
const LOGOUT = 'LOGOUT';

const authReducer = (state = {authenticated: false}, action) => {
  switch(action.type) {
    case LOGIN:
      return {
        authenticated: true
      }
    case LOGOUT:
      return {
        authenticated: false
      }
    default:
      return state;
  }
};

const rootReducer = Redux.combineReducers({
  count: counterReducer,
  auth: authReducer
}); // Define the root reducer here

const store = Redux.createStore(rootReducer);

发送 Action 数据到 Store

代码语言:javascript
复制
const ADD_NOTE = 'ADD_NOTE';

const notesReducer = (state = 'Initial State', action) => {
  switch(action.type) {
    // Change code below this line
    case ADD_NOTE:
      return action.text;
    // Change code above this line
    default:
      return state;
  }
};

const addNoteText = (note) => {
  // Change code below this line
  return {
    type: ADD_NOTE,
    text: note
  };
  // Change code above this line
};

const store = Redux.createStore(notesReducer);

console.log(store.getState());
store.dispatch(addNoteText('Hello!'));
console.log(store.getState());

使用中间件处理异步 Action

代码语言:javascript
复制
const REQUESTING_DATA = 'REQUESTING_DATA'
const RECEIVED_DATA = 'RECEIVED_DATA'

const requestingData = () => { return {type: REQUESTING_DATA} }
const receivedData = (data) => { return {type: RECEIVED_DATA, users: data.users} }

const handleAsync = () => {
  return function(dispatch) {
    // Dispatch request action here
    dispatch(requestingData());
    setTimeout(function() {
      let data = {
        users: ['Jeff', 'William', 'Alice']
      }
      // Dispatch received data action here
      dispatch(receivedData(data));
    }, 2500);
  }
};

const defaultState = {
  fetching: false,
  users: []
};

const asyncDataReducer = (state = defaultState, action) => {
  switch(action.type) {
    case REQUESTING_DATA:
      return {
        fetching: true,
        users: []
      }
    case RECEIVED_DATA:
      return {
        fetching: false,
        users: action.users
      }
    default:
      return state;
  }
};

const store = Redux.createStore(
  asyncDataReducer,
  Redux.applyMiddleware(ReduxThunk.default)
);

用 Redux 写一个计数器

代码语言:javascript
复制
// Define a constant for increment action types
const INCREMENT = "INCREMENT"; 
// Define a constant for decrement action types
const DECREMENT = "DECREMENT"; 

// Define the counter reducer which will increment or decrement the state based on the action it receives
const counterReducer = (state = 0, action) => {
  switch(action.type) {
    case INCREMENT:
      return state + 1;
    case DECREMENT:
      return state - 1;
    default:
      return state;
  }
}; 

// Define an action creator for incrementing
const incAction = () => {
  return {
    type: INCREMENT
  };
}; 

// Define an action creator for decrementing
const decAction = () => {
  return {
    type: DECREMENT
  };
}; 

// Define the Redux store here, passing in your reducers
const store = Redux.createStore(counterReducer); 

不可变 state

不可变状态意味着你从不直接修改状态,而是返回一个新的状态副本。 Redux 不会主动在其 store 或 reducer 中强制执行状态不变性, 该责任落在程序员身上。 代码编辑器中有一个 storereducer 用于管理待办事项。 完成在 reducer 中写入案例 ADD_TO_DO 以将新的待办事项附加到状态。 有几种方法可以使用标准 JavaScript 或 ES6 来完成此操作。 看看你是否可以找到一种方法来返回一个新数组, 其中的项目 action.todo 附加到末尾。 由于 Redux 中的状态不变性, 此挑战的目标是在 reducer 函数中返回一个新的状态副本。

代码语言:javascript
复制
const ADD_TO_DO = 'ADD_TO_DO';

// A list of strings representing tasks to do:
const todos = [
  'Go to the store',
  'Clean the house',
  'Cook dinner',
  'Learn to code',
];

const immutableReducer = (state = todos, action) => {
  switch(action.type) {
    case ADD_TO_DO:
      // Don't mutate state here or the tests will fail
      // 拼接, 加在数组最后
      // return state.concat(action.todo);
      // 或 ES6 展开运算符
      return [...state, action.todo]
    default:
      return state;
  }
};

const addToDo = (todo) => {
  return {
    type: ADD_TO_DO,
    todo
  }
}

const store = Redux.createStore(immutableReducer);

在数组上使用展开运算符

... 展开运算符有多种应用,其中之一非常适合 从现有数组生成新数组

代码语言:javascript
复制
let newArray = [...myArray];

newArray 现在是克隆 myArray。 两个数组仍然单独存在于内存中。 如果你执行类似 newArray.push(5) , myArray 则不会改变。

代码语言:javascript
复制
const immutableReducer = (state = ['Do not mutate state!'], action) => {
  switch(action.type) {
    case 'ADD_TO_DO':
      // Don't mutate state here or the tests will fail
      let newArray = [...state, action.todo];
      return newArray;
    default:
      return state;
  }
};

const addToDo = (todo) => {
  return {
    type: 'ADD_TO_DO',
    todo
  }
}

const store = Redux.createStore(immutableReducer);

从数组中删除一个项

代码语言:javascript
复制
const immutableReducer = (state = [0,1,2,3,4,5], action) => {
  switch(action.type) {
    case 'REMOVE_ITEM':
      // Don't mutate state here or the tests will fail
      return [
        ...state.slice(0, action.index),
        ...state.slice(action.index + 1, state.length)
      ];
      // 或
      // return state.slice(0, action.index).concat(state.slice(action.index + 1, state.length));
      // 或
      // return state.filter((_, index) => index !== action.index);
    default:
      return state;
  }
};

const removeItem = (index) => {
  return {
    type: 'REMOVE_ITEM',
    index
  }
}

const store = Redux.createStore(immutableReducer);

使用 Object.assign 复制一个对象

Object.assign()获取目标对象和源对象并将属性从源对象映射到目标对象。 任何匹配的属性都会被源对象中的属性覆盖。 此行为通常用于通过传递一个空对象作为第一个参数, 然后传递要复制的对象来制作对象的浅表副本。 这是一个例子:

代码语言:javascript
复制
const newObject = Object.assign({}, obj1, obj2);
代码语言:javascript
复制
const defaultState = {
  user: 'CamperBot',
  status: 'offline',
  friends: '732,982',
  community: 'freeCodeCamp'
};

const immutableReducer = (state = defaultState, action) => {
  switch(action.type) {
    case 'ONLINE':
      // Don't mutate state here or the tests will fail
      return Object.assign({}, state, { status: "online" });
    default:
      return state;
  }
};

const wakeUp = () => {
  return {
    type: 'ONLINE'
  }
};

const store = Redux.createStore(immutableReducer);

将 Redux 与 React 结合使用

开始使用 React Redux

代码语言:javascript
复制
class DisplayMessages extends React.Component {
  // Change code below this line
  constructor(props) {
    super(props);
    this.state = {
      input: "",
      messages: []
    };
  }
  // Change code above this line
  render() {
    return <div />
  }
};

首先在本地管理 state

代码语言:javascript
复制
class DisplayMessages extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      input: '',
      messages: []
    };
    this.handleChange = this.handleChange.bind(this);
    this.submitMessage = this.submitMessage.bind(this);
  }
  // Add handleChange() and submitMessage() methods here
  handleChange(event) {
    // 无需更新 messages, 会自动合并更新, 不会导致 messages 丢失
    this.setState({
      input: event.target.value
    });
  }

  submitMessage() {
    this.setState(state => ({
      input: "",
      messages: [...state.messages, state.input]
    }));
  }

  render() {
    return (
      <div>
        <h2>Type in a new Message:</h2>
        { /* Render an input, button, and ul below this line */ }
        <input onChange={this.handleChange} value={this.state.input} />
        <button onClick={this.submitMessage}>Add message</button>
        <ul>
          {
            this.state.messages.map((m, i) => 
              (
                <li key={i}>{m}</li>
              )
            )
          }
        </ul>
        { /* Change code above this line */ }
      </div>
    );
  }
};

将状态(state)逻辑提取到 Redux

代码语言:javascript
复制
// Define ADD, addMessage(), messageReducer(), and store here:
const ADD = "ADD";

const addMessage = message => {
  return {
    type: ADD,
    message
  };
};

// Use ES6 default paramter to give the 'previousState' parameter an initial value.
const messageReducer = (previousState = [], action) => {
  // Use switch statement to lay out the reducer logic in response to different action type
  switch (action.type) {
    case ADD:
      // Use ES6 spread operator to return a new array where the new message is added to previousState
      return [...previousState, action.message];
    default:
      // A default case to fall back on in case if the update to Redux store is not for this specific state.
      return previousState;
  }
};

const store = Redux.createStore(messageReducer);

使用 Provider 将 Redux 连接到 React

下一步是提供对 Redux store 的 React 访问以及调度(dispatch)更新所需的 action。 React Redux 提供了它的 react-redux 包来帮助完成这些任务。 React Redux 提供了一个具有两个关键特性的小型 API: ProviderconnectProvider 需要两个 props,即 Redux store 和应用的子组件。 为 App 组件定义 Provider 可能如下所示:

代码语言:javascript
复制
<Provider store={store}>
  <App/>
</Provider>
代码语言:javascript
复制
// Redux:
const ADD = 'ADD';

const addMessage = (message) => {
  return {
    type: ADD,
    message
  }
};

const messageReducer = (state = [], action) => {
  switch (action.type) {
    case ADD:
      return [
        ...state,
        action.message
      ];
    default:
      return state;
  }
};



const store = Redux.createStore(messageReducer);

// React:

class DisplayMessages extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      input: '',
      messages: []
    }
    this.handleChange = this.handleChange.bind(this);
    this.submitMessage = this.submitMessage.bind(this);
  }
  handleChange(event) {
    this.setState({
      input: event.target.value
    });
  }
  submitMessage() {  
    this.setState((state) => {
      const currentMessage = state.input;
      return {
        input: '',
        messages: state.messages.concat(currentMessage)
      };
    });
  }
  render() {
    return (
      <div>
        <h2>Type in a new Message:</h2>
        <input
          value={this.state.input}
          onChange={this.handleChange}/><br/>
        <button onClick={this.submitMessage}>Submit</button>
        <ul>
          {this.state.messages.map( (message, idx) => {
              return (
                 <li key={idx}>{message}</li>
              )
            })
          }
        </ul>
      </div>
    );
  }
};

const Provider = ReactRedux.Provider;

class AppWrapper extends React.Component {
  // Render the Provider below this line
  render() {
    return (
      <Provider store={store}>
        <DisplayMessages />
      </Provider>
    );
  }
  // Change code above this line
};

将 state 映射到 props

Provider 组件允许您为 React 组件提供 statedispatch , 但你必须准确指定所需的 state 和 action。 这样,您可以确保每个组件只能访问它所需的 state。 你可以通过创建两个函数来实现此目的: mapStateToProps()mapDispatchToProps()

代码语言:javascript
复制
const state = [];

// Change code below this line
const mapStateToProps = (state) => {
  return {
    messages: state
  };
};

将 dispatch 映射到 props

mapDispatchToProps() 函数用于为你的 React 组件提供特定的操作(action)创建者, 以便它们可以针对 Redux store 调度(dispatch) 操作(action)。 一个示例

代码语言:javascript
复制
{
  submitLoginUser: function(username) {
    dispatch(loginUser(username));
  }
}
代码语言:javascript
复制
const addMessage = (message) => {
  return {
    type: 'ADD',
    message: message
  }
};

// Change code below this line
const mapDispatchToProps = (dispatch) => {
  return {
    submitNewMessage: (message) => {
      dispatch(addMessage(message));
    }
  };
};

将 Redux 连接到 React

代码语言:javascript
复制
connect(mapStateToProps, mapDispatchToProps)(MyComponent)
代码语言:javascript
复制
const addMessage = (message) => {
  return {
    type: 'ADD',
    message: message
  }
};

const mapStateToProps = (state) => {
  return {
    messages: state
  }
};

const mapDispatchToProps = (dispatch) => {
  return {
    submitNewMessage: (message) => {
      dispatch(addMessage(message));
    }
  }
};

class Presentational extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return <h3>This is a Presentational Component</h3>
  }
};

const connect = ReactRedux.connect;
// Change code below this line
const ConnectedComponent = connect(mapStateToProps, mapDispatchToProps)(Presentational);

将 Redux 连接到 "消息" 应用

type-in-a-new-message.png

代码语言:javascript
复制
// Redux:
const ADD = 'ADD';

const addMessage = (message) => {
  return {
    type: ADD,
    message: message
  }
};

const messageReducer = (state = [], action) => {
  switch (action.type) {
    case ADD:
      return [
        ...state,
        action.message
      ];
    default:
      return state;
  }
};

const store = Redux.createStore(messageReducer);

// React:
class Presentational extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      input: '',
      messages: []
    }
    this.handleChange = this.handleChange.bind(this);
    this.submitMessage = this.submitMessage.bind(this);
  }
  handleChange(event) {
    this.setState({
      input: event.target.value
    });
  }
  submitMessage() {
    this.setState((state) => {
      const currentMessage = state.input;
      return {
        input: '',
        messages: state.messages.concat(currentMessage)
      };
    });
  }
  render() {
    return (
      <div>
        <h2>Type in a new Message:</h2>
        <input
          value={this.state.input}
          onChange={this.handleChange}/><br/>
        <button onClick={this.submitMessage}>Submit</button>
        <ul>
          {this.state.messages.map( (message, idx) => {
              return (
                 <li key={idx}>{message}</li>
              )
            })
          }
        </ul>
      </div>
    );
  }
};

// React-Redux:
const mapStateToProps = (state) => {
  return { messages: state }
};

const mapDispatchToProps = (dispatch) => {
  return {
    submitNewMessage: (newMessage) => {
       dispatch(addMessage(newMessage))
    }
  }
};

const Provider = ReactRedux.Provider;
const connect = ReactRedux.connect;

// Define the Container component here:
const Container = connect(mapStateToProps, mapDispatchToProps)(Presentational);

class AppWrapper extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    // Complete the return statement:
    return (
      <Provider store={store}>
        <Container />
      </Provider>
    );
  }
};

将本地 state 提取到 Redux 中

现在 Redux 已连接, 你需要将状态管理从 Presentational 组件中提取到 Redux 中。 目前,你已连接 Redux, 但你正在 Presentational 组件中本地处理状态。 在 Presentational 组件中, 首先删除本地 state 中的 messages 属性。 这些消息将由 Redux 管理。 接下来,修改 submitMessage() 方法, 使其从 this.props 调度 submitNewMessage() , 并将来自本地 state 的当前消息输入作为参数传入。 由于你从本地状态中删除了 messages , 因此也在此处从对 this.setState() 的调用中删除了 messages 属性。 最后,修改 render() 方法, 使其映射从 props 而不是 state 接收的消息。 进行这些更改后,应用将继续正常运行,但 Redux 管理状态除外。 此示例还说明了组件如何具有本地 state : 你的组件仍然在其自己的 state 中本地跟踪用户输入。 你可以看到 Redux 如何在 React 之上提供一个有用的状态管理框架。 一开始,你只使用 React 的本地状态就获得了相同的结果, 这通常可以通过简单的应用程序来实现。 但是,随着你的应用程序变得更大、更复杂, 你的状态管理也会变得更复杂,这就是 Redux 解决的问题。 本地 state 管理 input, 其它交给 Redux (message 存到 Redux store), 连接 React 与 Redux: 1. 将 Redux state 映射到 React 的 props 中Redux state 存储数据 React 从 props 中访问 Redux 存储的状态数据将 Redux dispatch 映射到 React 的 props 中Redux dispatch 更新状态数据 React 从 props 中取出来更新 Redux 管理的状态数据

代码语言:javascript
复制
// Redux:
const ADD = 'ADD';

const addMessage = (message) => {
  return {
    type: ADD,
    message: message
  }
};

const messageReducer = (state = [], action) => {
  switch (action.type) {
    case ADD:
      return [
        ...state,
        action.message
      ];
    default:
      return state;
  }
};

const store = Redux.createStore(messageReducer);

// React:
const Provider = ReactRedux.Provider;
const connect = ReactRedux.connect;

// Change code below this line
class Presentational extends React.Component {
  constructor(props) {
    super(props);
    // Remove property 'messages' from Presentational's local state
    this.state = {
      input: '',
      // messages: []
    }
    this.handleChange = this.handleChange.bind(this);
    this.submitMessage = this.submitMessage.bind(this);
  }
  handleChange(event) {
    this.setState({
      input: event.target.value
    });
  }
  submitMessage() {
    // Call 'submitNewMessage', which has been mapped to Presentational's props, with a new message;
    // meanwhile, remove the 'messages' property from the object returned by this.setState().
    this.props.submitNewMessage(this.state.input);
    this.setState({
      input: ''
    });
  }
  render() {
    return (
      <div>
        <h2>Type in a new Message:</h2>
        <input
          value={this.state.input}
          onChange={this.handleChange}/><br/>
        <button onClick={this.submitMessage}>Submit</button>
        <ul>
          {/* The messages state is mapped to Presentational's props; therefore, when rendering,
               you should access the messages state through props, instead of Presentational's
               local state. */}
          {this.props.messages.map( (message, idx) => {
              return (
                 <li key={idx}>{message}</li>
              )
            })
          }
        </ul>
      </div>
    );
  }
};
// Change code above this line

const mapStateToProps = (state) => {
  return {messages: state}
};

const mapDispatchToProps = (dispatch) => {
  return {
    submitNewMessage: (message) => {
      dispatch(addMessage(message))
    }
  }
};

const Container = connect(mapStateToProps, mapDispatchToProps)(Presentational);

class AppWrapper extends React.Component {
  render() {
    return (
      <Provider store={store}>
        <Container/>
      </Provider>
    );
  }
};

Moving Forward From Here

代码语言:javascript
复制
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider, connect } from 'react-redux'
import { createStore, combineReducers, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'

import rootReducer from './redux/reducers'
import App from './components/App'

const store = createStore(
  rootReducer,
  applyMiddleware(thunk)
);

ReactDOM.render(
  <Provider store={store}>
    <App/>
  </Provider>,
  document.getElementById('root')
);


// Only change code below this line
console.log("Now I know React and Redux!");

Projects

构建随机名言机

参考:

Q&A

补充

React.js Examples

参考:

实现 Sass 注释的三种方式

参考:

1. // 注释内容

在 Sass 中,这种注释方式在 编译后不会保留下来

2. /* 注释内容 */

在 Sass 中,这种注释方式在 编译后会保留下来。 因为这种注释方式跟 CSS 注释方式是相同的,所以编译后会保留下来。

3. /*! 注释内容 */

我们都知道压缩工具会删除所有的注释, 有些时候为了保留一些版权声明的注释说明,可以采用以下方式:

代码语言:javascript
复制
/*! 注释内容 */

也就是说在注释内容前面加上一个 !,这种压缩工具就不会删除这条注释信息了。 不过这种注释方式用得很少,一般在 CSS 文件顶部为了声明版权信息才会使用。 Sass

代码语言:javascript
复制
/*! Copyright ©2023-present yiyun, All Rights Reserved */
$height: 20px;

body {
  height: $height;
  line-height: $height;
}

编译出来的 CSS 代码如下: CSS

代码语言:javascript
复制
/*! Copyright ©2023-present yiyun, All Rights Reserved */
body {
  height: 20px;
  line-height: 20px;
}

参考

感谢帮助!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-06-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引言
  • SASS
    • 使用 Sass 变量存储数据
      • 嵌套 CSS
        • 使用 Mixins 创建可重用的 CSS
          • 使用 @if 和 @else 为样式添加逻辑
            • 使用 @for 创建 Sass 循环
              • 使用 @each 映射列表中的项
                • 应用样式直到条件满足 @while
                  • 使用 Partials 将样式拆分为更小的块
                    • 将一组 CSS 样式扩展 (**@extend**) 到另一个元素
                    • React.js
                      • 注释: { /* 我是注释 */ }
                        • 标签必须闭合
                          • 组件
                            • 默认 props 值: MyComponent.defaultProps
                              • 重写 默认 props 值
                                • 通过 propTypes 限制 props
                                  • 使用 this.props 访问 props
                                    • 综合示例: propTypes, defaultProps
                                      • Create a Stateful Component
                                        • 使用 setState 更新 state
                                          • 综合示例
                                            • 可控的 input
                                              • 可控的 Form
                                                • Pass State as Props to Child Components
                                                  • Pass a Callback as Props
                                                    • 生命周期: componentWillMount
                                                      • 生命周期: componentDidMount
                                                        • 添加事件侦听器
                                                          • 使用 shouldComponentUpdate 优化重新渲染
                                                            • 内联样式(**Inline Styles**)
                                                              • 在 React 渲染方法中使用高级 JavaScript
                                                                • 使用 if-else 条件渲染
                                                                  • 使用 && 简化条件
                                                                    • 使用 三元表达式 进行条件渲染
                                                                      • 从 props 有条件地渲染
                                                                        • 根据组件状态(state)有条件地更改内联 CSS
                                                                          • 使用 Array.map() 动态渲染元素
                                                                            • 为同级元素提供唯一的 key 属性
                                                                              • 使用 Array.filter() 动态过滤数组
                                                                                • 使用 renderToString 在服务端渲染 React
                                                                                • Redux
                                                                                  • 创建 Redux Store
                                                                                    • 从 Redux Store 中获取状态
                                                                                      • 定义 Redux Action
                                                                                        • 定义 Action 创建者
                                                                                          • Dispatch an Action Event
                                                                                            • 处理 Store 中的 Action
                                                                                              • 使用 switch 语句处理多个 Action
                                                                                                • 将 const 用于 Action 类型
                                                                                                  • 注册 store 侦听器
                                                                                                    • 组合多个 reducer
                                                                                                      • 发送 Action 数据到 Store
                                                                                                        • 使用中间件处理异步 Action
                                                                                                          • 用 Redux 写一个计数器
                                                                                                            • 不可变 state
                                                                                                              • 在数组上使用展开运算符
                                                                                                                • 从数组中删除一个项
                                                                                                                  • 使用 Object.assign 复制一个对象
                                                                                                                  • 将 Redux 与 React 结合使用
                                                                                                                    • 开始使用 React Redux
                                                                                                                      • 首先在本地管理 state
                                                                                                                        • 将状态(state)逻辑提取到 Redux
                                                                                                                          • 使用 Provider 将 Redux 连接到 React
                                                                                                                            • 将 state 映射到 props
                                                                                                                              • 将 dispatch 映射到 props
                                                                                                                                • 将 Redux 连接到 React
                                                                                                                                  • 将 Redux 连接到 "消息" 应用
                                                                                                                                    • 将本地 state 提取到 Redux 中
                                                                                                                                      • Moving Forward From Here
                                                                                                                                      • Projects
                                                                                                                                        • 构建随机名言机
                                                                                                                                        • Q&A
                                                                                                                                        • 补充
                                                                                                                                          • React.js Examples
                                                                                                                                            • 实现 Sass 注释的三种方式
                                                                                                                                              • 1. // 注释内容
                                                                                                                                              • 2. /* 注释内容 */
                                                                                                                                              • 3. /*! 注释内容 */
                                                                                                                                          • 参考
                                                                                                                                            • 嵌套 CSS
                                                                                                                                              • 使用 Mixins 创建可重用的 CSS
                                                                                                                                                • 使用 @if 和 @else 为样式添加逻辑
                                                                                                                                                  • 使用 @for 创建 Sass 循环
                                                                                                                                                    • 使用 @each 映射列表中的项
                                                                                                                                                      • 应用样式直到条件满足 @while
                                                                                                                                                        • 使用 Partials 将样式拆分为更小的块
                                                                                                                                                          • 将一组 CSS 样式扩展 (**@extend**) 到另一个元素
                                                                                                                                                          • React.js
                                                                                                                                                            • 注释: { /* 我是注释 */ }
                                                                                                                                                              • 标签必须闭合
                                                                                                                                                                • 组件
                                                                                                                                                                  • 默认 props 值: MyComponent.defaultProps
                                                                                                                                                                    • 重写 默认 props 值
                                                                                                                                                                      • 通过 propTypes 限制 props
                                                                                                                                                                        • 使用 this.props 访问 props
                                                                                                                                                                          • 综合示例: propTypes, defaultProps
                                                                                                                                                                            • Create a Stateful Component
                                                                                                                                                                              • 使用 setState 更新 state
                                                                                                                                                                                • 综合示例
                                                                                                                                                                                  • 可控的 input
                                                                                                                                                                                    • 可控的 Form
                                                                                                                                                                                      • Pass State as Props to Child Components
                                                                                                                                                                                        • Pass a Callback as Props
                                                                                                                                                                                          • 生命周期: componentWillMount
                                                                                                                                                                                            • 生命周期: componentDidMount
                                                                                                                                                                                              • 添加事件侦听器
                                                                                                                                                                                                • 使用 shouldComponentUpdate 优化重新渲染
                                                                                                                                                                                                  • 内联样式(**Inline Styles**)
                                                                                                                                                                                                    • 在 React 渲染方法中使用高级 JavaScript
                                                                                                                                                                                                      • 使用 if-else 条件渲染
                                                                                                                                                                                                        • 使用 && 简化条件
                                                                                                                                                                                                          • 使用 三元表达式 进行条件渲染
                                                                                                                                                                                                            • 从 props 有条件地渲染
                                                                                                                                                                                                              • 根据组件状态(state)有条件地更改内联 CSS
                                                                                                                                                                                                                • 使用 Array.map() 动态渲染元素
                                                                                                                                                                                                                  • 为同级元素提供唯一的 key 属性
                                                                                                                                                                                                                    • 使用 Array.filter() 动态过滤数组
                                                                                                                                                                                                                      • 使用 renderToString 在服务端渲染 React
                                                                                                                                                                                                                      • Redux
                                                                                                                                                                                                                        • 创建 Redux Store
                                                                                                                                                                                                                          • 从 Redux Store 中获取状态
                                                                                                                                                                                                                            • 定义 Redux Action
                                                                                                                                                                                                                              • 定义 Action 创建者
                                                                                                                                                                                                                                • Dispatch an Action Event
                                                                                                                                                                                                                                  • 处理 Store 中的 Action
                                                                                                                                                                                                                                    • 使用 switch 语句处理多个 Action
                                                                                                                                                                                                                                      • 将 const 用于 Action 类型
                                                                                                                                                                                                                                        • 注册 store 侦听器
                                                                                                                                                                                                                                          • 组合多个 reducer
                                                                                                                                                                                                                                            • 发送 Action 数据到 Store
                                                                                                                                                                                                                                              • 使用中间件处理异步 Action
                                                                                                                                                                                                                                                • 用 Redux 写一个计数器
                                                                                                                                                                                                                                                  • 不可变 state
                                                                                                                                                                                                                                                    • 在数组上使用展开运算符
                                                                                                                                                                                                                                                      • 从数组中删除一个项
                                                                                                                                                                                                                                                        • 使用 Object.assign 复制一个对象
                                                                                                                                                                                                                                                        • 将 Redux 与 React 结合使用
                                                                                                                                                                                                                                                          • 开始使用 React Redux
                                                                                                                                                                                                                                                            • 首先在本地管理 state
                                                                                                                                                                                                                                                              • 将状态(state)逻辑提取到 Redux
                                                                                                                                                                                                                                                                • 使用 Provider 将 Redux 连接到 React
                                                                                                                                                                                                                                                                  • 将 state 映射到 props
                                                                                                                                                                                                                                                                    • 将 dispatch 映射到 props
                                                                                                                                                                                                                                                                      • 将 Redux 连接到 React
                                                                                                                                                                                                                                                                        • 将 Redux 连接到 "消息" 应用
                                                                                                                                                                                                                                                                          • 将本地 state 提取到 Redux 中
                                                                                                                                                                                                                                                                            • Moving Forward From Here
                                                                                                                                                                                                                                                                            • Projects
                                                                                                                                                                                                                                                                              • 构建随机名言机
                                                                                                                                                                                                                                                                              • Q&A
                                                                                                                                                                                                                                                                              • 补充
                                                                                                                                                                                                                                                                                • React.js Examples
                                                                                                                                                                                                                                                                                  • 实现 Sass 注释的三种方式
                                                                                                                                                                                                                                                                                    • 1. // 注释内容
                                                                                                                                                                                                                                                                                    • 2. /* 注释内容 */
                                                                                                                                                                                                                                                                                    • 3. /*! 注释内容 */
                                                                                                                                                                                                                                                                                • 参考
                                                                                                                                                                                                                                                                                相关产品与服务
                                                                                                                                                                                                                                                                                多因子身份认证
                                                                                                                                                                                                                                                                                多因子身份认证(Multi-factor Authentication Service,MFAS)的目的是建立一个多层次的防御体系,通过结合两种或三种认证因子(基于记忆的/基于持有物的/基于生物特征的认证因子)验证访问者的身份,使系统或资源更加安全。攻击者即使破解单一因子(如口令、人脸),应用的安全依然可以得到保障。
                                                                                                                                                                                                                                                                                领券
                                                                                                                                                                                                                                                                                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档