美文网首页
React基础学习总结

React基础学习总结

作者: Scarlett_L | 来源:发表于2018-06-08 20:30 被阅读16次

一、JSX语法

1、花括号 { } 把任意的 [JavaScript 表达式]嵌入到 JSX 中

function formatName(user) {
  return user.firstName + ' ' + user.lastName;
}

const user = {
  firstName: 'Harper',
  lastName: 'Perez'
};

const element = (
  <h1>
    Hello, {formatName(user)}!
  </h1>
);

ReactDOM.render(
  element,
  document.getElementById('root')
);

2、JSX可作表达式

可以在 if 语句或者是 for 循环中使用 JSX,用它给变量赋值,当做参数接收,或者作为函数的返回值

function getGreeting(user) {
  if (user) {
    return <h1>Hello, {formatName(user)}!</h1>;
  }
  return <h1>Hello, Stranger.</h1>;
}

3、用 JSX 指定属性值

(1)用双引号 “” 来指定字符串字面量作为属性值
(2)用花括号 {} 嵌入一个 JavaScript 表达式作为属性值
在属性中嵌入 JavaScript 表达式时,不要使用引号来包裹大括号。否则,JSX 将该属性视为字符串字面量而不是表达式。对于字符串值你应该使用引号,对于表达式你应该使用大括号,但两者不能同时用于同一属性。

const element = <div tabIndex="0"></div>;

const element = <img src={user.avatarUrl}></img>;
4、用 JSX 指定子元素

(1)如果是空标签,您应该像 XML 一样,使用 />立即闭合它
(2)JSX 标签可能包含子元素

const element = <img src={user.avatarUrl} />;

const element = (
  <div>
    <h1>Hello!</h1>
    <h2>Good to see you here.</h2>
  </div>
);

5、JSX 表示对象

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

//两种形式等价
const element = (
  <h1 className="greeting">
    Hello, world!
  </h1>
);

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

二、元素渲染

元素(Elements)是 React 应用中最小的构建部件(或者说构建块,building blocks),不同于组件。

const element = <h1>Hello, world</h1>;

1、渲染一个元素到 DOM

要渲染一个 React 元素到一个 root DOM 节点,把它们传递给 ReactDOM.render() 方法:

const element = <h1>Hello, world</h1>;
ReactDOM.render(
  element,
  document.getElementById('root')
);

2 、更新已渲染的元素

React 元素是不可突变的. 一旦你创建了一个元素, 就不能再修改其子元素或任何属性。一个元素就像电影里的一帧: 它表示在某一特定时间点的 UI 。更新 UI 的唯一方法是创建一个新的元素, 并将其传入 ReactDOM.render()方法.

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

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

三、组件(Components) 和 属性(Props)

组件使你可以将 UI 划分为一个一个独立,可复用的小部件,并可以对每个部件进行单独的设计。

1、函数式组件和类组件

//函数式组件
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

//类组件
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

2、渲染组件

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>。

注意:

  • 组件名称总是以大写字母开始。
    <div /> 代表一个 DOM 标签,而 <Welcome /> 则代表一个组件,并且需要在作用域中有一个 Welcome 组件。
  • 组件必须返回一个单独的根元素。这就是为什么我们添加一个 <div> 来包含所有 <Welcome /> 元素的原因。
function App() {
  return (
    <div>
      <Welcome name="Sara" />
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </div>
  );
}

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

3、提取组件

提取组件可能看起来是一个繁琐的工作,但是在大型的 Apps 中可以回报给我们的是大量的可复用组件。一个好的经验准则是如果你 UI 的一部分需要用多次 (Button,Panel,Avatar),或者本身足够复杂(App,FeedStory,Comment),最好的做法是使其成为可复用组件。

