React17.0.2官方文档在线学习-核心概念

核心概念

JSX的使用

介绍:JSX是一个 JavaScript 的语法扩展。我们建议在 React 中配合使用 JSX,JSX 可以很好地描述 UI 应该呈现出它应有交互的本质形式。JSX 可能会使人联想到模版语言,但它具有 JavaScript 的全部功能。

特点

  • 在 JSX 中嵌入表达式。在标签中使用{}就可以使用变量和方法。
  • JSX 也是一个表达式。也就是说,你可以在 if 语句和 for 循环的代码块中使用 JSX,将 JSX 赋值给变量,把 JSX 当作参数传入,以及从函数中返回 JSX。
  • JSX 特定属性。通过使用引号,来将属性值指定为字符串字面量;也可以使用大括号,来在属性值中插入一个 JavaScript 表达式,比如使用变量或者一个函数等。在属性中嵌入 JavaScript 表达式时,不要在大括号外面加上引号。你应该仅使用引号(对于字符串值)或大括号(对于表达式)中的一个,对于同一属性不能同时使用这两种符号。
  • 使用 JSX 指定子元素。 JSX 标签里能够包含很多子元素
const element = (
  <div>
    <h1>Hello!</h1>
    <h2>Good to see you here.</h2>
  </div>
);
  • JSX 防止注入攻击。 React DOM 在渲染所有输入内容之前,默认会进行转义。它可以确保在你的应用中,永远不会注入那些并非自己明确编写的内容。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止 XSS(cross-site-scripting, 跨站脚本)攻击。

  • JSX 表示对象。 Babel 会把 JSX 转译成一个名为 React.createElement() 函数调用。

const element = (
  <h1 className="greeting">
    Hello, world!
  </h1>
);
// 等同于
const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);

元素渲染

介绍:元素是构成 React 应用的最小砖块。

特点

  • 将一个元素渲染为 DOM。 想要将一个 React 元素渲染到根 DOM 节点中,只需把它们一起传入 ReactDOM.render()
  • 更新已渲染的元素。 根据我们已有的知识,更新 UI 唯一的方式是创建一个全新的元素,并将其传入 ReactDOM.render()。
  • React 只更新它需要更新的部分。 React DOM 会将元素和它的子元素与它们之前的状态进行比较,并只会进行必要的更新来使 DOM 达到预期的状态。

组件 & Props

介绍:组件允许你将 UI 拆分为独立可复用的代码片段,并对每个片段进行独立构思。
特点

  • 函数组件与 class 组件
    • 函数组件:该函数是一个有效的 React 组件,因为它接收唯一带有数据的 “props”(代表属性)对象与并返回一个 React 元素。这类组件被称为“函数组件”,因为它本质上就是 JavaScript 函数。
    • 类组件:使用ES6的class属性继承React.Component
// 函数组件
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}
// 等同于

// 类组件
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
  • 渲染组件
    介绍:React不仅仅可以渲染DOM中的标签,也可以渲染自定义组件。当 React 元素为用户自定义组件时,它会将 JSX 所接收的属性(attributes)以及子组件(children)转换为单个对象传递给组件,这个对象被称之为 “props”。
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

const element = <Welcome name="Sara" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

渲染的过程:

  1. 我们调用 ReactDOM.render() 函数,并传入 <Welcome name=“Sara” /> 作为参数。
  2. React 调用 Welcome 组件,并将 {name: ‘Sara’} 作为 props 传入。
  3. Welcome 组件将<h1>Hello, Sara</h1> 元素作为返回值。
  4. React DOM 将 DOM 高效地更新为 <h1>Hello, Sara<\h1>。
  • 组合组件
    介绍:组件可以在其输出中引用其他组件。
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

function App() {
  return (
    <div>
      <Welcome name="Sara" />
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </div>
  );
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);
  • 提取组件。 将组件拆分为更小的组件

  • Props 的只读性。组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props。
    特点:所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。

纯函数:该函数不会尝试更改入参,且多次调用下相同的入参始终返回相同的结果。

function sum(a, b) {
  return a + b;
}

