继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

React文档笔记

郎朗坤
关注TA
已关注
手记 378
粉丝 49
获赞 213

jsx

const element = <h1>Hello, world!</h1>;
这就是 JSX ,他是 JavaScrip 的一种扩展语法。我们推荐在 React 中使用这种语法来描述 UI 信息。JSX 可能会让你想起某种模板语言,但是它具有 JavaScrip 的全部能力。

JSX 表示对象

  • Babel 将JSX编译成 React.createElement() 调用。

  • 下面的两个例子是是完全相同的:

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

React 只更新必需要更新的部分

React DOM 会将元素及其子元素与之前版本逐一对比, 并只对有必要更新的 DOM 进行更新, 以达到 DOM 所需的状态。

组件(Components) 和 属性(Props)

愚人码头注:Props , 即属性(Property), 在代码中写作 props , 故可用 props 指代 properties .


  1. 函数式组件


  1. 类组件

警告:
组件名称总是以大写字母开始。
举例来说, <div /> 代表一个 DOM 标签,而 <Welcome /> 则代表一个组件,并且需要在作用域中有一个 Welcome 组件。

提取组件

  • 不要害怕把一个组件分为多个更小的组件。

  • 组件化思维

Props 是只读的

  • “纯函数”
    因为它们不会试图改变它们的输入,并且对于同样的输入,始终可以得到相同的结果。

  • 所有 React 组件都必须是纯函数,并禁止修改其自身 props 。


状态(State) 和 生命周期

example:

function Clock(props) {  return (    <div>
      <h1>Hello, world!</h1>
      <h2>It is {props.date.toLocaleTimeString()}.</h2>
    </div>
  );
}function tick() {
  ReactDOM.render(    <Clock date={new Date()} />,
    document.getElementById('root')
  );
}

setInterval(tick, 1000);
使用state改进

state 和 props 类似,但是它是私有的,并且由组件本身完全控制。

  • 把函数式组件转化为类组件:

  1. 创建一个继承自 React.Component 类的 ES6 class 同名类。

  2. 添加一个名为 render() 的空方法。

  3. 把原函数中的所有内容移至 render() 中。

  4. render() 方法中使用 this.props 替代 props

  5. 删除保留的空函数声明。

class Clock extends React.Component {
  render() {    return (      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}
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>
    );
  }
}

ReactDOM.render(  <Clock />,
  document.getElementById('root')
);
  • 在类中添加生命周期方法

class Clock extends React.Component {  constructor(props) {    super(props);    this.state = {date: new Date()};
  }

  componentDidMount() {    this.timerID = setInterval(      () => this.tick(),      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {    this.setState({      date: new Date()
    });
  }

  render() {    return (      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

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

-组件调用过程

  1. <Clock /> 被传入 ReactDOM.render()时, React 会调用 Clock组件的构造函数。 因为Clock要显示的是当前时间,所以它将使用包含当前时间的对象来初始化this.state。我们稍后会更新此状态。

2.然后 React调用了 Clock 组件的 render()方法。 React 从该方法返回内容中得到要显示在屏幕上的内容。然后,React然后更新DOM以匹配 Clock的渲染输出。

3.当Clock 输出被插入到DOM中时,React 调用 componentDidMount()生命周期钩子。在该方法中,Clock组件请求浏览器设置一个定时器来一次调用 tick()

4.浏览器会每隔一秒调用一次 tick()方法。在该方法中, Clock 组件通过 setState()方法并传递一个包含当前时间的对象来安排一个UI的更新。通过 setState(), React 得知了组件 state(状态)的变化, 随即再次调用render()方法,获取了当前应该显示的内容。 这次,render() 方法中的this.state.date的值已经发生了改变, 从而,其输出的内容也随之改变。React于是据此对DOM 进行更新。

5.如果通过其他操作将 Clock 组件从 DOM中移除了, React会调用 componentWillUnmount()生命周期钩子, 所以计时器也会被停止。

正确地使用 State(状态)


  1. 不要直接修改 state(状态)

// 错误this.state.comment = 'Hello';
// 正确this.setState({comment: 'Hello'});

  1. state(状态) 更新可能是异步的
    React 为了优化性能,有可能会将多个 setState() 调用合并为一次更新。
    因为 this.props 和 this.state 可能是异步更新的,你不能依赖他们的值计算下一个state(状态)。

例如, 以下代码可能导致 counter(计数器)更新失败:// 错误this.setState({
  counter: this.state.counter + this.props.increment,
});

要弥补这个问题,使用另一种 setState() 的形式,它接受一个函数而不是一个对象。这个函数将接收前一个状态作为第一个参数,应用更新时的 props 作为第二个参数:

// 正确this.setState((prevState, props) => ({  counter: prevState.counter + props.increment
}));

