深色模式
React 教程 - 核心概念
参考:React 教程
JSX简介
关注点分离
使用 JSX 的目的,是为了实现关注点分离。
嵌入js表达式
在 JSX 中使用大括号{}
嵌入js表达式,可以是任何有效的js表达式。
jsx
const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;
JSX 表达式有多行时,建议在外层加上括号()
,可以避免自动插入分号的陷阱。
JSX 也是一个表达式
JSX 也是一个表达式,可以赋值给变量。JSX 表达式会被转为普通 JavaScript 函数调用。
jsx
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
JSX 中指定属性
在 JSX 中为标签指定属性,属性值可以是:
放在引号
""
中的字符串;jsxconst element = <a href="https://www.reactjs.org"> link </a>;
放在大括号
{}
中的js表达式。jsxconst element = <img src={user.avatarUrl}></img>;
另外,React DOM 中使用 camelCase 定义属性名,而不是全小写。
JSX 中的标签可以嵌套。
举🌰:
jsx
const element = (
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
);
JSX 防注入攻击
JSX 表示一个对象
Babel 会把 JSX 转译成一个名为 React.createElement()
函数调用。
以下两种示例代码完全等效:
jsx
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
jsx
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
元素渲染
渲染根元素
将 React 元素渲染到 HTML DOM 中,先使用<div>
定义一个根节点:
html
<div id="root"></div>
然后使用 ReactDOM.createRoot()
创建基于该 <div>
节点的 React DOM 元素:
jsx
const root = ReactDOM.createRoot(
document.getElementById('root')
);
最后将 JSX 对象渲染到 root
中:
jsx
const element = <h1>Hello, world</h1>;
root.render(element);
更新已渲染的元素
React 元素是不可变的对象,要更新渲染,需要创建一个新的元素,并将其传入root.rendoer()
方法。
React 只更新它需要更新的部分
React DOM 会将元素和它的子元素与它们之前的状态进行比较,并只会进行必要的更新来使 DOM 达到预期的状态。
组件 & Props
函数组件与class组件
jsx
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
jsx
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
渲染组件
将React元素像DOM标签一样使用
jsx
const element = <Welcome name="Sara" />;
组合组件
在组件中组合其它组件。
提取组件
将大段嵌套的组件拆分为更小的组件
props 只读
不要修改组件自身的props。
不修改入参的函数,被称了纯函数,组件应该由纯函数构成。
State & 生命周期
class 组件的 state
只有class组件才可以使用 state
(暂不考虑Hooks)。
state
与props
类似,但state
是组件私有的,并完全受控于当前组件。
注意class组件构造函数中必须调用super(props);
在class的构造函数中定义this.state
对象。
class 组件的生命周期
组件已经被渲染到 DOM 中,React 就会调用 componentDidMount()
生命周期方法;
组件被从 DOM 中移除,React 就会调用 componentWillUnmount()
生命周期方法。
JavaScript
componentDidMount() {
}
componentWillUnmount() {
}
使用 state
不要直接修改state,而要通过setState()
方法修改。
jsx
this.setState({comment: 'Hello'});
state的更新可能是异步的,出于性能考虑,React 可能会把多个 setState()
调用合并成一个调用。
JavaScript
// 访问当前状态,用这种写法
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
state的更新可能被合并
当你调用 setState()
的时候,React 会把你提供的对象合并到当前的 state。
数据是向下流动的:单向数据流
事件处理
语法
React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
jsx
<button onClick={activateLasers}>
Activate Lasers
</button>
在 React 中另一个不同点是你不能通过返回 false
的方式阻止默认行为。你必须显式的使用 preventDefault
。
jsx
function Form() {
function handleSubmit(e) {
e.preventDefault();
console.log('You clicked submit.');
}
return (
<form onSubmit={handleSubmit}>
<button type="submit">Submit</button>
</form>
);
}
事件处理常规做法
使用事件处理函数时,要注意绑定函数中的 this:
JSX
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>
);
}
}
还有一种方法,可以省略绑定 this,就是使用 ES6 的 public class fields 语法:
。。。
事件处理传递参数
JSX
//这里要显式传递事件参数e
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
//这里会隐式传递事件参数e
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
上述两种方式是等价的,分别通过箭头函数和 Function.prototype.bind
来实现。
在这两种情况下,React 的事件对象 e
会被作为第二个参数传递。如果通过箭头函数的方式,事件对象必须显式的进行传递,而通过 bind
的方式,事件对象以及更多的参数将会被隐式的进行传递。
条件渲染
if
条件
可以通过if
或条件运算符渲染不同的元素。
jsx
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
与运算符 &&
在 JavaScript 中,true && expression
总是会返回 expression
, 而 false && expression
总是会返回 false
。
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>
);
}
在上面的表达式中,要么返回一个组件,要么返回一个 falsy 值。
三目运算符
jsx
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in.
</div>
);
}
阻止组件渲染
在 class 组件的 render()
方法中,返回 null
,或在函数组件中返回 null
,则此组件不会被渲染,但是生命周期方法仍然会执行。
列表与 Key
使用 map()
方法
使用数据map()
方法,将数据映射为组件或元素
jsx
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li>{number}</li>
);
jsx
<ul>{listItems}</ul>
为列表指定 key
渲染列表时,指定key,提高性能。
jsx
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
指定 key 的姿势
组件的 key 应该在使用这个组件的地方指定,而不是在这个组件的内部指定。
错误用法:
jsx
function ListItem(props) {
const value = props.value;
return (
// 错误!你不需要在这里指定 key:
<li key={value.toString()}>
{value}
</li>
);
}
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<ListItem value={number} />
);
return (
<ul>
{listItems}
</ul>
);
}
正确用法:
jsx
function ListItem(props) {
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>
);
}
key 值唯一
key值在兄弟节点之前必须唯一
表单
控制表单元素的和
属性,使用它成为受控组件。
受控输入框,举🌰:
jsx
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>
);
}
}
其它表单用法,可参考文档。
状态提升
组合 vs 继承
只使用组合,不使用继承