非纯函数示例,因为修改了传入的参数amount和account.total的值

function withdraw(account, amount) {
  account.total -= amount;
}

State & 生命周期

  • state和props的区别
  1. State 与 props 类似,但是 state 是私有的,并且完全受控于当前组件。
  • 将函数组件转换成 class 组件
    通过以下五步将 Clock 的函数组件转成 class 组件:

    1. 创建一个同名的 ES6 class,并且继承于 React.Component。
    2. 添加一个空的 render() 方法。
    3. 将函数体移动到 render() 方法之中。
    4. 在 render() 方法中使用 this.props 替换 props。
    5. 删除剩余的空函数声明。
  • 向 class 组件中添加局部的 state
    我们通过以下三步将 date 从 props 移动到 state 中:

    1. 把 render() 方法中的 this.props.date 替换成 this.state.date

    2. 添加一个 class 构造函数,然后在该函数中为 this.state 赋初值:

      class Clock extends React.Component {
      constructor(props) {
      	super(props);
      	 this.state = {date: new Date()};
       }
      
      render() {
      	return (
      	 	<div>
      			<h1>Hello, world!</h1>
      			<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      		</div>
      		);
      	}
      }
      
    3. 移除 <Clock /> 元素中的 date 属性,因为当前组件已经可以通过state获取到date的值

  • 将生命周期方法添加到 Class 中
    常用的生命周期函数。

    • 挂载阶段
      constructor:当组件创建的时候调用

      componentWillMount:当组件被挂载到页面的时候执行(挂载页面之前)

      componentDidMount:当组件挂载之后执行(页面渲染之后)

      render: 当state或者props改变的时候调用

    • 更新阶段
      shouldComponentUpdate:组件更新之前它会自动执行

      componentWillUpdate:组件更新之前,它会自动执行,但是在shouldComponentUpdate之后被执行,如果shouldComponentUpdate返回值是true才被执行,如果返回值是false,则不执行页面也不会更新。

      componentWillReceiveProps:两种情况会执行,第一种情况当一个组件从父组件接收了参数;第二种情况,只要父组件的render函数被执行了,子组件的这个生命周期函数才会被执行。

      render: 当state或者props改变的时候调用

    • 卸载阶段
      componentWillUnmount:当组件即将卸载之前,页面销毁之前调用。

  • 正确地使用 State
    setState() 你应该了解三件事

    • 不要直接修改 State
      构造函数是唯一可以给 this.state 赋值的地方,其余地方修改state的值应该通过setState的方式修改。

    • State 的更新可能是异步的
      因为 this.props 和 this.state 可能会异步更新,所以你不要依赖他们的值来更新下一个状态。

      要解决这个问题,可以让 setState() 接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数

      	// Correct
      	this.setState((state, props) => ({
      		counter: state.counter + props.increment
      	}));
      
    • State 的更新会被合并,当你调用 setState() 的时候,React 会把你提供的对象合并到当前的 state。

      这里的合并是浅合并,所以 this.setState({comments}) 完整保留了 this.state.posts, 但是完全替换了 this.state.comments。

      	componentDidMount() {
          fetchPosts().then(response => {
            this.setState({
              posts: response.posts
            });
          });
      
          fetchComments().then(response => {
            this.setState({
              comments: response.comments
            });
          });
        }
      
  • 数据是向下流动的
    这通常会被叫做“自上而下”或是“单向”的数据流。任何的 state 总是所属于特定的组件,而且从该 state 派生的任何数据或 UI 只能影响树中“低于”它们的组件。

    组件可以选择把它的 state 作为 props 向下传递到它的子组件中。

事件处理

特点:

  1. React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
  2. 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
  3. 在 React 中另一个不同点是你不能通过返回 false 的方式阻止默认行为。你必须显式的使用 preventDefault 。例如,
/** 传统的 HTML 中阻止链接默认打开一个新页面,你可以这样写 */
<a href="#" onclick="console.log('The link was clicked.'); return false">
  Click me
</a>
// React的写法
function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}

