文章目录
JSX简介
const element = <h1>Hello, world!</h1>;
这个有趣的标签语法既不是字符串也不是 HTML。
它被称为 JSX,是一个 JavaScript 的语法扩展。JSX 可以生成 React “元素”(可以渲染为DOM)。
在JSX中嵌入表达式
const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;
在 JSX 语法中,你可以在大括号内放置任何有效的 JavaScript 表达式。例如,2 + 2
,user.firstName
或 formatName(user)
都是有效的 JavaScript 表达式。
JSX也是一个表达式
也就是说,你可以在 if 语句和 for 循环的代码块中使用 JSX,将 JSX 赋值给变量,把 JSX 当作参数传入,以及从函数中返回 JSX:
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
JSX特定属性
- 你可以通过使用
引号
,来将属性值指定为字符串字面量
:
const element = <div tabIndex="0"></div>;
- 也可以使用
大括号
,来在属性值中插入一个JavaScript 表达式
:
const element = <img src={user.avatarUrl}></img>;
在属性中嵌入 JavaScript 表达式时,不要在大括号外面加上引号。你应该仅使用引号(对于字符串值)或大括号(对于表达式)中的一个,对于同一属性不能同时使用这两种符号。
元素渲染
将一个元素渲染为 DOM
想要将一个 React 元素渲染到根 DOM 节点中,只需把它们一起传入 ReactDOM.render()
:
const element = <h1>Hello, world</h1>;
ReactDOM.render(element, document.getElementById('root'));
更新已渲染的元素
React 元素是不可变对象。一旦被创建,你就无法更改它的子元素或者属性。
目前,更新 UI (视图)的方式是创建一个全新的元素,并将其传入 ReactDOM.render()
。(重复调用ReactDOM.render()
方法)
注:大多数 React 应用只会调用一次 ReactDOM.render()。
React 只更新它需要更新的部分
React DOM 会将元素和它的子元素与它们之前的状态进行比较,并只会进行必要的更新来使 DOM 达到预期的状态。
组件&props
函数组件和class组件
函数:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
class:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
注:组件名称必须以大写字母开头。
渲染组件
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const element = <Welcome name="Sara" />;
ReactDOM.render(
element,
document.getElementById('root')
);
props的只读性
下面这个函数则不是纯函数,因为它更改了自己的入参:
function sum(a, b) {
return a + b;
}
上面这样的函数被称为“纯函数”,因为该函数不会尝试更改入参,且多次调用下相同的入参始终返回相同的结果。
function withdraw(account, amount) {
account.total -= amount;
}
所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。
state&生命周期
向 class 组件中添加局部的 state
state的位置:
class Clock extends 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>
);
}
}
将生命周期方法添加到 Class 中
// componentDidMount() 方法会在组件已经被渲染到 DOM 中后运行
componentDidMount(){
}
componentWillUnmount(){
}
正确地使用 State
- state的正确使用:不要直接修改 State
// Correct
this.setState({comment: 'Hello'});
// Wrong
this.state.comment = 'Hello';
- State 的更新可能是异步的
// Correct 使用函数
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
- State 的更新会被合并 :
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
posts和comments的更新,互不影响。
“自上而下”或是“单向”的数据流。任何的 state 总是所属于特定的组件,而且从该 state 派生的任何数据或 UI 只能影响树中“低于”它们的组件。
事件处理
- bind方法
this.fn=this.fn.bind(this)
this.fn.bind(this)
,即:在构造器中绑定。
const domEle = document.getElementById('root');
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {
isToggleOn: false,
}
this.hanleClick = this.hanleClick.bind(this);
}
hanleClick() {
console.log("this:", this); // 如果没有bind: this = undefined
this.setState({isToggleOn: !this.state.isToggleOn});
}
render() {
return (
<div>
<button onClick={this.hanleClick}>{this.state.isToggleOn ? 'On' : 'Off'}</button>
// <button onClick={this.hanleClick.bind(this)}>{this.state.isToggleOn ? 'On' : 'Off'}</button>
</div>
)
}
}
ReactDOM.render(
<Toggle/>,
domEle
)
- 实验性语法。
hanleClick = () => {}
,即: public class fields 语法
const domEle = document.getElementById('root');
class Toggle extends React.Component{
constructor(props){
super(props);
this.state={
isToggleOn:false,
}
// this.hanleClick=this.hanleClick.bind(this);
}
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
// 注意: 这是 *实验性* 语法。
hanleClick = () => { // 如果没有bind的话,可以使用这种方法。
console.log("this:",this);
this.setState({isToggleOn:!this.state.isToggleOn});
}
render(){
return (
<div>
<button onClick={this.hanleClick}>{this.state.isToggleOn?'On':'Off'}</button>
</div>
)
}
}
ReactDOM.render(
<Toggle />,
domEle
)
- 在回调中使用箭头函数:
onClick={(e)=>this.hanleClick(e)}
,不推荐使用这种方法。
<button onClick={(e)=>this.hanleClick(e)}>{this.state.isToggleOn ? 'On' : 'Off'}</button>
向事件处理程序传递参数
// 在回调中使用箭头函数
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
条件渲染
function UserGreeting(props) {
return <h1>user打招呼了</h1>
}
function GuestGreeting(props) {
return <h2>访客打招呼了</h2>
}
class Greeting extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
const isLoggedIn = this.props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting/>
} else {
return <GuestGreeting/>
}
}
}
ReactDOM.render(
<Greeting isLoggedIn={false}/>,
domEle
)
元素变量
你可以使用变量来储存元素。
const domEle = document.getElementById('root');
function UserGreeting(props) {
return <h2>欢迎回来。</h2>
}
function GuestGreeting(props) {
return <h2>请登录</h2>
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting/>
}
return <GuestGreeting/>
}
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.state = {
isLoggedIn: true,
};
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.handleLoginClick = this.handleLoginClick.bind(this);
}
handleLogoutClick() {
this.setState({
isLoggedIn: false,
})
}
handleLoginClick() {
this.setState({
isLoggedIn: true,
})
}
render() {
const isLoggedIn = this.state.isLoggedIn;
let button;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick}/>
} else {
button = <LoginButton onClick={this.handleLoginClick}/>
}
return (
<div>
<Greeting isLoggedIn={this.state.isLoggedIn}/>
{button}
</div>
);
}
}
ReactDOM.render(
<LoginControl/>,
domEle
)
与运算符 &&
return (
<div>
<h1>你好!</h1>
{unreadMessage.length>0&&
<h3>你有{unreadMessage.length}条消息</h3>
}
</div>
)
或者:
{
unreadMessage.length>0 ?
(<h3>你有{unreadMessage.length}条消息</h3>):
(null)
}
三目运算符
render() {
const isLoggedIn = this.state.isLoggedIn;
let button;
// if (isLoggedIn) {
// button = <LogoutButton onClick={this.handleLogoutClick}/>
// } else {
// button = <LoginButton onClick={this.handleLoginClick}/>
// }
return (
<div>
{/*{button}*/}
{
isLoggedIn ? (<LogoutButton onClick={this.handleLogoutClick}/>)
: (<LoginButton onClick={this.handleLoginClick}/>)
}
</div>
);
}
LogoutButton和LoginButton这两个组件可以合并到一起的。
function LoginButton(props) {
// 执行父组件的事件
return (
<button onClick={props.onClick}>
{props.isLoggedIn?'登出':'登录'}
</button>
)
}
<LoginButton isLoggedIn={this.state.isLoggedIn} onClick={this.handleLoginClick}/>
阻止组件渲染
在极少数情况下,你可能希望能隐藏组件,即使它已经被其他组件渲染。若要完成此操作,你可以让 render 方法直接返回 null
,而不进行任何渲染。
function WarningBanner(props) {
if(!props.warn){
return null;
}
return (
<div className="warning">
warning!
</div>
)
}
列表&key
const numbers=[1,2,3,4,5];
// const listItems= numbers.map((number)=><li key={number}>我是第{number}个</li>)
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) => <li key={number}>我是第{number}个</li>);
return (
<ul>
{listItems}
</ul>
)
}
ReactDOM.render(
<NumberList numbers={numbers}/>,
domEle
)
key
key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。一个元素的 key 最好是这个元素在列表中拥有的一个独一无二
的字符串
。通常,我们使用数据中的 id 来作为元素的 key:
const listItems = numbers.map((number)=><li key={number.toString()}>我是第{number}个1</li>);
用 key 提取组件
元素的 key 只有放在就近的数组上下文中才有意义。
比方说,如果你提取出一个 ListItem
组件,你应该把 key
保留在数组中的这个 <ListItem />
元素上,而不是放在 ListItem
组件中的 <li>
元素上。
<ListItem key={number.toString()}
value={number} />
key 只是在兄弟节点之间必须唯一
数组元素中使用的 key 在其兄弟节点之间应该是独一无二的。然而,它们不需要是全局唯一的。当我们生成两个不同的数组时,我们可以使用相同的 key 值:
key 会传递信息给 React ,但不会传递给你的组件。如果你的组件中需要使用 key 属性的值,请用其他属性名显式传递这个值:
const content = posts.map((post) =>
<Post
key={post.id}
id={post.id}
title={post.title} />
);
表单
input
handleChange(event) {
console.log("change-e:", event);
this.setState({value: event.target.value,});
}
handleSubmit(event) {
console.log("submit-e:", event);
console.log("submit-val:", 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>
)
}
如果只有value,没有onChange,那么input是只读的。
Failed prop type: You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.
对于受控组件来说,每个 state 突变都有一个相关的处理函数。这使得修改或验证用户输入变得简单。例如,如果我们要强制要求所有名称都用大写字母书写,我们可以将 handlechange 改写为:
handleChange(event) {
this.setState({value: event.target.value.toUpperCase()});
}
textarea
textarea与input是一样的:
class EssayForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
console.log("change-event:", event);
this.setState({value: event.target.value})
}
handleSubmit(event) {
console.log("submit-event:", event);
console.log("submit-value:", this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
文章:
<textarea placeholder="请输入"
value={this.state.value}
onChange={this.handleChange}>
</textarea>
</label>
<input type="submit" value="提交"/>
</form>
)
}
}
select标签
class FlavorForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'coconut'};
this.handleChange = this.handleChange.bind(this);
this.handelSubmit = this.handelSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handelSubmit(event) {
console.log("选中的值:", this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handelSubmit}>
<label>
选择你喜欢的:
<select value={this.state.value} onChange={this.handleChange}>
<option value="grapefruit">葡萄柚</option>
<option value="lime">酸橙</option>
<option value="coconut">椰子</option>
<option value="mango">芒果</option>
</select>
</label>
<input type="submit" value="提交"/>
</form>
)
}
}
处理多个输入
当需要处理多个 input 元素时,我们可以给每个元素添加 name 属性,并让处理函数根据 event.target.name 的值选择要执行的操作。
class Reservation extends React.Component {
constructor(props) {
super(props);
this.state = {
isGoing: true,
numberOfGuests: 2,
};
this.handleChange=this.handleChange.bind(this);
this.handleSubmit=this.handleSubmit.bind(this);
}
handleSubmit(event) {
console.log("submit-event", event);
console.log("submit-value", this.state.isGoing,this.state.numberOfGuests);
event.preventDefault();
}
handleChange(event) {
console.log("change-event:", event,event.target,event.target.checked);
const target = event.target;
const value = target.type=='checkbox'?target.checked:target.value;
const name = target.name;
console.log("name:",name);
this.setState({
[name]:value,
})
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
参与:
<input type="checkbox"
name="isGoing"
checked={this.state.isGoing}
onChange={this.handleChange}/>
</label>
<br/>
<label>
来宾人数:
<input type="number"
name="numberOfGuests"
value={this.state.numberOfGuests}
onChange={this.handleChange}/>
</label>
<input type="submit" value="提交"/>
</form>
)
}
}
其中:
this.setState({
[name]: value
});
状态提升
通常,多个组件需要反映相同的变化数据,这时我们建议将共享状态提升到最近的共同父组件中去。
在 React 中,将多个组件中需要共享的 state 向上移动到它们的最近共同父组件中,便可实现共享 state。这就是所谓的“状态提升”。
props 是只读的。