react基础知识

  1. 1. react 基础知识
    1. 1.1. 基础语法JSX
    2. 1.2. 组建/props
    3. 1.3. state & 生命周期
      1. 1.3.1. 生命周期
      2. 1.3.2. 数据是向下流动的
    4. 1.4. 事件
    5. 1.5. 元素渲染
      1. 1.5.1. 条件渲染
      2. 1.5.2. 列表 & key
        1. 1.5.2.1. key
      3. 1.5.3. 表单

react 基础知识

基础语法JSX

  • 什么是jsx
    JSX即JavaScript XML。运用于React架构中,其格式比较像是模版语言,但事实上完全是在JavaScript内部实现的。元素是构成React应用的最小单位,JSX就是用来声明React当中的元素,React使用JSX来描述用户界面。
    语法例子:
    const element = <h1>Hello, world!</h1>;

  • JSX 是react 标配吗?
    对于这个问题,在react.js 框架出来的时候为了更好的实现react渲染逻辑,出现了jsx。React 认为渲染逻辑本质上与其他 UI 逻辑内在耦合,比如,在 UI 中需要绑定处理事件、在某些时刻状态发生变化时需要通知到 UI,以及需要在 UI 中展示准备好的数据。
    React 并没有采用将标记与逻辑进行分离到不同文件这种人为地分离方式,而是通过将二者共同存放在称之为“组件”的松散耦合单元之中
    比如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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')
    );

    React 不强制要求使用 JSX,但是大多数人发现,在 JavaScript 代码中将 JSX 和 UI 放在一起时,会在视觉上有辅助作用。它还可以使 React 显示更多有用的错误和警告消息。

  • JSX 嵌入表达式

    1
    2
    3
    4
    5
    6
    7
    const name = 'Josh Perez';
    const element = <h1>Hello, {name}</h1>;

    ReactDOM.render(
    element,
    document.getElementById('root')
    );
  • JSX 特定属性

    1
    nst element = <div tabIndex="0"></div>;
    1
    const element = <img src={user.avatarUrl}></img>;

    警告:
    因为 JSX 语法上更接近 JavaScript 而不是 HTML,所以 React DOM 使用 camelCase(小驼峰命名)来定义属性的名称,而不使用 HTML 属性名称的命名约定。
    例如,JSX 里的 class 变成了 className,而 tabindex 则变为 tabIndex。

  • JSX 指定子元素
    一个标签里面没有内容,你可以使用 /> 来闭合标签,就像 XML 语法一样:

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

    JSX 标签里能够包含很多子元素:

    1
    2
    3
    4
    5
    6
    const element = (
    <div>
    <h1>Hello!</h1>
    <h2>Good to see you here.</h2>
    </div>
    );
  • JSX 防止注入攻击

    1
    2
    3
    const title = response.potentiallyMaliciousInput;
    // 直接使用是安全的:
    const element = <h1>{title}</h1>;

    React DOM 在渲染所有输入内容之前,默认会进行转义。它可以确保在你的应用中,永远不会注入那些并非自己明确编写的内容。所有的内容在渲染之前都被转换成了字符串。这样可以有效地防止 XSS(cross-site-scripting, 跨站脚本)攻击。

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

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

    React.createElement() 会预先执行一些检查,以帮助你编写无错代码,但实际上它创建了一个这样的对象:

    1
    2
    3
    4
    5
    6
    7
    8
    // 注意:这是简化过的结构
    const element = {
    type: 'h1',
    props: {
    className: 'greeting',
    children: 'Hello, world!'
    }
    };

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

组建/props

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

注意: 组件名称必须以大写字母开头。
React 会将以小写字母开头的组件视为原生 DOM 标签。例如,<div /> 代表 HTML 的 div 标签,而 <Welcome /> 则代表一个组件,并且需在作用域内使用 Welcome。

你可以在深入 JSX 中了解更多关于此规范的原因。

state & 生命周期

React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。

React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}

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

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

正确地使用 State

  • 不要直接修改 State
    1
    2
    // Wrong  此代码不会重新渲染组件:
    this.state.comment = 'Hello';
1
2
// Correct
this.setState({comment: 'Hello'});

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

  • State 的更新可能是异步的
    出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。

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

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

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

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

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

注意:在某些特殊情况下 需要将更改后的状态立即更新此时可以设置setState()第二个参数
react的setstate方法中的第二个参数为会回掉函数,可以调用 forceUpdate() 强制让组件重新渲染

1
2
3
4
5
this.setState({
mystate: 'xxx'
}, ()=>{
this.forceUpdate()
})

生命周期

react 16之前
react lifecycle
react 17之后
react life new

数据是向下流动的

不管是父组件或是子组件都无法知道某个组件是有状态的还是无状态的,并且它们也并不关心它是函数组件还是 class 组件。