注意点:
必须谨慎对待 JSX 回调函数中的 this,在 JavaScript 中,class 的方法默认不会绑定 this。如果你忘记绑定 this.handleClick 并把它传入了 onClick,当你调用这个函数的时候 this 的值为 undefined。

解决方式有两种

  • 使用 class fields 正确的绑定回调函数
class LoggingButton extends React.Component {
  // 此语法确保 `handleClick` 内的 `this` 已被绑定。
  // 注意: 这是 *实验性* 语法。
  handleClick = () => {
    console.log('this is:', this);
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    );
  }
}
  • 回调中使用箭头函数
class LoggingButton extends React.Component {
  handleClick() {
    console.log('this is:', this);
  }

  render() {
    // 此语法确保 `handleClick` 内的 `this` 已被绑定。
    return (
      <button onClick={() => this.handleClick()}>
        Click me
      </button>
    );
  }
}

这两种方式的区别是:

  1. 第二种方式问题在于每次渲染 LoggingButton 时都会创建不同的回调函数。在大多数情况下,这没什么问题,但如果该回调函数作为 prop 传入子组件时,这些组件可能会进行额外的重新渲染。我们通常建议在构造器中绑定或使用 class fields 语法来避免这类性能问题。
  2. 向事件处理传递参数时,使用bind绑定的方式,传参是隐式传参;但是使用箭头函数是显式传参,即函数需要必传的参数必须传入。

向事件处理程序传递参数

两种实现参数传递的方式

  • funtion使用bind绑定this
  • 箭头函数
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

两种区别:在这两种情况下,React 的事件对象 e 会被作为第二个参数传递。如果通过箭头函数的方式,事件对象必须显式的进行传递,而通过 bind 的方式,事件对象以及更多的参数将会被隐式的进行传递。

条件渲染

介绍:在 React 中,你可以创建不同的组件来封装各种你需要的行为。然后,依据应用的不同状态,你可以只渲染对应状态下的部分内容。

  • 元素变量
    说明:使用变量来储存元素。 它可以帮助你有条件地渲染组件的一部分,而其他的渲染部分并不会因此而改变。
class LoginControl extends React.Component {
  constructor(props) {
    super(props);
    this.handleLoginClick = this.handleLoginClick.bind(this);
    this.handleLogoutClick = this.handleLogoutClick.bind(this);
    this.state = {isLoggedIn: false};
  }

  handleLoginClick() {
    this.setState({isLoggedIn: true});
  }

  handleLogoutClick() {
    this.setState({isLoggedIn: false});
  }

  render() {
    const isLoggedIn = this.state.isLoggedIn;
    let button;
    if (isLoggedIn) {
      button = <LogoutButton onClick={this.handleLogoutClick} />;
    } else {
      button = <LoginButton onClick={this.handleLoginClick} />;
    }

    return (
      <div>
        <Greeting isLoggedIn={isLoggedIn} />
        {button}
      </div>
    );
  }
}

ReactDOM.render(
  <LoginControl />,
  document.getElementById('root')
);
  • 与运算符 &&
    说明:通过花括号包裹代码,你可以在 JSX 中嵌入任何表达式。这也包括 JavaScript 中的逻辑与 (&&) 运算符。它可以很方便地进行元素的条件渲染。

实现的思路:在 JavaScript 中,true && expression 总是会返回 expression, 而 false && expression 总是会返回 false。因此,如果条件是 true,&& 右侧的元素就会被渲染,如果是 false,React 会忽略并跳过它。

function Mailbox(props) {
  const unreadMessages = props.unreadMessages;
  return (
    <div>
      <h1>Hello!</h1>
      {unreadMessages.length > 0 &&
        <h2>
          You have {unreadMessages.length} unread messages.
        </h2>
      }
    </div>
  );
}

const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
  <Mailbox unreadMessages={messages} />,
  document.getElementById('root')
);
  • 三目运算符
    说明:另一种内联条件渲染的方法是使用 JavaScript 中的三目运算符 condition ? true : false。
render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
    </div>
  );
}
  • 阻止组件渲染
    说明:在极少数情况下,你可能希望能隐藏组件,即使它已经被其他组件渲染。若要完成此操作,你可以让 render 方法直接返回 null,而不进行任何渲染。