state(状态)更新会被合并

当你调用 setState(), React 将合并你提供的对象到当前的状态中。

 componentDidMount() {
    fetchPosts().then(response => {      this.setState({        posts: response.posts
      });
    });

    fetchComments().then(response => {      this.setState({        comments: response.comments
      });
    });
  }

合并是浅合并,所以 this.setState({comments}) 不会改变 this.state.posts 的值,但会完全替换this.state.comments 的值。

数据向下流动

如果把组件树想像为 props(属性) 的瀑布,所有组件的 state(状态) 就如同一个额外的水源汇入主流,且只能随着主流的方向向下流动。


处理事件

  • 取消点击默认行为

React 中你不能通过返回 false(愚人码头注:即 return false; 语句) 来阻止默认行为。必须明确调用 preventDefault

function ActionLink() {  function handleClick(e) {    // e 无兼容性问题
    e.preventDefault();    console.log('The link was clicked.');
  }  return (    <a href="#" onClick={handleClick}>
      Click me    </a>
  );
}

this的绑定及事件参数传递

<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button><button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

述两行代码是等价的,分别使用 arrow functionsFunction.prototype.bind


条件渲染

  • 有状态组件
    它将渲染<LoginButton /> 或者 <LogoutButton /> ,取决于当前状态。同时渲染 <Greeting />组件:

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 = null;    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')
);

使用逻辑 && 操作符的内联 if 用法

您可以 在JSX中嵌入任何表达式 ,方法是将其包裹在花括号中。这也包括 JavaScript 逻辑 && 运算符。

使用条件操作符的内联 If-Else

render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (    <div>
      {isLoggedIn ? (        <LogoutButton onClick={this.handleLogoutClick} />
      ) : (        <LoginButton onClick={this.handleLoginClick} />
      )}    </div>
  );
}

防止组件渲染

  • 在极少数情况下,您可能希望组件隐藏自身,即使它是由另一个组件渲染的。为此,返回 null 而不是其渲染输出。

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(prevState => ({      showWarning: !prevState.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')
);

列表(Lists) 和 键(Keys)

const numbers = [1, 2, 3, 4, 5];const listItems = numbers.map((number) =>
  <li>{number}</li>
);

example:

function NumberList(props) {  const numbers = props.numbers;  const listItems = numbers.map((number) =>
    <li>{number}</li>
  );  return (
    <ul>{listItems}</ul>
  );
}const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);
  • 当运行上述代码的时候,将会收到一个警告:a key should be provided for list items(应该为列表元素提供一个键)(愚人码头注 :CodeOpen 中没有报警告,是因为其示例中使用的是 min 版本的 React,换成非 min 版本的就可以看到)。

  • 解决丢失 key 的问题。

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')
);

键(Keys)

  • 键(Keys) 帮助 React 标识哪个项被修改、添加或者移除了。数组中的每一个元素都应该有一个唯一不变的键(Keys)来标识,

  • 挑选 key 最好的方式是使用一个在它的同辈元素中不重复的标识字符串。多数情况你可以使用数据中的 IDs 作为 keys:

const todoItems = todos.map((todo) =>  <li key={todo.id}>
    {todo.text}  </li>);
  • 当要渲染的列表项中没有稳定的 IDs 时,你可以使用数据项的索引值作为 key 的最后选择:

const todoItems = todos.map((todo, index) =>  // Only do this if items have no stable IDs
  <li key={index}>
    {todo.text}
  </li>
);

如果列表项可能被重新排序时,我们不建议使用索引作为 keys,因为这导致一定的性能问题,会很慢。