这就是为什么称 state 为局部的或是封装的的原因。除了拥有并设置了它的组件,其他组件都无法访问。

事件

React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同:

React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。

传统HTML

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

react中:

1
2
3
<button onClick={activateLasers}>
Activate Lasers
</button>

在 React 中另一个不同点是你不能通过返回 false 的方式阻止默认行为。你必须显式的使用 preventDefault。例如,传统的 HTML 中阻止表单的默认提交行为,你可以这样写:

1
2
3
<form onsubmit="console.log('You clicked submit.'); return false">
<button type="submit">Submit</button>
</form>

在 React 中,可能是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
function Form() {
function handleSubmit(e) {
e.preventDefault();
console.log('You clicked submit.');
}

return (
<form onSubmit={handleSubmit}>
<button type="submit">Submit</button>
</form>
);
}

在这里,e 是一个合成事件。React 根据 W3C 规范来定义这些合成事件,所以你不需要担心跨浏览器的兼容性问题。React 事件与原生事件不完全相同。

如果想了解更多,请查看 SyntheticEvent 参考指南。

完整例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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')
);

  • 向事件处理程序传递参数
    1
    2
    <button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
    <button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
    React 的事件对象 e 会被作为第二个参数传递。如果通过箭头函数的方式,事件对象必须显式的进行传递,而通过 bind 的方式,事件对象以及更多的参数将会被隐式的进行传递。

元素渲染

条件渲染

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

1
2
3
4
5
6
7
function UserGreeting(props) {
return <h1>Welcome back!</h1>;
}

function GuestGreeting(props) {
return <h1>Please sign up.</h1>;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}

ReactDOM.render(
// Try changing to isLoggedIn={true}:
<Greeting isLoggedIn={false} />,
document.getElementById('root')
);

运算符 &&
通过花括号包裹代码,你可以在 JSX 中嵌入表达式。这也包括 JavaScript 中的逻辑与 (&&) 运算符。它可以很方便地进行元素的条件渲染:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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。

1
2
3
4
5
6
7
8
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
</div>
);
}

列表 & key

map函数
map() 方法创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。

渲染多个组件
你可以通过使用 {} 在 JSX 内构建一个元素集合。

下面,我们使用 Javascript 中的 map() 方法来遍历 numbers 数组。将数组中的每个元素变成

  • 标签,最后我们将得到的数组赋值给 listItems:

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

    基础列表组件

    通常你需要在一个组件中渲染列表。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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')
    );

    key

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

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

    一个元素的 key 最好是这个元素在列表中拥有的一个独一无二的字符串。 不建议使用Math.random() 来当作key值
    为什么 key 是必须的

    表单

    在 React 里,HTML 表单元素的工作方式和其他的 DOM 元素有些不同,这是因为表单元素通常会保持一些内部的 state。例如这个纯 HTML 表单只接受一个名称:

    1
    2
    3
    4
    5
    6
    7
    <form>
    <label>
    名字:
    <input type="text" name="name" />
    </label>
    <input type="submit" value="提交" />
    </form>
    • 受控组件
      在 HTML 中,表单元素(如<input><textarea><select>)通常自己维护 state,并根据用户输入进行更新。而在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()来更新。

    我们可以把两者结合起来,使 React 的 state 成为“唯一数据源”。渲染表单的 React 组件还控制着用户输入过程中表单发生的操作。被 React 以这种方式控制取值的表单输入元素就叫做“受控组件”。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    class NameForm extends React.Component {
    constructor(props) {
    super(props);
    this.state = {value: ''};

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

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

    handleSubmit(event) {
    alert('提交的名字: ' + this.state.value);
    event.preventDefault();
    }

    render() {
    return (
    <form onSubmit={this.handleSubmit}>
    <label>
    名字:
    <input type="text" value={this.state.value} onChange={this.handleChange} />
    </label>
    <input type="submit" value="提交" />
    </form>
    );
    }
    }
    • 非受控组件
      在大多数情况下,我们推荐使用 受控组件 来处理表单数据。在一个受控组件中,表单数据是由 React 组件来管理的。另一种替代方案是使用非受控组件,这时表单数据将交由 DOM 节点来处理。

    要编写一个非受控组件,而不是为每个状态更新都编写数据处理函数,你可以 使用 ref 来从 DOM 节点中获取表单数据。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class NameForm extends React.Component {
    constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.input = React.createRef();
    }

    handleSubmit(event) {
    alert('A name was submitted: ' + this.input.current.value);
    event.preventDefault();
    }

    render() {
    return (
    <form onSubmit={this.handleSubmit}>
    <label>
    Name:
    <input type="text" ref={this.input} />
    </label>
    <input type="submit" value="Submit" />
    </form>
    );
    }
    }