注意点:在组件的 render 方法中返回 null 并不会影响组件的生命周期。例如,下面这个示例中,componentDidUpdate 依然会被调用。因为组件并未卸载。

function WarningBanner(props) {
  if (!props.warn) {
    return null;
  }

  return (
    <div className="warning">
      Warning!
    </div>
  );
}

class Page extends React.Component {
  constructor(props) {
    super(props);
    this.state = {showWarning: true};
    this.handleToggleClick = this.handleToggleClick.bind(this);
  }

  handleToggleClick() {
    this.setState(state => ({
      showWarning: !state.showWarning
    }));
  }

  render() {
    return (
      <div>
        <WarningBanner warn={this.state.showWarning} />
        <button onClick={this.handleToggleClick}>
          {this.state.showWarning ? 'Hide' : 'Show'}
        </button>
      </div>
    );
  }
}

ReactDOM.render(
  <Page />,
  document.getElementById('root')
);

列表 & Key

  • 渲染多个组件
    说明:你可以通过使用 {} 在 JSX 内构建一个元素集合
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
  <li>{number}</li>
);

ReactDOM.render(
  <ul>{listItems}</ul>,
  document.getElementById('root')
);
  • 基础列表组件
    说明:通常你需要在一个组件中渲染列表。我们可以把前面的例子重构成一个组件,这个组件接收 numbers 数组作为参数并输出一个元素列表。
function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <li key={number.toString()}>
      {number}
    </li>
  );
  return (
    <ul>{listItems}</ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);
  • key
    说明:key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。
    注意点:
    1. 一个元素的 key 最好是这个元素在列表中拥有的一个独一无二的字符串。通常,我们使用数据中的 id 来作为元素的 key
    2. 当元素没有确定 id 的时候,万不得已你可以使用元素索引 index 作为 key
    3. 如果列表项目的顺序可能会变化,我们不建议使用索引来用作 key 值,因为这样做会导致性能变差,还可能引起组件状态的问题。
    4. 元素的 key 只有放在就近的数组上下文中才有意义
    5. key 只是在兄弟节点之间必须唯一

Robin Pokorny 的深度解析使用索引作为 key 的负面影响
为什么Key值是唯一的

  • 用 key 提取组件
    说明:如果你提取出一个 ListItem 组件,你应该把 key 保留在数组中的这个 <ListItem /> 元素上,而不是放在 ListItem 组件中的 <li> 元素上。

    一个好的经验法则是:在 map() 方法中的元素需要设置 key 属性。

  • key 只是在兄弟节点之间必须唯一
    说明:数组元素中使用的 key 在其兄弟节点之间应该是独一无二的。然而,它们不需要是全局唯一的。当我们生成两个不同的数组时,我们可以使用相同的 key 值

  • 在 JSX 中嵌入 map()
    说明:JSX 允许在大括号中嵌入任何表达式,所以我们可以内联 map() 返回的结果

function NumberList(props) {
  const numbers = props.numbers;
  return (
    <ul>
      {numbers.map((number) =>
        <ListItem key={number.toString()}
                  value={number} />
      )}
    </ul>
  );
}

表单

  • 受控组件
    说明:使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。

    对于受控组件来说,输入的值始终由 React 的 state 驱动。你也可以将 value 传递给其他 UI 元素,或者通过其他事件处理函数重置,但这意味着你需要编写更多的代码。

  • select 标签
    与其他的受控组件的不同地方:React 并不会使用 selected 属性,而是在根 select 标签上使用 value 属性。这在受控组件中更便捷,因为您只需要在根标签中更新它。

render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          选择你喜欢的风味:
          <select value={this.state.value} onChange={this.handleChange}>
            <option value="grapefruit">葡萄柚</option>
            <option value="lime">酸橙</option>
            <option value="coconut">椰子</option>
            <option value="mango">芒果</option>
          </select>
        </label>
        <input type="submit" value="提交" />
      </form>
    );
  }

