适用范围:熟悉es6语法、最好是Vue开发者
环境:
这就其实指能快速run react 语法的环境:
- codepen:缺点是没有语法提示(或许是我自己不会配?)
- react脚手架
第一个项目:
ReactDOM.render(
Hello, world!
,
document.getElementById('root')
);
JSX简介:
const element = Hello, world!
;
它被称为 JSX, 一种 JavaScript 的语法扩展。 我们推荐在 React 中使用 JSX 来描述用户界面。JSX 乍看起来可能比较像是模版语言,但事实上它完全是在 JavaScript 内部实现的。
在 JSX 中使用表达式:
function formatName(user) {
return user.firstName + ' ' + user.lastName;
}
const user = {
firstName: 'Harper',
lastName: 'Perez'
};
const element = (
Hello, {formatName(user)}!
);
ReactDOM.render(
element,
document.getElementById('root')
);
function getGreeting(user) {
if (user) {
return Hello, {formatName(user)}!
;
}
return Hello, Stranger.
;
}
jsx可以作为参数 或者返回值。
JSX 属性
const element = ;
或者打括号定义:
const element = ;
警告:
因为 JSX 的特性更接近 JavaScript 而不是 HTML , 所以 React DOM 使用camelCase
小驼峰命名 来定义属性的名称,而不是使用 HTML 的属性名称。
例如,class
变成了className
,而tabindex
则对应着tabIndex
。
JSX 防注入攻击:会自动过滤
JSX 代表 Objects
Babel 转译器会把 JSX 转换成一个名为 React.createElement() 的方法调用。
const element = (
Hello, world!
);
等同于
const element = React.createElement(
'h1',
{className: 'greeting'},
'Hello, world!'
);
返回
const element = {
type: 'h1',
props: {
className: 'greeting',
children: 'Hello, world'
}
};
组件&Props
React 元素都是immutable 不可变的。当元素被创建之后,你是无法改变其内容或属性的。一个元素就好像是动画里的一帧,它代表应用界面在某一时间点的样子。
根据我们现阶段了解的有关 React 知识,更新界面的唯一办法是创建一个新的元素,然后将它传入 ReactDOM.render()
方法:
function tick() {
const element = (
Hello, world!
It is {new Date().toLocaleTimeString()}.
);
ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick, 1000);
React 只会更新必要的部分
React DOM 首先会比较元素内容先后的不同,而在渲染过程中只会更新改变了的部分。
定义组件的两种方式:
函数定义:
function Welcome(props) {
return Hello, {props.name}
;
}
类定义:
class Welcome extends React.Component {
render() {
return Hello, {this.props.name}
;
}
}
调用:
const element = ;
在函数定义:props. 可以直接访问,而在类中需要this.props.
Props的只读性
props是单向数据流,在组件里不能修改。
State & 生命周期
定义为类的组件有一些额外的特性。局部状态就是如此:只能用于类的一个功能。
这样我们就可以定义一个状态:状态与属性十分相似,但是状态是私有的,完全受控于当前组件。
这里可以理解为Vue里组件的data。
{{ conut }}
当count改变的时候,视图也会刷新,而这里就和react的state类型。
vue里的data是双向绑定的,但是react里不是
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
render() {
return (
Hello, world!
It is {this.state.date.toLocaleTimeString()}.
);
}
}
如何让时钟走起来?在生命周期函数中挂载方法:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
Hello, world!
It is {this.state.date.toLocaleTimeString()}.
);
}
}
关于setState()
需要注意:
- 不能直接更新状态
// Wrong
this.state.comment = 'Hello';
- 状态更新是异步的
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
// Correct
this.setState(function(prevState, props) {
return {
counter: prevState.counter + props.increment
};
});
- 可以单独设置状态的某一个属性 而不是整个对象:
constructor(props) {
super(props);
this.state = {
posts: [],
comments: []
};
}
componentDidMount() {
fetchPosts().then(response => {
this.setState({
posts: response.posts
});
});
fetchComments().then(response => {
this.setState({
comments: response.comments
});
});
}
事件处理:
- React事件绑定属性的命名采用驼峰式写法,而不是小写。
- 如果采用 JSX 的语法你需要传入一个函数作为事件处理函数,而不是一个字符串(DOM元素的写法)
在 React 中另一个不同是你不能使用返回 false 的方式阻止默认行为。你必须明确的使用 preventDefault。
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
Click me
);
}
你必须谨慎对待 JSX 回调函数中的 this
,类的方法默认是不会绑定 this
的。如果你忘记绑定 this.handleClick
并把它传入 onClick
, 当你调用这个函数的时候 this
的值会是 undefined
。
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
这并不是 React 的特殊行为;它是函数如何在 JavaScript 中运行的一部分。通常情况下,如果你没有在方法后面添加 ()
,例如 onClick={this.handleClick}
,你应该为这个方法绑定 this
。
如果使用 bind
让你很烦,这里有两种方式可以解决。如果你正在使用实验性的属性初始化器语法,你可以使用属性初始化器来正确的绑定回调函数:
class LoggingButton extends React.Component {
// This syntax ensures `this` is bound within handleClick.
// Warning: this is *experimental* syntax.
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
);
}
}
如果没有属性初始化器语法:
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// This syntax ensures `this` is bound within handleClick
return (
);
}
}
条件渲染:
if-else:
render() {
const isLoggedIn = this.state.isLoggedIn;
let button = null;
if (isLoggedIn) {
button = ;
} else {
button = ;
}
return (
{button}
);
}
与运算符 && 和三目运算符:
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
Hello!
{unreadMessages.length > 0 &&
You have {unreadMessages.length} unread messages.
}
);
}
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
{isLoggedIn ? (
) : (
)}
);
}
if-else是语法不是表达式,{}可以填入任何表达式。所以可以用运算符&&,这点和vue也是一样的。但是需要注意的是&&前必须是Boolean类型不能类Boolean类型,比如0
,空字符串''
。
如何阻止组件渲染?
在极少数情况下,你可能希望隐藏组件,即使它被其他组件渲染。让 render 方法返回 null 而不是它的渲染结果即可实现。
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
Warning!
);
}
组件的 render 方法返回 null 并不会影响该组件生命周期方法的回调。例如,componentWillUpdate 和 componentDidUpdate 依然可以被调用。
列表 & Keys
渲染多个组件:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
{number}
);
我们把整个listItems
插入到ul
元素中,然后渲染进DOM:
ReactDOM.render(
{listItems}
,
document.getElementById('root')
);
当我们运行这段代码,将会看到一个警告 a key should be provided for list items ,意思是当你创建一个元素时,必须包括一个特殊的 key 属性。
Keys
Keys可以在DOM中的某些元素被增加或删除的时候帮助React识别哪些元素发生了变化。因此你应当给数组中的每一个元素赋予一个确定的标识。
熟悉Vue这里就能直接理解
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
{number}
);
表单:
受控组件:在HTML当中,像,
, 和
这类表单元素会维持自身状态,并根据用户输入进行更新。但在React中,可变的状态通常保存在组件的状态属性中,并且只能用
setState()
方法进行更新。
在vue里我们有双向绑定:val变化视图变化,视图修改了val也变了。但是在react我们需要自己实现这一点,如何实现?
首先:
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
然后每次当input更改的时候调用this.handleChange方法
handleChange(event) {
this.setState({value: event.target.value});
}
达到双向绑定的效果。
textarea 标签
在HTML当中,
render() {
return (
);
}
select 标签
render() {
return (
);
}
多个输入的解决方法
当你有处理多个受控的input元素时,你可以通过给每个元素添加一个name属性,来让处理函数根据 event.target.name的值来选择做什么。
render() {
return (
);
}
状态提升:
建议直接看文档能够清楚理解。
但是我们需要明白为什么这么写。
需求:InputA变了InputB也要,我们知道能改变的属性值只有内部属性state。生命在InputA中的state不能影响B。同理B也不能影响A。那么怎么解决呢?用一个容器组件C包裹InputA和InputB,容器组件C中的state就能自由改变,且作为props传给InputA和B,这样首先我们解决第一个问题:A、B共享可变数据。 再看需求我们并不是要C变了A、B改变,而是A变了B变,B变了C变,那么我们就需要解决A变了之后,把这个改变通知给C,交给C去改变,这样就能影响B了。
如何通知?类似Vue的事件机制:
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.props.onTemperatureChange(e.target.value);
}
render() {
const temperature = this.props.temperature;
const scale = this.props.scale;
return (
);
}
}
TemperatureInput在这里可以理解为是组件A或B。
通知机制就是:this.props.onTemperatureChange(e.target.value);
组件C:就在外部监视:
class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
this.state = {temperature: '', scale: 'c'};
}
handleCelsiusChange(temperature) {
this.setState({scale: 'c', temperature});
}
handleFahrenheitChange(temperature) {
this.setState({scale: 'f', temperature});
}
render() {
const scale = this.state.scale;
const temperature = this.state.temperature;
const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
return (
);
}
这样A、B变了,就通知到了C,C更新自己的state并传给A、B。
组合 vs 继承
组合:这里就类型Vue的slot:
包含关系
function FancyBorder(props) {
return (
{props.children}
);
}
这样做还允许其他组件通过嵌套 JSX 来传递子组件。
function WelcomeDialog() {
return (
Welcome
Thank you for visiting our spacecraft!
);
}
虽然不太常见,但有时你可能需要在组件中有多个入口,这种情况下你可以使用自己约定的属性而不是 children:(具名slot)
function SplitPane(props) {
return (
{props.left}
{props.right}
);
}
function App() {
return (
}
right={
} />
);
}
React的基础概念就是这些,会了Vue基本上一天内搞定。
参考文献
https://react.docschina.org/