function Comment(props) {
  return (
    <div className="Comment">
      <UserInfo user={props.author} />
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

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

四、状态(State) 和 生命周期

1、把函数式组件转化为类组件

遵从以下5步, 把一个类似 Clock这样的函数式组件转化为类组件:

  1. 创建一个继承自 React.Component类的 ES6 class 同名类。
  2. 添加一个名为 render() 的空方法。
  3. 把原函数中的所有内容移至 render() 中。
  4. 在 render() 方法中使用 this.props 替代 props。
  5. 删除保留的空函数声明。

Clock 现在被定为类组件,而不是函数式组件。
类允许我们在其中添加本地状态(state)和生命周期钩子。

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

2、在类组件中添加本地状态(state)

我们现在通过以下3步, 把date从属性(props) 改为 状态(state):

class Clock extends React.Component {

2、添加一个 类构造函数(class constructor)初始化 this
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }


1、替换 render() 方法中的 this.props.date 为 this.state.date
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}


3、移除 <Clock /> 元素中的 date 属性
ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

3、在类中添加生命周期方法

在一个具有许多组件的应用程序中,在组件被销毁时释放所占用的资源是非常重要的。

当 Clock 第一次渲染到DOM时,我们要设置一个定时器。 这在 React 中称为 “挂载(mounting)” 。
当 Clock 产生的 DOM 被销毁时,我们也想 清除该计时器 。 这在 React 中称为 “卸载(unmounting)” 。

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


1、this.props 由 React 本身设定, 而 this.state 具有特殊的含义,但如果需要存储一些不用于视觉输出的内容,则可以手动向类中添加额外的字段。
如果在 render() 方法中没有被引用, 它不应该出现在 state 中。

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

2、我们在componentWillUnmount()生命周期钩子中取消这个计时器
  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() 生命周期钩子, 所以计时器也会被停止。

4、正确地使用 State(状态)

setState() 有三件事是你应该知道的:

(1)用 setState() 设置状态:
this.setState({comment: 'Hello'});
(2)state(状态) 更新可能是异步的:

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

例如, 以下代码可能导致 counter(计数器)更新失败:

// 错误
this.setState({
  counter: this.state.counter + this.props.increment,
});

要解决这个问题,应该使用第 2 种 setState() 的格式,它接收一个函数,而不是一个对象。该函数接收前一个状态值作为第 1 个参数, 并将更新后的值作为第 2个参数:

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

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

// 正确
this.setState(function(prevState, props) {
  return {
    counter: prevState.counter + props.increment
  };
});
(3)state(状态)更新会被合并

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

例如,你的状态可能包含几个独立的变量:

constructor(props) {
    super(props);
    this.state = {
      posts: [],
      comments: []
    };
  }

然后通过调用独立的 setState() 调用分别更新它们:
合并是浅合并,所以 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
      });
    });
  }

5、数据向下流动

无论作为父组件还是子组件,它都无法获悉一个组件是否有状态,同时也不需要关心另一个组件是定义为函数组件还是类组件。

这就是 state(状态) 经常被称为 本地状态 或 封装状态的原因。 它不能被拥有并设置它的组件 以外的任何组件访问。

一个组件可以选择将 state(状态) 向下传递,作为其子组件的 props(属性):

<h2>It is {this.state.date.toLocaleTimeString()}.</h2>

同样适用于用户定义组件:

<FormattedDate date={this.state.date} />

FormattedDate 组件通过 props(属性) 接收了 date 的值,但它仍然不能获知该值是来自于 Clock的 state(状态) ,还是 Clock 的 props(属性),或者是直接手动创建的:

function FormattedDate(props) {
  return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}

这通常称为一个“从上到下”,或者“单向”的数据流。任何 state(状态) 始终由某个特定组件所有,并且从该 state(状态) 导出的任何数据 或 UI 只能影响树中 “下方” 的组件

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

五、处理事件

1、与普通HTML区别

(1) React 事件使用驼峰命名,而不是全部小写。
(2)通过 JSX , 你传递一个函数作为事件处理程序,而不是一个字符串。
<button onClick={activateLasers}>
  Activate Lasers
</button>
(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>
  );
}
(4)当使用一个 ES6 类定义一个组件时,通常的一个事件处理程序是类上的一个方法。

Toggle 组件渲染一个按钮,让用户在 “ON” 和 “OFF” 状态之间切换:

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // 这个绑定是必要的,使`this`在回调中起作用
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

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