注意点:
你可以将数组传递到 value 属性中,以支持在 select 标签中选择多个选项:

<select multiple={true} value={['B', 'C']}>
  • 文件 input 标签
    说明:在 HTML 中,<input type=“file”> 允许用户从存储设备中选择一个或多个文件,将其上传到服务器,或通过使用 JavaScript 的 File API进行控制。
    注意点:因为它的 value 只读,所以它是 React 中的一个非受控组件。将与其他非受控组件在后续文档中一起讨论。
<input type="file" />
  • 处理多个输入
    说明:当需要处理多个 input 元素时,我们可以给每个元素添加 name 属性,并让处理函数根据 event.target.name 的值选择要执行的操作。
class Reservation extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isGoing: true,
      numberOfGuests: 2
    };

    this.handleInputChange = this.handleInputChange.bind(this);
  }

  handleInputChange(event) {
    const target = event.target;
    const value = target.name === 'isGoing' ? target.checked : target.value;
    const name = target.name;

    this.setState({
      [name]: value
    });
  }

  render() {
    return (
      <form>
        <label>
          参与:
          <input
            name="isGoing"
            type="checkbox"
            checked={this.state.isGoing}
            onChange={this.handleInputChange} />
        </label>
        <br />
        <label>
          来宾人数:
          <input
            name="numberOfGuests"
            type="number"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange} />
        </label>
      </form>
    );
  }
}
  • 受控输入空值
    说明:在受控组件上指定 value 的 prop 会阻止用户更改输入。如果你指定了 value,但输入仍可编辑,则可能是你意外地将value 设置为 undefined 或 null。

    输入最初被锁定,但在短时间延迟后变为可编辑。

ReactDOM.render(<input value="hi" />, mountNode);

setTimeout(function() {
  ReactDOM.render(<input value={null} />, mountNode);
}, 1000);
  • 受控组件的替代品
    说明:有时使用受控组件会很麻烦,因为你需要为数据变化的每种方式都编写事件处理函数,并通过一个 React 组件传递所有的输入 state。当你将之前的代码库转换为 React 或将 React 应用程序与非 React 库集成时,这可能会令人厌烦。在这些情况下,你可能希望使用非受控组件, 这是实现输入表单的另一种方式。
  • 成熟的解决方案
    说明:表单验证等拓展性内容,需要深入的学习。

状态提升

说明:通常,多个组件需要反映相同的变化数据,这时我们建议将共享状态提升到最近的共同父组件中去。
总结:
1. 在 React 应用中,任何可变数据应当只有一个相对应的唯一“数据源”。通常,state 都是首先添加到需要渲染数据的组件中去。然后,如果其他组件也需要这个 state,那么你可以将它提升至这些组件的最近共同父组件中。你应当依靠 自上而下 的数据流,而不是尝试在不同组件间同步 state。
2. 虽然提升 state 方式比双向绑定方式需要编写更多的“样板”代码,但带来的好处是,排查和隔离 bug 所需的工作量将会变少。由于“存在”于组件中的任何 state,仅有组件自己能够修改它,因此 bug 的排查范围被大大缩减了。此外,你也可以使用自定义逻辑来拒绝或转换用户的输入。
3. 如果某些数据可以由 props 或 state 推导得出,那么它就不应该存在于 state 中。

组合 vs 继承

  • 组合

    • 包含关系
      说明:有些组件无法提前知晓它们子组件的具体内容。
      function FancyBorder(props) {
        return (
          <div className={'FancyBorder FancyBorder-' + props.color}>
            {props.children}
          </div>
        );
      }
      
    • 特例关系
      说明:“特殊”组件可以通过 props 定制并渲染“一般”组件
      function Dialog(props) {
        return (
          <FancyBorder color="blue">
            <h1 className="Dialog-title">
              {props.title}
            </h1>
            <p className="Dialog-message">
              {props.message}
            </p>
          </FancyBorder>
        );
      }
      
      function WelcomeDialog() {
        return (
          <Dialog
            title="Welcome"
            message="Thank you for visiting our spacecraft!" />
        );
      }
      
  • 继承

    Props 和组合为你提供了清晰而安全地定制组件外观和行为的灵活方式。注意:组件可以接受任意 props,包括基本数据类型,React 元素以及函数。

    如果你想要在组件间复用非 UI 的功能,我们建议将其提取为一个单独的 JavaScript 模块,如函数、对象或者类。组件可以直接引入(import)而无需通过 extend 继承它们。