使用 keys 提取组件

  • 错误的 key 用法

  • 一个好的经验准则是元素中调用 map() 需要 keys 。

function ListItem(props) {  // 正确!这里不需要指定 key :
  return <li>{props.value}</li>;
}function NumberList(props) {  const numbers = props.numbers;  const listItems = numbers.map((number) =>
    // 正确!key 应该在这里被指定
    <ListItem key={number.toString()}
              value={number} />

  );  return (    <ul>
      {listItems}    </ul>
  );
}const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

keys 在同辈元素中必须是唯一的

  • 在数组中使用的 keys 必须在它们的同辈之间唯一。然而它们并不需要全局唯一。

  • 键是React的一个内部映射,但其不会传递给组件的内部。如果你需要在组件中使用相同的值,可以明确使用一个不同名字的 prop 传入。

const content = posts.map((post) =>  <Post
    key={post.id}
    id={post.id}
    title={post.title} />);

上面的例子中, Post 组件可以读取 props.id,但是不能读取 props.key 。

在 JSX 中嵌入 map()

  • 在上面的例子中,我们单独声明了一个 listItems 变量,并在 JSX 中引用了该变量:

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

  );  return (    <ul>
      {listItems}    </ul>
  );
}// 或者jsx使用大括号function NumberList(props) {  const numbers = props.numbers;  return (    <ul>
      {numbers.map((number) =>        <ListItem key={number.toString()}
                  value={number} />

      )}    </ul>
  );
}

表单(Forms)

  • select 标签

在 HTML 中,<select> 创建了一个下拉列表。例如,这段 HTML 创建一个下拉的口味(flavors)列表:

<select>
  <option value="grapefruit">Grapefruit</option>
  <option value="lime">Lime</option>
  <option selected value="coconut">Coconut</option>
  <option value="mango">Mango</option></select>

注意,Coconut 选项是初始化选中的,因为它的 selected 属性。React 中,并不使用这个 selected 属性,而是在根 select 标签中使用了一个 value 属性。这使得受控组件使用更方便,因为你只需要更新一处即可。例如:

class FlavorForm extends React.Component {  constructor(props) {    super(props);    this.state = {value: 'coconut'};    this.handleChange = this.handleChange.bind(this);    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('Your favorite flavor is: ' + this.state.value);
    event.preventDefault();
  }

  render() {    return (      <form onSubmit={this.handleSubmit}>
        <label>
          Pick your favorite La Croix flavor:          <select value={this.state.value} onChange={this.handleChange}>
            <option value="grapefruit">Grapefruit</option>
            <option value="lime">Lime</option>
            <option value="coconut">Coconut</option>
            <option value="mango">Mango</option>
          </select>
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}
  • 总的来说,这使 <input type="text">, <textarea> 和 <select> 都以类似的方式工作 —— 它们都接受一个 value 属性可以用来实现一个受控组件。

file input 标签

<input type="file" />
在 React 中,一个 <input type =“file”/> 和一个普通的 <input /> 类似,但有一个重要的区别:它是只读的(read-only)。 (您不能以编程方式设置值。)相反,你应该使用 File API 与文件进行交互。

class FileInput extends React.Component {  constructor(props) {    super(props);    this.handleSubmit = this.handleSubmit.bind(      this
    );
  }
  handleSubmit(event) {
    event.preventDefault();
    alert(      `Selected file - ${        this.fileInput.files[0].name
      }`
    );
  }

  render() {    return (      <form
        onSubmit={this.handleSubmit}>
        <label>
          Upload file:          <input
            type="file"
            ref={input => {
              this.fileInput = input;
            }}
          />        </label>
        <br />
        <button type="submit">
          Submit        </button>
      </form>
    );
  }
}

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

处理多个输入元素

  • 当您需要处理多个受控的 input 元素时,您可以为每个元素添加一个 name 属性,并且让处理函数根据 event.target.name 的值来选择要做什么。
    forExample:

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.type === 'checkbox' ? target.checked : target.value;    const name = target.name;    this.setState({
      [name]: value
    });
  }