2、将参数传递给事件处理程序

在循环内部,通常需要将一个额外的参数传递给事件处理程序。 例如,如果 id 是一个内联 ID,则以下任一方式都可以正常工作:

等价的,分别使用 arrow functions 和 Function.prototype.bind
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

上面两个例子中,参数 e 作为 React 事件对象将会被作为第二个参数进行传递。通过箭头函数的方式,事件对象必须显式的进行传递,但是通过 bind 的方式,事件对象以及更多的参数将会被隐式的进行传递。

六、条件渲染

1、在函数式组件中用if判断

我们需要创建一个 Greeting 组件, 用来根据用户是否登录, 判断并显示上述两个组件之一:

function Greeting(props) {
  const isLoggedIn = props.isLoggedIn;
  if (isLoggedIn) {
    return <UserGreeting />;
  }
  return <GuestGreeting />;
}

ReactDOM.render(
  // 修改为 isLoggedIn={true} 试试:
  <Greeting isLoggedIn={false} />,
  document.getElementById('root')
);

2、元素变量

在接下来的例子中,我们将会创建一个有状态组件,叫做 LoginControl 。
它将渲染 <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')
);

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

(1)可以在JSX中嵌入任何表达式,方法是将其包裹在花括号中
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')
);
(2)条件操作符 condition ? true : false
render() {
  const isLoggedIn = this.state.isLoggedIn;
  return (
    <div>
      The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
    </div>
  );
}


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

七、列表(Lists) 和 键(Keys)

1、基本列表组件

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

2、键(Keys)

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

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

const todoItems = todos.map((todo) =>
  <li key={todo.id}>
    {todo.text}
  </li>
);
(1)使用 keys 提取组件

keys 只在数组的上下文中存在意义。

例如,如果你提取一个 ListItem 组件,应该把 key 放置在数组处理的 <ListItem /> 元素中,不能放在 ListItem 组件自身中的 <li> 根元素上。

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')
);
(2)keys 在同辈元素中必须是唯一的

在数组中使用的 keys 必须在它们的同辈之间唯一。然而它们并不需要全局唯一。我们可以在操作两个不同数组的时候使用相同的 keys

(3)键是React的一个内部映射,但其不会传递给组件的内部。

如果你需要在组件中使用相同的值,可以明确使用一个不同名字的 prop 传入。

相关文章

  • react学习(2019/7/30-8/7)

    今日总结 课上提到的内容 1. 讲了react要学习的内容提纲 react 基础,JSX语法,插件 react-r...

  • React 基础学习总结

    React 基础学习总结 1、创建虚拟DOM对象的两种方式 React.createElement(type, p...

  • React基础学习总结

    一、JSX语法 1、花括号 { } 把任意的 [JavaScript 表达式]嵌入到 JSX 中 2、JSX可作表...

  • React Native 学习总结(一)

    本章主要总结RN的基础概况 在学习React Native 起初,总会在官网看到类似于React、JSX、ES6等...

  • React面试题总结

    React面试题总结内容主要包括基础知识,React 组件,React Redux,React 路由。 基本知识 ...

  • React简单入门

    前几天学习了React框架的一些基础知识,这里就做个总结吧: 开发环境 在学习React的时候第一步就难住了,那就...

  • React 基础学习笔记

    React 基础学习笔记 黑马程序员视频:传送门 1. React 基础 1.1 介绍react React起源于...

  • 【React】1.0 React入门

    1.0 开始学习React——React全家桶 React基础 React-Router路由库 PubSub消息管...

  • React学习 & 与Vue的比较总结

    React文档学习 学习资源:react中文文档 前言 有过Vue与Vuex的学习基础;在重新学习React的时候...

  • React Native学习提纲(简)

    React Native学习提纲 一、 React.js入门基础 1.基础HTML/CSS与基础开发工具使用 ht...

网友评论

      本文标题:React基础学习总结

      本文链接:https://www.haomeiwen.com/subject/jzsnsftx.html