React 设计思想

React 最棒的部分之一是引导我们思考如何构建一个应用。

  • 从设计稿开始
    • 第一步:将设计好的 UI 划分为组件层级
      首先,你需要在设计稿上用方框圈出每一个组件(包括它们的子组件),并且以合适的名称命名。

      但你如何确定应该将哪些部分划分到一个组件中呢?你可以将组件当作一种函数或者是对象来考虑,根据单一功能原则来判定组件的范围。也就是说,一个组件原则上只能负责一个功能。如果它需要负责更多的功能,这时候就应该考虑将它拆分成更小的组件。

      在实践中,因为你经常是在向用户展示 JSON 数据模型,所以如果你的模型设计得恰当,UI(或者说组件结构)便会与数据模型一一对应,这是因为 UI 和数据模型都会倾向于遵守相同的信息结构。将 UI 分离为组件,其中每个组件需与数据模型的某部分匹配。

    • 第二步:用 React 创建一个静态版本
      实现方式:最容易的方式,是先用已有的数据模型渲染一个不包含交互功能的 UI。最好将渲染 UI 和添加交互这两个过程分开。这是因为,编写一个应用的静态版本时,往往要编写大量代码,而不需要考虑太多交互细节;添加交互功能时则要考虑大量细节,而不需要编写太多代码。所以,将这两个过程分开进行更为合适。我们会在接下来的代码中体会到其中的区别。

      注意点

      1. 最容易的方式,是先用已有的数据模型渲染一个不包含交互功能的 UI。最好将渲染 UI 和添加交互这两个过程分开。这是因为,编写一个应用的静态版本时,往往要编写大量代码,而不需要考虑太多交互细节;添加交互功能时则要考虑大量细节,而不需要编写太多代码。所以,将这两个过程分开进行更为合适。我们会在接下来的代码中体会到其中的区别。
      2. 由于我们构建的是静态版本,所以这些组件目前只需提供 render() 方法用于渲染。

      你可以自上而下或者自下而上构建应用:自上而下意味着首先编写层级较高的组件(比如 FilterableProductTable),自下而上意味着从最基本的组件开始编写(比如 ProductRow)。当你的应用比较简单时,使用自上而下的方式更方便;对于较为大型的项目来说,自下而上地构建,并同时为低层组件编写测试是更加简单的方式。

    • 第三步:确定 UI state 的最小(且完整)表示。
      说明:首先需要找出应用所需的 state 的最小表示,并根据需要计算出其他所有数据。

      通过问自己以下三个问题,你可以逐个检查相应数据是否属于 state:

      1. 该数据是否是由父组件通过 props 传递而来的?如果是,那它应该不是 state。
      2. 该数据是否随时间的推移而保持不变?如果是,那它应该也不是 state。
      3. 你能否根据其他 state 或 props 计算出该数据的值?如果是,那它也不是 state。
    • 第四步:确定 state 放置的位置
      注意:React 中的数据流是单向的,并顺着组件层级从上往下传递。

      哪个组件应该拥有state的判断:

      • 对于应用中的每一个 state
        • 找到根据这个 state 进行渲染的所有组件。
        • 找到他们的共同所有者(common owner)组件(在组件层级上高于所有需要该 state 的组件)。
        • 该共同所有者组件或者比它层级更高的组件应该拥有该 state。
        • 如果你找不到一个合适的位置来存放该 state,就可以直接创建一个新的组件来存放该 state,并将这一新组件置于高于共同所有者组件层级的位置。
    • 第五步:添加反向数据流
      实现的方式:
      例如:子组件输入数据,需要调用父组件提供的callback方法,将数据作为参数传给父组件的方法进行修改,父组件的state数据发生改变。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值