React(2-2):JSX进阶

ObjectKaz Lv4

JSX 进阶

列表渲染

所谓列表渲染,是指当写入 jsx 的数据是一个React DOM数组时,最终会生成一个列表。

这个例子生成一个简单的列表:

1
2
3
4
5
6

const arr = [1,2,3,4,5];

const lists = arr.map(x => <li>{x}</li>);

const el = (<ul>{lists}</ul>);

但我们很快收到了一个警告:

Each child in a list should have a unique “key” prop.

意思是当你创建一个元素时,必须包括一个特殊的 key 属性。

我们可以指定 key 来解决问题:

1
2
3
4
5
const arr = [1,2,3,4,5];

const lists = arr.map(x => <li key={x}>{x}</li>);

const el = (<ul>{lists}</ul>);

问题是解决了,那为什么加一个 key 呢?

因为,key 帮助 React 识别哪些元素改变了,比如被添加或删除。[1]

对于同一个数组,每个React DOM 应该具有不同的 key

条件渲染

使用 if 语句

使用 if 语句就可以快速实现条件渲染:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let state = true;

function render() {
let el = null;
if (state) {
el = <p>state is true.</p>;
} else {
el = <p>state is false.</p>;
}
let editState = () => (state = !state);

ReactDOM.render(
<div>
{el} <button onClick={editState}>更改状态</button>{" "}
</div>,
document.querySelector("#container")
);
}

render();

使用运算符

除了使用 if 语句,还可以使用运算符来实现条件渲染:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let state = true;

function render() {
let editState = () => (state = !state);

ReactDOM.render(
<div>
{state ? <p>state is true.</p> : <p>state is false.</p>} <button onClick={editState}>更改状态</button>{" "}
</div>,
document.querySelector("#container")
);
}

render();

元素的不可变性

在上面的两个例子,你应该发现了这样一个问题:元素下面的按钮无论怎么点,变量是改变了,但视图却没有刷新。

需要注意一点,React 元素是不可变的。一旦被创建,你就无法更改它的子元素或者属性。唯一修改的方式便是创建一个全新的元素,并将其传入 ReactDOM.render() 。但是,React DOM 会将元素和它的子元素与它们之前的状态进行比较,并只会进行必要的更新来使 DOM 达到预期的状态。[2]

所以要想让界面发生变化,我们需要修改一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let state = true;

function render() {
let editState = () => {
state = !state;
render();
};

ReactDOM.render(
<div>
{state ? <p>state is true.</p> : <p>state is false.</p>}
<button onClick={editState}>更改状态</button>
</div>,
document.querySelector("#container")
);
}

render();

这样,再点击按钮,视图就发生变化了。

但这看起来有些麻烦,使用过 Vue 的同学应该希望当变量发生改变时,UI也会自动更新吧。

后文会提到组件的状态,它就可以自动调用 render,来实现UI的自动更新。

表单输入绑定

受控组件

在HTML中,像 input, textareaselect 等表单元素会维持自身状态,并会根据用户输入进行更新。

我们可以将表单元素的值绑定在变量中,并且监听表单事件的更新,就可以将表单输入和表单对象进行同步。

在这种操作下,inputtextareaselect 像是被父组件控制了一样,称为 受控组件

处理表单输入

处理 input type=text

1
2
3
4
5
6
7
8
9
10
11
12
let handleInputChange = (name) => (e) => {
form[name] = e.target.value;
render();
};

let textInput = (
<input
type="text"
value={form["text"]}
onChange={handleInputChange("text")}
/>
);

处理 textarea

这里和原始的 html 有点不一样,它使用 value 进行传值,使用 onChange 进行更新

1
2
3
4
5
6
7
8
let handleInputChange = (name) => (e) => {
form[name] = e.target.value;
render();
};

let textAreaInput = (
<textarea value={form["textarea"]} onChange={handleInputChange("textarea")} />
);

处理 radio

1
2
3
4
5
6
7
8
9
10
11
12
let handleCheckState = (name) => (e) => {
form[name] = e.target.checked;
render();
};

let radioInput = (
<radio
name="radio"
checked={form["radio"]}
onChange={handleCheckState("textarea")}
/>
);

处理 input type=checkbox

1
2
3
4
5
6
7
8
9
10
11
12
13
let handleCheckState = (name) => (e) => {
form[name] = e.target.checked;
render();
};

