JSX语法是JavaScript的扩展, 有JS的全部功能, 用来定义UI JSX更接近JS, 不是html, 所以是使用camelCase命名 JSX在渲染前会把所有的值转换成字符串, 可以避免注入和XSS
包含渲染逻辑问题和与其他UI的逻辑的问题:
React没有分离标记与逻辑, 使用组件来组织标记与逻辑
// 可以使用花括号来插入JS表达式
const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;
// JSX 也是一个表达式
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
// JSX 可以指定属性
const element = <a href="https://www.reactjs.org"> link </a>;
const element = <img src={user.avatarUrl}></img>;
// JSX 可以嵌套
const element = (
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
);
// JSX createElement
// Babel 将 JSX编译成 React.createElement()
// 下面两个例子相等
const element = (
<h1 className="greeting">
Hello, world!
</h1>
);
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
element是react中最小的单元. 不像浏览器DOM元素, react元素创建开销小, 组件是使用元素组成 react元素是不可变的, 更新元素时需要创建新的元素, 重新渲染. 重新渲染react也会只渲染改变 了的元素
渲染react需要rootDOM, root DOM是html一个容器, 在React Application中一般只有一个根节点, 如果是集成react, 那么可以有任意个.
const root = ReactDOM.createRoot(
document.getElementById('root')
);
const element = <h1>Hello, world</h1>;
root.render(element)
组件允许你将 UI 拆分为独立可复用的代码片段,并对每个片段进行独立构思。本指南旨在介绍组件的相关理念。 // 函数组件 function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
// class组件
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
} > 二种组件等效, React元素可以是DOM标签(小字字母开头), 也可以是自定义组件(大写字母开头) > JSX收到的属性及子组件, 会打包成props参数传入组件中, > 所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。
const element = <div />;
const element = <Welcome name="Sara" />;
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
const root = ReactDOM.createRoot(document.getElementById('root'));
const element = <Welcome name="Sara" />;
root.render(element);
React 应用顶层一般为App组件
class组件才能使用状态,
this.state属性为状态, 当调用this.setState()函数时更新组件, 每次更新时 , class的render方法会被调用, 但是只要在相同的DOM节点中渲染, 就仅有一个组件对象被创建使用.
class Clock extends React.Component {
// 1
constructor(props) {
super(props);
this.state = {date: new Date()};
}
// 3
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
// 4
componentWillUnmount() {
clearInterval(this.timerID);
} tick() {
this.setState({
date: new Date()
});
}
// 2
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
// Wrong
this.state.comment = 'Hello';
// Correct
this.setState({comment: 'Hello'});
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
组件可以选择把它的 state 作为 props 向下传递到它的子组件中
使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
<button onClick={activateLasers}> Activate Lasers </button>
<a href="#" onclick="console.log('The link was clicked.'); return false">
Click me
</a>
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
你必须谨慎对待 JSX 回调函数中的 this,在 JavaScript 中,class 的方法默认不会绑定 this。 如果你忘记绑定 this.handleClick 并把它传入了 onClick,当你调用这个函数的时候 this 的值为 undefined。
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// 为了在回调中使用 `this`,这个绑定是必不可少的
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
// public class fields 语法, 实验性质
class LoggingButton extends React.Component {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
// 注意: 这是 *实验性* 语法。
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
// 箭头函数, 如果该函数作为prop传入子组件, 会造成额外的重新渲染
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// 此语法确保 `handleClick` 内的 `this` 已被绑定。
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);
}
}
// 事件对象e显式传递
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
// 事件对象e隐式传递
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.handleLoginClick = this.handleLoginClick.bind(this);
this.handleLogoutClick = this.handleLogoutClick.bind(this);
this.state = {isLoggedIn: false};
}
handleLoginClick() {
this.setState({isLoggedIn: true});
}
handleLogoutClick() {
this.setState({isLoggedIn: false});
}
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={isLoggedIn} />
{button}
</div>
);
}
}
ReactDOM.render(
<LoginControl />,
document.getElementById('root')
);
在 JavaScript 中,true && expression 总是会返回 expression, 而 false && expression 总是会返回 false。
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')
);
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn
? <LogoutButton onClick={this.handleLogoutClick} />
: <LoginButton onClick={this.handleLoginClick} />
}
</div>
);
}
render方法返回null, 该方法不会影响组件生命周期
创建多个组件时, 需要传递key值, key值帮助react识别出哪些元素改变了. 一般使用id值, 列表中唯一. 万不得已时使用下标
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
元素的 key 只有放在就近的数组上下文中才有意义。 key 只是在兄弟节点之间必须唯一
function ListItem(props) {
// 正确!这里不需要指定 key:
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>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
key 会传递信息给 React ,但不会传递给你的组件。如果你的组件中需要使用 key 属性的值,请用其他属性名显式传递这个值:
const content = posts.map((post) =>
<Post
key={post.id}
id={post.id}
title={post.title} />
);
在 HTML 中,表单元素(如、
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>
);
}
}
textarea, select 使用value属性
在 React 中,将多个组件中需要共享的 state 向上移动到它们的最近共同父组件中,便可实现共享 state。这就是所谓的“状态提升”
子组件的state值, 父组件通过props传入, props是只读的, 还需要传入onChange函数 , 子组件通过onChange通知父组件调用
this.setState()函数.
DRY: Don’t Repeat Yourself React 中的数据流是单向的,并顺着组件层级从上往下传递