React 基础语法

一、Hello World

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>React demo</title>
    <script src="../build/react.js"></script>
    <script src="../build/react-dom.js"></script>
    <script src="../build/browser.min.js"></script>
</head>
<body>

<div id="root"></div>
<script type="text/babel">
    function  Hello() {
        return <h1>Hello world.</h1>;
    }

    ReactDOM.render(
        <Hello/>,
        document.getElementById('root')
    )
</script>
</body>
</html>

二、JSX 简介

JSX 是一种 JavaScript 的语法扩展。推荐在 React 中使用 JSX 来描述用户界面,JSX 用来声明 React 当中的元素。

2.1 在 JSX 中使用表达式

在 JSX 当中的表达式要包含在大括号里。

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

推荐在 JSX 代码的外面括上小括号,这样可以防止分号自动插入的 BUG。

JSX 本身其实也是一种表达式

在编译之后,JSX 其实会被转化为普通的 JavaScript 对象。

JSX 属性

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

使用了大括号包裹的 JavaScript 表达式时就不要再到外面套引号了。 JSX 会将引号当中的内容识别为字符串而不是表达式。

JSX 嵌套

如果 JSX 标签是闭合式的,那么你需要在结尾处用/>,就好像 XML/HTML 一样:

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

JSX 标签同样可以相互嵌套:

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

警告:
因为 JSX 的特性更接近 JavaScript 而不是 HTML , 所以 React DOM 使用 camelCase 小驼峰命名 来定义属性的名称,而不是使用 HTML 的属性名称。
例如,class 变成了 className,而 tabindex 则对应着 tabIndex.

JSX 代表 Objects

Babel 转译器会把 JSX 转换成一个名为 React.createElement() 的方法调用。

下面两种代码的作用是完全相同的:

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

React.createElement() 这个方法首先会进行一些避免 bug 的检查,之后返回一个类似下面例子的对象:

// 注意: 以下示例是简化过的(不代表在 React 源码中是这样)
const element = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Hello, world'
  }
};

这样的对象被称为“React 元素”。

三、元素渲染

元素是构成 React 应用的最小单位。

3.1 将元素渲染到 DOM 中

首先定义一个 id 为 root 的 div:

<div id='root'></div>

在此 div 中的所有内容都将由 React DOM 来管理,所以我们将其称之为“根” DOM 节点。

我们用 React 开发应用时一般只会定义一个根节点。

要将 React 元素渲染到根 DOM 节点中,我们通过把它们都传递给 ReactDOM.render() 的方法来将其渲染到页面上:

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

四、组件 & Props

组件可以将 UI 切分为一些独立的、可复用的部件,这样你就只需专注于构建每一个单独的部件。

组件从概念上看就像是函数,它可以接收任意的输入值(称之为“props”),并返回一个需要在页面上展示的 React 元素。

4.1 函数定义/类定义组件

定义一个组件最简单的方式是使用 JavaScript 函数:

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

也可以使用 ES6 class 来定义一个组件:

拓展:
ES6 是什么?
ES6:全称 ECMScript 6.0。是 JavaScript 语言的下一代标准,已经在 2015 年 6 月正式发布。

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

4.2 组件渲染

第一步,定义一个 Welcome 组件:

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

第二步,使用该组件创建 React 元素:

const element = <Welcome name='kevin'/>;

第三步,使用 ReactDOM.render() 方法将 React 元素渲染到页面上:

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

4.3 组合组件

组件可以在它的输出中引用其他组件:

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

function App() {
    return (
        <div>
            <Welcome name='kevin'/>
            <Welcome name='tom'/>
            <Welcome name='bob'/>
        </div>
    );
}

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

警告:
组件的返回值只能有一个根元素。

4.4 props 的只读性

props 是不可变的

五、State & 生命周期

状态与属性十分相似,但是状态是私有的,完全受控于当前组件。

使用类定义组件,就可以使用局部状态、生命周期钩子等特性。

5.1 为一个类添加局部状态

第一步:使用 ES6 class 定义一个组件

class Clock extents React.component {
    render() {
        return (
            <div>
                <h1>Hello,world!</h1>
                <h2>It is {this.state.date.toLocaleTimeString}</h2>
            </div>
        )
    }
}

第二步:添加一个类构造函数来初始化状态 this.state

class Clock extents 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() 方法渲染组件到页面

class Clock extents 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')
)

5.2 将生命周期方法添加到类中

在 5.1 中,我们只是添加了一个状态,那么,如何更新状态呢?答案是:使用生命周期方法。

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

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

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

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

当 Clock 的输出插入到 DOM 中时,React 会调用 componentDidMount() 生命周期方法。上面代码示例中,我们在该方法中设置了一个定时器,每个 1 秒钟调用一次 tick() 方法。

在 tick() 方法中,Clock 组件通过使用包含当前时间的对象调用 setState() 来更新状态。当 React 发现状态已经更新时,会再次调用 ReactDOM.render() 方法更新 DOM。

当 Clock 组件被从 DOM 中移除,React 会调用 componentWillUnmount() 生命周期方法。

5.3 正确地使用状态

  • 不要直接更新状态

构造函数是唯一能够初始化 this.state 的地方。其他地方更薪状态只能使用 this.setState();

  • 状态更新可能是异步的

不太懂 TODO

  • 状态更新合并

不太懂 TODO

5.4 数据自顶向下流动

父组件或子组件都不能知道某个组件是有状态还是无状态,并且它们不应该关心某组件是被定义为一个函数还是一个类。

所以状态通常被成为局部或封装。除了拥有并设置它的组件外,其他组件不可访问。

当组件可以选择将其状态作为属性传递给其子组件。

