我是靠谱客的博主 炙热毛巾,最近开发中收集的这篇文章主要介绍React 官网核心概念核心概念,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

核心概念

2. JSX

JS 扩展

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

视图与逻辑之间的结合


Babel 会把 JSX 转译成一个名为 React.createElement() 函数调用

以下两种示例代码完全等效:

const element = (
  <h1 className="greeting">
    Hello, world!
  </h1>
);

// 预先执行一些检查,以帮助你编写无错代码
const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);

// 实际上它创建了一个这样的对象
// 注意:这是简化过的结构
const element = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Hello, world!'
  }
};

这些对象被称为 “React 元素”

它们描述了你希望在屏幕上看到的内容 React 通过读取这些对象,然后使用它们来构建 DOM 以及保持随时更新

3. 元素渲染进根节点

元素:构成 React 应用的最小砖块 ,描述了你在屏幕上想看到的内容

将一个元素渲染为 DOM

假设你的 HTML 文件某处有一个 <div>

<div id="root"></div>

想要将一个 React 元素渲染到根 DOM 节点中,只需把它们一起传入 ReactDOM.render()

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

4. 组件 & Props

组件,类似函数, 接受 “props” = > React 元素 (展页面示内容)

定义组件

1️⃣ 编写 JavaScript 函数

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

2️⃣ 使用 ES6 的 class 来定义组件

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

组件名称必须以大写字母开头

不能更改props

不能修改自身的 props。来看下这个 sum 函数:

function sum(a, b) {
	// 纯函数:不会尝试更改入参
  return a + b;
}
function withdraw(account, amount) {
	// 不是纯函数:更改了自己的入参
  account.total -= amount;
}

所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。

5. State & 生命周期

state : 私有的,完全受控于当前组件。 是组件自动更新

将函数组件转换成 class 组件

缺点:需要将 ReactDOM.render 放进一个函数,然后使用 setInterval 调用,并且 Clock需要传入参数

改进:直接调用 ReactDOM.render 并且 Clock 不要参数

方法:将函数组件变为 class 组件,state 类似与 props

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);
  1. 创建一个同名的 ES6 class,并且继承于 React.Component。
  2. 添加一个空的 render() 方法。
  3. 将函数体移动到 render() 方法之中。
  4. render() 方法中使用 this.props 替换 props
// 1
class Clock extends React.Component {
  // 2
  render() {
    // 3
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
      </div>
    );
    // 4 this.props		
  }
}

向 class 组件中添加局部的 state

我们通过以下三步将 date 从 props 移动到 state 中:

  1. render() 方法中的 this.props.date 替换成 this.state.date
  2. 添加一个 class 构造函数,然后在该函数中为 this.state 赋初值
  3. 移除 <Clock /> 元素中的 date 属性
class Clock extends React.Component {
    constructor(props) {
        // 将 props 传递到父类的构造函数中
        // Class 组件应该始终使用 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 中

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

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

  // 当 Clock 组件第一次被渲染到 DOM 中的时候,就为其设置一个计时器。这在 React 中被称为“挂载(mount)”
  componentDidMount() {
    // 把计时器的 ID 保存在 this.timerID 之中
    // 尽管 this.props 和 this.state 是 React 本身设置的,且都拥有特殊的含义,但是其实你可以向 class 中随意添加不参与数据流(比如计时器 ID)的额外字段
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  // 当 DOM 中 Clock 组件被删除的时候,应该清除计时器。这在 React 中被称为“卸载(unmount)”
  componentWillUnmount() {
    // 清除计时器
    clearInterval(this.timerID);
  }

  // Clock 组件每秒都会调用它
  tick() {
    // 使用 this.setState() 来时刻更新组件 state
    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')
);

正确地使用 State

关于 setState() 你应该了解三件事:

不要直接修改 State

例如,此代码不会重新渲染组件:

// Wrong
this.state.comment = 'Hello';

而是应该使用 setState():

// Correct
this.setState({comment: 'Hello'});

构造函数是唯一可以给 this.state 赋值的地方:

State 的更新可能是异步的

出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。

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

例如,此代码可能会无法更新计数器:

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

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

// Correct
this.setState((state, props) => ({
  counter: state.counter + props.increment
}));

上面使用了箭头函数,不过使用普通的函数也同样可以:

// Correct
this.setState(function(state, props) {
  return {
    counter: state.counter + props.increment
  };
});

State 的更新会被合并

当你调用 setState() 的时候,React 会把你提供的对象合并到当前的 state。

例如,你的 state 包含几个独立的变量:

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

然后你可以分别调用 setState() 来单独地更新它们:

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

6. 事件处理

在 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>
    );
}

在这里,e 是一个合成事件

使用 React 时,你一般不需要使用 addEventListener 为已创建的 DOM 元素添加监听器

事实上,你只需要在该元素初始渲染的时候添加监听器即可

class Toggle extends React.Component {
    constructor(props) {
        super(props);
        this.state = {isToggleOn: true};
        // 为了在回调中使用 `this`,这个绑定是必不可少的		
        // 在 JavaScript 中,class 的方法默认不会绑定 this。
        this.handleClick = this.handleClick.bind(this);
    }

    // 当你使用 ES6 class 语法定义一个组件的时候,通常的做法是将事件处理函数声明为 class 中的方法
    handleClick() {
        this.setState(state => ({
            isToggleOn: !state.isToggleOn
        }));
    }

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

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

不使用 bind 的另一种方法,可以在回调中使用箭头函数

class LoggingButton extends React.Component {
  handleClick() {
    console.log('this is:', this);
  }

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

向事件处理程序传递参数

在循环中,通常我们会为事件处理函数传递额外的参数。例如,若 id 是你要删除那一行的 ID,以下两种方式都可以向事件处理函数传递参数:

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

7. 条件渲染

登陆退出改变文字

import React from 'react'
import ReactDOM from 'react-dom'

// 两种行为
function UserGreeting(props) {
  return <h1>Welcome back!</h1>;
}

function GuestGreeting(props) {
  return <h1>Please sign up.</h1>;
}

// 呈现两种行为的方式
function Greeting(props) {
  const isLoggedIn = props.isLoggedIn;
  if (isLoggedIn) {
    return <UserGreeting />;
  }
  return <GuestGreeting />;
}

// 两个 button 样式
function LoginButton(props) {
  return (
    <button onClick={props.onClick}>
      Login
    </button>
  );
}

function LogoutButton(props) {
  return (
    <button onClick={props.onClick}>
      Logout
    </button>
  );
}


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;
		// isLoggedIn 决定 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')
);

点击隐藏 Warning

import React from 'react'
import ReactDOM from 'react-dom'

// 传入的状态决定是否展示内容到界面上
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')
);

8. 列表 & Key

import React from 'react'
import ReactDOM from 'react-dom'

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

ReactDOM.render(
  <ul>{listItems}</ul>, document.getElementById('root')
);
import React from 'react'
import ReactDOM from 'react-dom'

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 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。

元素的 key 只有放在就近的数组上下文中才有意义。

比方说,如果你提取出一个 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')
);

10. 状态提升

计算水在给定温度下是否会沸腾

function BoilingVerdict(props) {
    if (props.celsius >= 100) {
        return <p>The water would boil.</p>;
    }
    return <p>The water would not boil.</p>;
}

class Calculator extends React.Component {
    constructor(props) {
        super(props);
        this.handleChange = this.handleChange.bind(this);
        this.state = { temperature: '' };
    }

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

    render() {
        const temperature = this.state.temperature;
        return (
            // fieldset:对表单进行分组,一个表单可以有多个fieldset
            // legend:说明每组的内容描述
            <fieldset>
                <legend>Enter temperature in Celsius:</legend>
                {/* value:  显示的值为 temperature */}
                <input
                    value={temperature}
                    onChange={this.handleChange} />

                <BoilingVerdict
                    celsius={parseFloat(temperature)} />

            </fieldset>
        );
    }
}

两个输入框内的数值彼此能够同步

状态提升:将多个组件中需要共享的 state 向上移动到它们的最近共同父组件中,便可实现共享 state

import React from 'react'
import ReactDOM from 'react-dom'

function BoilingVerdict(props) {
    if (props.celsius >= 100) {
        return <p>The water would boil.</p>;
    }
    return <p>The water would not boil.</p>;
}

const scaleNames = {
    c: 'Celsius',
    f: 'Fahrenheit'
};

class TemperatureInput extends React.Component {
    constructor(props) {
        super(props);
        this.handleChange = this.handleChange.bind(this);
        this.state = { temperature: '' };
    }

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

    render() {
        // Before: const temperature = this.state.temperature;
        
        // 自定义组件中的 temperature 和 onTemperatureChange 这两个 prop 的命名没有任何特殊含义。
        // 我们可以给它们取其它任意的名字,例如,把它们命名为 value 和 onChange 就是一种习惯。
        const temperature = this.props.temperature;
        const scale = this.props.scale;
        return (
            <fieldset>
                <legend>Enter temperature in {scaleNames[scale]}:</legend>
                <input value={temperature}
                    // handlechange 在每次按键时都会执行并更新 React 的 props
                    onChange={this.handleChange} />
            </fieldset>
        );
    }
}


function toCelsius(fahrenheit) {
    return (fahrenheit - 32) * 5 / 9;
}

function toFahrenheit(celsius) {
    return (celsius * 9 / 5) + 32;
}

function tryConvert(temperature, convert) {
    const input = parseFloat(temperature);
    if (Number.isNaN(input)) {
        return '';
    }
    const output = convert(input);
    // Math.round()函数: 返回一个数字四舍五入后最接近的整数
    const rounded = Math.round(output * 1000) / 1000;
    return rounded.toString();
}

class Calculator extends React.Component {
    constructor(props) {
        super(props);
        this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
        this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
        this.state = {temperature: '', scale: 'c'};
    }

    handleCelsiusChange(temperature) {
        // 调用 this.setState() 进而请求 React 重新渲染自己本身。
        this.setState({scale: 'c', temperature});
    }

    handleFahrenheitChange(temperature) {
        this.setState({scale: 'f', temperature});
    }

    render() {
        const scale = this.state.scale;
        const temperature = this.state.temperature;
        const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
        const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;

        return (
            <div>
                
                <TemperatureInput
                    scale="c"
                    // 父组件 Calculator 提供参数
                    temperature={celsius}
                    // 无论哪个输入框被编辑都会调用 Calculator 组件中对应的方法。	然后会调用 this.setState()
                    onTemperatureChange={this.handleCelsiusChange} />
                <TemperatureInput
                    scale="f"
                    temperature={fahrenheit}
                    onTemperatureChange={this.handleFahrenheitChange} />
                <BoilingVerdict
                    celsius={parseFloat(celsius)} />
                
            </div>
        );
    }
}


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

11. 组合 vs 继承

包含关系

使用一个特殊的 children prop 来将他们的子组件传递到渲染结果中

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

function WelcomeDialog() {
  return (
    <FancyBorder color="blue">
      <h1 className="Dialog-title">
        Welcome
      </h1>
      <p className="Dialog-message">
        Thank you for visiting our spacecraft!
      </p>
    </FancyBorder>
  );
}

在一个组件中预留出几个“洞”。这种情况下,我们可以不使用 children,而是自行约定:将所需内容传入 props,并使用相应的 prop

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

// <Contacts /> 和 <Chat /> 之类的 React 元素本质就是对象(object),所以你可以把它们当作 props,像其他数据一样传递
function Contacts(){
    return(
        <div>Contacts</div>
    )
}
function Chat(){
    return(
        <div>Chat</div>
    )
}

function App() {
    return (
        <SplitPane
            left={
                <Contacts />
            }
            right={
                <Chat />
            } />
    );
}

特例关系

有些时候,我们会把一些组件看作是其他组件的特殊实例,比如 WelcomeDialog 可以说是 Dialog 的特殊实例。

在 React 中,我们也可以通过组合来实现这一点。“特殊”组件可以通过 props 定制并渲染“一般”组件:

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

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!" />
  );
}

最后

以上就是炙热毛巾为你收集整理的React 官网核心概念核心概念的全部内容,希望文章能够帮你解决React 官网核心概念核心概念所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(48)

评论列表共有 0 条评论

立即
投稿
返回
顶部