let checkboxInput = (
<input
name="check"
type="checkbox"
checked={form["checkbox"]}
onChange={handleCheckState("checkbox")}
/>
);

处理 select

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

let handleInputChange = (name) => (e) => {
form[name] = e.target.value;
render();
};

let selectInput = (
<select value={form["select"]} onChange={handleInputChange("select")}>
<option>星期一</option>
<option>星期二</option>
<option>星期三</option>
<option>星期四</option>
<option>星期五</option>
<option>星期六</option>
<option>星期日</option>
</select>
);

不可修改的输入

如果只绑定了 value,而没有绑定更新的事件,那么这个表单将无法接受用户的输入:

1
let readOnlyInput = <input value={form["readonly"]} />;

但是,如果表单输入属性的 value 被指定为 undefinednull,那么表单便可以修改了:

1
let writableInput = <input value={form["writable"]} />; // form["writable"] 默认为 undefined

完整的示例

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
let form = {
readonly: "123",
};

let handleInputChange = (name) => (e) => {
form[name] = e.target.value;
render();
};

let handleCheckState = (name) => (e) => {
form[name] = e.target.checked;
render();
};

let textInput = (
<input
type="text"
value={form["text"]}
onChange={handleInputChange("text")}
/>
);

let textAreaInput = (
<textarea value={form["textarea"]} onChange={handleInputChange("textarea")} />
);

let radioInput = (
<input
name="radio"
type="radio"
checked={form["radio"]}
onChange={handleCheckState("radio")}
/>
);

let checkboxInput = (
<input
name="check"
type="checkbox"
checked={form["checkbox"]}
onChange={handleCheckState("checkbox")}
/>
);

let selectInput = (
<select value={form["select"]} onChange={handleInputChange("select")}>
<option>星期一</option>
<option>星期二</option>
<option>星期三</option>
<option>星期四</option>
<option>星期五</option>
<option>星期六</option>
<option>星期日</option>
</select>
);

let readOnlyInput = <input value={form["readonly"]} />;
let writableInput = <input value={form["writable"]} />;

function render() {
ReactDOM.render(
<div>
{textInput}
{textAreaInput}
{radioInput}
{checkboxInput}
{selectInput}
{readOnlyInput}
{writableInput}
</div>,
document.querySelector("#container")
);
}

render();

小结

表单元素绑定属性绑定方法事件回调新值
input[type=text]valueonChangeevent.target.value
input[type=checkbox]checkedonChangeevent.target.checked
input[type=radio]checkedonChangeevent.target.checked
textareavalueonChangeevent.target.value
selectvalueonChangeevent.target.value

片段

有些时候,我们希望将一些 React DOM 组合起来,像这样:

1
<a>1</a><a>2</a>

但默认情况下,直接把它们写在一起是会报错的:

1
const el = (<a>1</a><a>2</a>);

因为 React 规定,一个 jsx 必须有一个根节点。

然后条件反射,就直接加上了一个根 div节点:

1
const el = (<div><a>1</a><a>2</a></div>);

但有时候并不是开发者想要的。

比如,开发者想把不同的 a 链接写到一块,这时候有另外一些代码:

1
2
const el2 = (<div><a>3</a><a>4</a></div>);
const el3 = (<div>{el}{el2}</div>);

这样一写,这些链接便被分成了两行了。

这种情况下,我们就可以使用 片段(Fragments) 来解决这个问题。

片段是一个 React 的组件,它可以当成一个节点使用,但不会渲染出任何 HTML DOM

上面的例子就可以这样写:

1
2
3
const el = (<React.Fragment><a>1</a><a>2</a></React.Fragment>);
const el2 = (<React.Fragment><a>3</a><a>4</a></React.Fragment>);
const el3 = (<div>{el}{el2}</div>);

用过 Vue 的朋友应该很容易发现,这类似于 Vuetemplate 组件。


  1. 列表&key - React ↩︎

  2. 元素渲染 - React ↩︎

  • 标题: React(2-2):JSX进阶
  • 作者: ObjectKaz
  • 创建于: 2021-05-03 05:58:58
  • 更新于: 2021-05-03 06:00:00
  • 链接: https://www.objectkaz.cn/b3995e6fdd1f.html
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
此页目录
React(2-2):JSX进阶