这通常被称为 自顶向下单向 数据流。任何状态始终由某些特定组件所有,并且该状态导出的任何数据或 UI 只能影响树中 下方 的组件。

六、事件处理

  • React 事件绑定属性的命名采用驼峰式写法,而不是小写。
  • 如果采用 JSX 的语法你需要传入一个函数作为事件处理函数,而不是字符串。

例如,传统的 HTML 写法:

<button onclick="activateLasers()">
    Activate Lasers
</button>

React 写法:

<button onClick={activateLasers}>
    Activate Lasers
</button>
  • 在 React 中,不能使用返回 false 的方式阻止默认行为。你必须明确的使用 preventDefault。

6.1 开关实例

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

6.2 向事件处理程序传递参数

有两种方式向事件处理程序传递参数,分别是:

  • arrow function:箭头函数
<button onClick={(e) => this.deleleRow(id,e)}>Delete Row</button>
  • Function.prototype.bind:绑定
<button onClick={this.deleleRow.bind(this,id)}>Delete Row</button>

参数 e 作为 React 的事件对象将会被作为第二个参数进行传递。

箭头函数的方式,事件对象必须显式传递;

绑定的方式,事件对象以及更多的参数将会被隐式传递。值得注意的是,通过绑定的方式向监听函数传参,在类组件中定义的监听组件,事件对象 e 排在所传递参数的后面。

七、条件渲染

React 中的条件和 JavaScript 中的一致,使用 JavaScript 操作符 if条件运算符 来创建表示当前状态的元素,然后让 React 来更新 UI。

7.1 if

示例:

function  UserGreeting() {
    return <h1>Welcome back!</h1>;
}

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

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

ReactDOM.render(
    <Greeting isLoggedIn={true}/>,
    document.getElementById('root')
)

7.2 与运算符 &&

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

const message = ['React', 'Re:React','Re:Re:React'];
ReactDOM.render(
    <Mailbox message={message}/>,
    document.getElementById('root')
)

true && expression 总是返回 expression,而 false && expression,React 会忽略并跳过它。

7.3 三目运算符

class Login extends React.Component {
    constructor(props) {
        super(props);
        this.state = {isLogin: false};
        this.login = this.login.bind(this);
    }

    login() {
        this.setState(
            prevState => ({
                isLogin: !prevState.isLogin
            })
        );
    }

    render (){
        return (
            <div>
                <h1>
                    {this.state.isLogin ? 'Welcome' : 'Please Login'}
                </h1>
                <button onClick={this.login}></button>
            </div>
        );
    }
}

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

7.4 阻止组件渲染

让组件的 render 方法返回 null 即可阻止组件渲染。

八、列表 & Keys

function ToDoList(props) {
    const things = props.things;
    const  todolist = things.map((thing) =>
        <li key={thing.toString()}>{thing}</li>
    );
    return (
        <ul>{todolist}</ul>
    )
}

const things = [1,2,3,4,5];
ReactDOM.render(
    <ToDoList things={things}/>,
    document.getElementById('root')
)
  1. 使用 map() 函数遍历数组
  2. 应当给数组中的每一个元素赋予一个确定的标识(key)。这样,当某些元素被增加或删除时能帮助 React 识别哪些元素发生了变化。
  3. 元素的 key 只有在它和它的兄弟节点对比时才有意义。
  4. 元素的 key 在其兄弟元素之间应该唯一。

九、表单

9.1 受控组件

在HTML当中,像 <input><textarea><select> 这类表单元素会维持自身状态,并根据用户输入进行更新。但在 React 中,可变的状态通常保存在组件的状态属性中,并且只能用 setState() 方法进行更新。
我们通过使 React 变成一种单一数据源的状态来结合二者。React 负责渲染表单的组件仍然控制用户后续输入时所发生的变化。相应的,其值由 React 控制的输入表单元素称为“受控组件”。

class Form extends React.Component {
    constructor(props) {
        super(props);
        this.state = {value : '1'}

        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
    }

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

    handleSubmit (event) {
        alert('name is ' + this.state.value);
        event.preventDefault();
    }

    render() {
        return (
            <form onSubmit={this.handleSubmit}>
                <label>
                    Name:
                    <select value={this.state.value} onChange={this.handleChange}>
                        <option value="1">1</option>
                        <option value="2">2</option>
                        <option value="3">3</option>
                        <option value="4">4</option>
                    </select>
                </label>
                <input type="submit" value="Submit"></input>
            </form>
        )
    };
}

ReactDOM.render(
    <Form/>,
    document.getElementById('root')
)
```

`<input type='text'>`、`<textarea>`、`<select>` 类似,都是通过传入一个 `value` 属性来实现对组件的控制。

#### 9.2 多个输入框

class Form extends React.Component {
constructor(props) {
super(props);
this.state = {
name : '',
age: 0
}

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
}

handleChange(event) {
    const target = event.target;
    const name = target.name;
    const value = target.value;

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

handleSubmit(event) {
    alert('name is ' + this.state.name + ', and age is ' + this.state.age);
    event.preventDefault();
}

render () {
    return (
        <form onSubmit={this.handleSubmit}>
            <label>
                Name:
                <input type="text" name="name" value={this.state.name} onChange={this.handleChange}/>
            </label>
            <label>
                age:
                <input type="text" name="age" value={this.state.age} onChange={this.handleChange}/>
            </label>
            <input type="submit" value="Submit"/>
        </form>
    )
}

}

ReactDOM.render(

,
document.getElementById('root')
)
````

当有多个受控的元素时,可以给每个元素添加一个 name 属性,来让处理函数 event.target.name 的值区分。

猜你喜欢

转载自www.cnblogs.com/devywb/p/9054720.html