  render() {    return (
      <form>
        <label>
          Is going:
          <input
            name="isGoing"
            type="checkbox"
            checked={this.state.isGoing}
            onChange={this.handleInputChange} />
        </label>
        <br />
        <label>
          Number of guests:
          <input
            name="numberOfGuests"
            type="number"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange} />
        </label>
      </form>
    );
  }
}

状态提升(Lifting State Up)

  • 两个组件都将其值保持在本地状态中,但是我们希望弄些状态是共用的,在React中,共享 state(状态) 是通过将其移动到需要它的组件的最接近的共同祖先组件来实现的。

  • 我们知道 props(属性) 是只读的。在 React 中,通常通过使组件“受控”的方式来解决。就像 DOM <input>一样接受一个 value 和一个 onChange prop(属性)
    forExample:

handleChange(e) {    // 之前是: this.setState({temperature: e.target.value});
    this.props.onTemperatureChange(e.target.value);

onTemperatureChange prop(属性) 和 temperature prop(属性) 一起由父级的 Calculator 组件提供。它将通过修改自己的本地 state(状态) 来处理变更,从而通过新值重新渲染两个输入。我们将很快看到新的 Calculator 实现。

经验总结

  • 在一个 React 应用中,对于任何可变的数据都应该循序“单一数据源”原则。通常情况下,state 首先被添加到需要它进行渲染的组件。然后,如果其它的组件也需要它,你可以提升状态到它们最近的祖先组件。你应该依赖 从上到下的数据流向 ,而不是试图在不同的组件中同步状态。


组合 VS 继承

包含

  • 我们建议这种组件使用特别的 children prop 来直接传递 子元素到他们的输出中(好比vue的slot):
    forExample:

function FancyBorder(props) {  return (    <div className={'FancyBorder FancyBorder-' + props.color}>
      {props.children}    </div>
  );
}

这允许其他组件通过嵌套 JSX 传递任意子组件给他们:

function WelcomeDialog() {  return (    <FancyBorder color="blue">
        //***************      <h1 className="Dialog-title">
        Welcome      </h1>
      <p className="Dialog-message">
        Thank you for visiting our spacecraft!      </p>
       //***************    </FancyBorder>
  );
}
  • 然而这并不常见,有时候,在一个组件中你可能需要多个 “占位符” 。在这种情况下,你可以使用自定义的 prop(属性),而不是使用 children :

function SplitPane(props) {  return (    <div className="SplitPane">
      <div className="SplitPane-left">
        {props.left}      </div>
      <div className="SplitPane-right">
        {props.right}      </div>
    </div>
  );
}function App() {  return (    <SplitPane
      left={
        <Contacts />
      }
      right={        <Chat />
      } />
  );
}

React 的编程思想

  • 该如何拆分组件呢?
    其实只需要像拆分一个新方法或新对象一样的方式即可。一个常用的技巧是单一职责原则,即一个组件理想情况下只处理一件事。如果一个组件持续膨胀,就应该将其拆分为多个更小的组件中。

  • 不要重复你自己 (DRY,don’t repeat yourself)

  • 让我们仔细分析每一个数据,弄清楚哪一个是 state(状态) 。请简单地提出有关每个数据的 3 个问题:

  1. 是否通过 props(属性) 从父级传入? 如果是这样,它可能不是 state(状态) 。

  2. 是否永远不会发生变化? 如果是这样,它可能不是 state(状态)。

  3. 是否可以由组件中其他的 state(状态) 或 props(属性) 计算得出?如果是这样,则它不是 state(状态)。

React 单向数据流在层级中自上而下进行。这样有可能不能立即判断出状态属于哪个组件。这常常是新手最难理解的一部分,试着按下面的步骤分析操作:
  • 确定每个基于这个 state(状态) 渲染的组件。

  • 找出公共父级组件(一个单独的组件,在组件层级中位于所有需要这个 state(状态) 的组件的上面。愚人码头注:父级组件)。

  • 公共父级组件 或者 另一个更高级组件拥有这个 state(状态) 。

  • 如果找不出一个拥有该 state(状态) 的合适组件,可以创建一个简单的新组件来保留这个 state(状态) ,并将其添加到公共父级组件的上层即可。



作者:琪先生_zZ
链接:https://www.jianshu.com/p/e40df01926fc


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP