目录
本文为本人略读官网文档后的大略笔记,实在不适合他人阅读
前言
React起源于Facebook(的内部项目,用来架设Instagram的网站)。
2013年5月开源。
是一个用于构建UI界面的JavaScript库。
特点
1. 声明式设计
React采用声明范式。
2. 高效
React通过模拟DOM操作,最大限度地减少与DOM交互。
3.灵活
React可结合已知框架。
4.JSX
React开发建议使用JSX。
JSX对JavaScript语法进行了扩展。
5.组件
React通过构建组件来进行复用。
6.单向响应的数据流
React实现了单向响应的数据流,从而减少了重复代码(相比传统数据绑定,更简单)。
源(CDN)
Staticfile CDN
<!--React 的核心库-->
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<!--提供与 DOM 相关的功能-->
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<!--Babel 将ES6代码转为ES5代码。Babel内嵌了对JSX的支持。通过将Babel和babel-sublime包一同使用可以让源码的语法渲染上升到一个全新的水平。-->
<!-- 生产环境中不建议使用,在浏览器中使用 Babel 来编译 JSX 效率是非常低的 -->
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
官方 CDN
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<!-- 生产环境中不建议使用 -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
Hello world
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel">
ReactDOM.render(
<h1>Hello world!</h1>,
document.getElementById('example')
);
</script>
</body>
</html>
说明:
ReactDOM.render函数,将参数1(h1标题)插入参数2(id="example"节点)中。
注意:
如果需要使用JSX,则<script>标签的type属性必需设置为text/babel。
展示结果
1. 快速构建React开发环境(create-react-app)
create-react-app 来自于Facebook。
通过该命令无需配置就能快速构建React开发环境(基于Webpack + ES6)。
cnpm install -g create-react-app
create-react-app my-app
cd my-app/
npm start
浏览器输入http://localhost:3000/ 查看效果
展示结果
项目的目录结构
my-app/
README.md
node_modules/
package.json
yarn.lock
public/
favicon.ico
index.html
manifest.json
src/
App.css
App.js
App.test.js
index.css
index.js
logo.svg
serviceWorker.js
setupTests.js
2. React元素渲染与更新
- React元素渲染
const element = <h1>Hello, world!</h1>;
元素是构成React应用的最小单位。
与浏览器的DOM元素不同,React的DOM元素本质上是普通的对象。
》》第一步:在HTML页面中添加一个带id的<div>
<div id="example"></div>
在该div中的所有内容都将由React DOM来管理,所以称该div为根DOM节点。
一般只会定义一个根节点。但如果是在一个已有的项目当中引入 React 的话,可能会需要在不同的部分单独定义不同的React根节点。
》》第二步:使用ReactDOM.render函数渲染React元素到根DOM节点中
const element = <h1>Hello, world!</h1>;
ReactDOM.render(
element,
document.getElementById('example')
);
- 更新元素渲染
React元素被创建后不可变(无法改变其内容或属性)。
目前更新界面的唯一办法是再创建一个新的元素,重新调用ReactDOM.render()方法传入新元素。React DOM首先会比较元素内容的不同,在渲染过程中只会更新改变的部分。
例1(时钟,1s调用一次render)
function tick() {
const element = (
<div>
<h1>Hello, world!</h1>
<h2>现在是 {new Date().toLocaleTimeString()}.</h2>
</div>
);
ReactDOM.render(
element,
document.getElementById('example')
);
}
setInterval(tick, 1000);
上述代码优化:
function Clock(props) {
return (
<div>
<h1>Hello, world!</h1>
<h2>现在是 {props.date.toLocaleTimeString()}.</h2>
</div>
);
}
function tick() {
ReactDOM.render(
<Clock date={new Date()} />,
document.getElementById('example')
);
}
setInterval(tick, 1000);
上述代码再次优化:
创建一个 React.Component 的 ES6 类
class Clock extends React.Component {
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>现在是 {this.props.date.toLocaleTimeString()}.</h2><!--注意这里的this!!!-->
</div>
);
}
}
function tick() {
ReactDOM.render(
<Clock date={new Date()} />,
document.getElementById('example')
);
}
setInterval(tick, 1000);
例2
var myStyle = {color:'red',textAlign:'center'}
class Name extends React.Component {
render() {
return <h1 style={myStyle}>网站名称:{this.props.name}</h1>;
}
}
class Url extends React.Component {
render() {
return <h1>网站地址:{this.props.url}</h1>;
}
}
class Nickname extends React.Component {
render() {
return <h1>网站地址:{this.props.nickname}</h1>;
}
}
function App(props) {
return (
<div>
<Name name={props.name}/>
<Url url={props.url}/>
<Nickname nickname={props.nickname}/>
</div>
);
}
ReactDOM.render(
<App name={"百度"} url={"https://www.baidu.com"} nickname={"cx"}/>,
document.getElementById('example')
);
3. React JSX
JSX对JavaScript进行了扩展。
推荐在React中使用JSX来描述用户界面。
var myDivElement = <div className="foo" />;
以上代码既不是字符串也不是HTML, 它被称为JSX,JSX就是用来声明React当中的元素。
1. 一些标识符像class和for(等html关键字)不建议作为JSX属性名。
作为替代,React DOM 使用 className 和 htmlFor 来做对应的属性。
2. 自定义属性需要使用 data- 前缀。
使用JSX优点:
1. 执行更快(因为它在编译为JavaScript代码时进行了优化)。
2. 类型安全(在编译过程中就能发现错误)。
3. 使用JSX编写模板更加简单快速。
例1
ReactDOM.render(
<div>
<h1>Hello World</h1>
<h2>JSX</h2>
<p data-myattribute = "somevalue">这是一个很不错的 JavaScript 库!</p>
</div>
,
document.getElementById('example')
);
- 独立文件
helloworld_react.js
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);
helloworld_react.index
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello React!</title>
<script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
<script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
<script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
</head>
<body>
<div id="example"></div>
<script type="text/babel" src="helloworld_react.js"></script>
</body>
</html>
- JavaScript表达式
可以在JSX中使用JavaScript表达式,表达式写在花括号 {} 中。
可以使用三元运算表达式,不能使用 if else 语句。
例1
ReactDOM.render(
<div>
<h1>{1+1}</h1>
</div>
,
document.getElementById('example')
);
例2
ReactDOM.render(
<div>
<h1>{i == 1 ? 'True!' : 'False'}</h1>
</div>
,
document.getElementById('example')
);
- 样式
React 推荐使用内联样式。
可以使用 camelCase 语法来设置内联样式。
React会自动添加px。
例1
var myStyle = {
fontSize: 100,
color: '#FF0000'
};
ReactDOM.render(
<h1 style = {myStyle}>Hello World</h1>,
document.getElementById('example')
);
- 注释
注释需要写在花括号{}中
ReactDOM.render(
<div>
<h1>Hello World</h1>
{/*注释...*/}
</div>,
document.getElementById('example')
);
- 数组
JSX允许在模板中插入数组
例1
var arr = [
<h1>Hello World</h1>,
<h2>Come on</h2>,
];
ReactDOM.render(
<div>{arr}</div>,
document.getElementById('example')
);
3. React 组件
组件只能包含一个顶层标签,否则会报错。
给组件添加属性时, class属性需要写成className ,for属性需要写成htmlFor (因为class和for是 JavaScript的保留字)。
定义组件(2方式)
方式一(函数)
function HelloMessage(props) {
return <h1>Hello World!</h1>;
}
方式二(ES6 class)
class Welcome extends React.Component {
render() {
return <h1>Hello World!</h1>;
}
}
例1
function HelloMessage(props) {
return <h1>Hello World!</h1>;
}
const element = <HelloMessage />;
ReactDOM.render(
element,
document.getElementById('example')
);
例2(传参)
function HelloMessage(props) {
return <h1>Hello {props.name}!</h1>;
}
const element = <HelloMessage name="World"/>;
ReactDOM.render(
element,
document.getElementById('example')
);
例3(复合组件/嵌套组件)
function Name(props) {
return <h1>网站名称:{props.name}</h1>;
}
function Url(props) {
return <h1>网站地址:{props.url}</h1>;
}
function Nickname(props) {
return <h1>网站小名:{props.nickname}</h1>;
}
function App() {
return (
<div>
<Name name="Hello World" />
<Url url="https://www.baidu.com" />
<Nickname nickname="baidu" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('example')
);
4. React State(状态)
React把组件看成是一个状态机。
开发者只需更新组件的state,React会根据新的state重新渲染用户界面(不要操作 DOM)。
数据自顶向下单向流动。
例1
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 (
<div>
<h1>Hello, world!</h1>
<h2>现在是 {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('example')
);
说明:
当 <Clock /> 被传递给 ReactDOM.render() 时,React
首先调用 Clock 组件的 构造函数。
然后调用 Clock 组件的 render()方法
然后更新 DOM,当Clock的输出插入到DOM中时,调用 componentDidMount() 生命周期钩子。
一旦Clock组件被从DOM中移除,会调用 componentWillUnmount() 这个钩子函数
Clock 组件通过使用包含当前时间的对象调用 setState() ,让React 知道状态已经改变,并再次调用 render() 方法来渲染屏幕。
例2
function FormattedDate(props) {
return <h2>现在是 {props.date.toLocaleTimeString()}.</h2>;
}
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 (
<div>
<h1>Hello, world!</h1>
<FormattedDate date={this.state.date} />
</div>
);
}
}
function App() {
return (
<div>
<Clock />
<Clock />
<Clock />
</div>
);
}
ReactDOM.render(<App />, document.getElementById('example'));
5. React Props
props(传递数据)不可变。
state(更新数据)根据与用户交互来改变
1. 通过 组件类的defaultProps属性 设置默认值
例1
function HelloMessage(props) {
return <h1>Hello {props.name}!</h1>;
}
const element = <HelloMessage name="cx"/>;
ReactDOM.render(
element,
document.getElementById('example')
);
例2 (默认值)
class HelloMessage extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
HelloMessage.defaultProps = {
name: 'CX'
};
const element = <HelloMessage/>;
ReactDOM.render(
element,
document.getElementById('example')
);
例3( state和props结合使用 )
class WebSite extends React.Component {
constructor() {
super();
this.state = {
name: "hello",
site: "https://www.baidu.com"
}
}
render() {
return (
<div>
<Name name={this.state.name} />
<Link site={this.state.site} />
</div>
);
}
}
class Name extends React.Component {
render() {
return (
<h1>{this.props.name}</h1>
);
}
}
class Link extends React.Component {
render() {
return (
<a href={this.props.site}>
{this.props.site}
</a>
);
}
}
ReactDOM.render(
<WebSite />,
document.getElementById('example')
);
- Props 验证
React.PropTypes 提供很多验证器来验证传入数据是否有效。
当向 props 传入无效数据时,JavaScript 控制台会抛出警告。
React.PropTypes 在 React v15.5 版本后已经移到了 prop-types 库。
<script src="https://cdn.bootcss.com/prop-types/15.6.1/prop-types.js"></script>
例1(属性title必须是字符串,非字符串类型会自动转换为字符串)
var title = "cx";
// var title = 123;
class MyTitle extends React.Component {
render() {
return (
<h1>Hello, {this.props.title}</h1>
);
}
}
MyTitle.propTypes = {
title: PropTypes.string
};
ReactDOM.render(
<MyTitle title={title} />,
document.getElementById('example')
);
例2(属性title是必须的)
var title = "hello";
// var title = 123;
var MyTitle = React.createClass({
propTypes: {
title: React.PropTypes.string.isRequired,
},
render: function() {
return <h1> {this.props.title} </h1>;
}
});
ReactDOM.render(
<MyTitle title={title} />,
document.getElementById('example')
);
例3 (常用验证器)
MyComponent.propTypes = {
// 可以声明 prop 为指定的 JS 基本数据类型,默认情况,这些数据是可选的
optionalArray: React.PropTypes.array,
optionalBool: React.PropTypes.bool,
optionalFunc: React.PropTypes.func,
optionalNumber: React.PropTypes.number,
optionalObject: React.PropTypes.object,
optionalString: React.PropTypes.string,
// 可以被渲染的对象 numbers, strings, elements 或 array
optionalNode: React.PropTypes.node,
// React 元素
optionalElement: React.PropTypes.element,
// 用 JS 的 instanceof 操作符声明 prop 为类的实例。
optionalMessage: React.PropTypes.instanceOf(Message),
// 用 enum 来限制 prop 只接受指定的值。
optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
// 可以是多个对象类型中的一个
optionalUnion: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number,
React.PropTypes.instanceOf(Message)
]),
// 指定类型组成的数组
optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
// 指定类型的属性构成的对象
optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
// 特定 shape 参数的对象
optionalObjectWithShape: React.PropTypes.shape({
color: React.PropTypes.string,
fontSize: React.PropTypes.number
}),
// 任意类型加上 `isRequired` 来使 prop 不可空。
requiredFunc: React.PropTypes.func.isRequired,
// 不可空的任意类型
requiredAny: React.PropTypes.any.isRequired,
// 自定义验证器。如果验证失败需要返回一个 Error 对象。不要直接使用 `console.warn` 或抛异常,因为这样 `oneOfType` 会失效。
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error('Validation failed!');
}
}
}
}
6. 事件
React元素的事件处理和DOM元素类似。
不同点如下:
1. React事件绑定属性的命名采用驼峰式写法,而不是小写。
2. React如果采用JSX语法,需要传入一个函数作为事件处理函数,而不是一个字符串(DOM 元素的写法) 。
3. React必须明确的使用 preventDefault,不能使用返回false的方式阻止默认行为。
React通常仅需要在元素初始渲染时提供监听器, 不需要使用addEventListener为一个已创建的 DOM 元素添加监听器。
传参(2方式)
方式一
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
通过箭头函数的方式,事件对象e必须显式的进行传递。
方式二
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
通过 bind 的方式,事件对象e以及更多的参数将会被隐式的进行传递。
在类组件中定义的监听函数中,事件对象e作为参数 要排在最后。
this
React JSX回调函数中的this必须谨慎对待,类的方法默认是不会绑定 this 的。
如果忘记绑定 this.handleClick 并把它传入 onClick, 当调用这个函数的时候 this 的值会是 undefined。
bind很烦,有两种方式可以解决:
1. 可以使用属性初始化器来正确的绑定回调函数
2. 在回调函数中使用 箭头函数(该方式存在缺陷)
================HTML===============
<button onclick="activateLasers()">
激活按钮
</button>
================React===============
<button onClick={activateLasers}>
激活按钮
</button>
================HTML===============
<a href="#" onclick="console.log('点击链接'); return false">
点我
</a>
================React===============
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('链接被点击');
}
return (
<a href="#" onClick={handleClick}>
点我
</a>
);
}
例1 ( ES6 class 语法来定义组件 )
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>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('example')
);
例2(bind传参)
class Popper extends React.Component{
constructor(){
super();
this.state = {name:'Hello world!'};
}
preventPop(name, e){ //事件对象e要放在最后
e.preventDefault();
alert(name);
}
render(){
return (
<div>
<p>hello</p>
{/* 通过 bind() 方法传递参数。 */}
<a href="https://reactjs.org" onClick={this.preventPop.bind(this,this.state.name)}>Click</a>
</div>
);
}
}
例3(属性初始化器)
class LoggingButton extends React.Component {
// 这个语法确保了 `this` 绑定在 handleClick 中
// 这里只是一个测试
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
例4 (在回调函数中使用 箭头函数)
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// 这个语法确保了 `this` 绑定在 handleClick 中
return (
<button onClick={(e) => this.handleClick(e)}>
Click me
</button>
);
}
}
缺陷:
每次 LoggingButton 渲染的时候都会创建一个不同的回调函数。
在大多数情况下,这没有问题。然而如果这个回调函数作为一个属性值传入低阶组件,这些组件可能会进行额外的重新渲染。我们通常建议在构造函数中绑定或使用属性初始化器语法来避免这类性能问题。
7. React 条件渲染
首先 使用if、条件运算符来创建表示当前状态的元素。
然后 通过改变状态进行条件渲染。
例1
function UserGreeting(props) {
return <h1>欢迎回来!</h1>;
}
function GuestGreeting(props) {
return <h1>请先注册。</h1>;
}
function Greeting(props) {
const isLoggedIn = props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
ReactDOM.render(
// 尝试修改 isLoggedIn={true}:
<Greeting isLoggedIn={false} />,
document.getElementById('example')
);
例2 ( 通过改变状态 进行条件渲染 )
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 = null;
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('example')
);
- &&
在JavaScript中
true && expression 总是返回 expression,
false && expression 总是返回 false。
因此
如果条件是 true,&& 右侧的元素就会被渲染,
如果是 false,React 会忽略并跳过它。
例1
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
您有 {unreadMessages.length} 条未读信息。
</h2>
}
</div>
);
}
const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
<Mailbox unreadMessages={messages} />,
document.getElementById('example')
);
- condition ? true : false
三目运算符
例1
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn ? (
<LogoutButton onClick={this.handleLogoutClick} />
) : (
<LoginButton onClick={this.handleLoginClick} />
)}
</div>
);
}
- 阻止组件渲染
组件的render方法返回null。
这并不会影响该组件生命周期方法的回调(componentWillUpdate、componentDidUpdate等方法依然可以被调用)。
例1
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return (
<div className="warning">
警告!
</div>
);
}
class Page extends React.Component {
constructor(props) {
super(props);
this.state = {showWarning: true}
this.handleToggleClick = this.handleToggleClick.bind(this);
}
handleToggleClick() {
this.setState(prevState => ({
showWarning: !prevState.showWarning
}));
}
render() {
return (
<div>
<WarningBanner warn={this.state.showWarning} />
<button onClick={this.handleToggleClick}>
{this.state.showWarning ? '隐藏' : '显示'}
</button>
</div>
);
}
}
ReactDOM.render(
<Page />,
document.getElementById('example')
);
8. React map & Keys
map
map()
key
可以在DOM中的某些元素被增加或删除的时候,帮助React识别哪些元素发生了变化。
每个列表元素li必须分配一个 key,不然会出现警告 a key should be provided for list items。
key应该是独一无二的(通常为数据的id),但不需要全局唯一。
key 会作为给 React 的提示,但不会传递给组件。如果组件中需要使用和key相同的值,请将其作为属性传递
不建议使用索引来进行排序,因为这会导致渲染变得很慢
例1
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
ReactDOM.render(
<ul>{listItems}</ul>,
document.getElementById('example')
);
在没有确定的 id 时,使用index
const todoItems = todos.map((todo, index) =>
<li key={index}>
{todo.text}
</li>
);
例2
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('example')
);
例3 (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('example')
);
例4
function Blog(props) {
const sidebar = (
<ul>
{props.posts.map((post) =>
<li key={post.id}>
{post.title}
</li>
)}
</ul>
);
const content = props.posts.map((post) =>
<div key={post.id}>
<h3>{post.title}</h3>
<p>{post.content}</p>
</div>
);
return (
<div>
{sidebar}
<hr />
{content}
</div>
);
}
const posts = [
{id: 1, title: 'Hello World', content: 'Welcome to learning React!'},
{id: 2, title: 'Installation', content: 'You can install React from npm.'}
];
ReactDOM.render(
<Blog posts={posts} />,
document.getElementById('example')
);
例5(JSX)
function NumberList(props) {
const numbers = props.numbers;
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
)}
</ul>
);
}
9. React 组件 API
- 设置状态:setState
React触发UI更新的主要方法。
setState(object nextState[, function callback])
nextState 新状态(该状态会和当前的state合并)
callback 回调(可选,setState成功且组件重新渲染后回调)
合并nextState和当前state,并重新渲染组件。合并???
setState() 并不会立即改变this.state,而是创建一个即将处理的state。
setState() 并不一定是同步的,为了提升性能React会批量执行state和DOM渲染。
不能在组件内部通过this.state修改状态,因为该状态会在调用setState()后被替换。
setState()总是会触发一次组件重绘,除非在shouldComponentUpdate()中实现了一些条件渲染逻辑。
- 替换状态:replaceState
replaceState()方法与setState()类似
但是只会保留nextState中状态,原state不在nextState中的状态都会被删除。
replaceState(object nextState[, function callback])
nextState,新状态(该状态会替换当前state)
callback,回调(可选,replaceState成功且组件重新渲染后调用)
- 设置属性:setProps
设置组件属性,并重新渲染组件。
更新组件 : 可以 调用React.render(),也可以通过setProps()方法改变组件属性。触发组件重新渲染。
setProps(object nextProps[, function callback])
nextProps,新属性(会和当前的props合并)
callback,回调(可选,setProps成功且组件重新渲染后调用)
props相当于组件的数据流,它总是会从父组件向下传递至所有的子组件中。
- 替换属性:replaceProps
replaceProps()方法与setProps类似,但会删除原有 props。
replaceProps(object nextProps[, function callback])
nextProps,新属性(会替换当前的props)
callback,回调(可选,replaceProps成功且组件重新渲染后调用)
- 强制更新:forceUpdate
使组件调用自身的render()方法重新渲染组件,组件的子组件也会调用自己的render()。
应尽量避免使用forceUpdate()。
forceUpdate([function callback])
callback,回调(可选,在组件render()方法调用后调用)
组件重新渲染时,依然会读取this.props和this.state,如果状态没有改变,那么React只会更新DOM。
forceUpdate()方法适用于this.props和this.state修改之后的组件重绘,通过该方法通知React需要调用render()。
- 获取DOM节点:findDOMNode
DOMElement findDOMNode()
返回:DOM元素DOMElement
如果组件已经挂载到DOM中,该方法返回对应的本地浏览器 DOM 元素。
当render返回null 或 false时,this.findDOMNode()也会返回null。
从DOM 中读取值的时候,该方法很有用,如:获取表单字段的值和做一些 DOM 操作
- 判断组件挂载状态:isMounted
使用该方法保证了setState()和forceUpdate()在异步场景下的调用不会出错
bool isMounted()
返回:组件是否已挂载到DOM中
例1
class Counter extends React.Component{
constructor(props) {
super(props);
this.state = {clickCount: 0};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(function(state) {
return {clickCount: state.clickCount + 1};
});
}
render () {
return (<h2 onClick={this.handleClick}>点我!点击次数为: {this.state.clickCount}</h2>);
}
}
ReactDOM.render(
<Counter />,
document.getElementById('example')
);
10. React 组件生命周期
- 组件 3状态
Mounting
已插入真实 DOM
Updating
正在被重新渲染
Unmounting
`已移出真实 DOM
- 组件 生命周期方法
componentWillMount
渲染前调用,在客户端也在服务端。
componentDidMount
第一次渲染后调用,只在客户端。
调用之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。
如果想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异步操作阻塞)。
componentWillReceiveProps
在组件接收到一个新的 prop (更新后)时被调用。
这个方法在初始化render时不会被调用。
shouldComponentUpdate
在组件接收到新的props或者state时被调用。返回一个布尔值。
在初始化时或者使用forceUpdate时不被调用。
可以在确认不需要更新组件时使用。
componentWillUpdate
在组件接收到新的props或者state但还没有render时被调用。
在初始化时不会被调用。
componentDidUpdate
在组件完成更新后立即调用。
在初始化时不会被调用。
componentWillUnmount
在组件从 DOM 中移除之前立刻被调用。
例1
class Hello extends React.Component {
constructor(props) {
super(props);
this.state = {opacity: 1.0};
}
componentDidMount() {
this.timer = setInterval(function () {
var opacity = this.state.opacity;
opacity -= .05;
if (opacity < 0.1) {
opacity = 1.0;
}
this.setState({
opacity: opacity
});
}.bind(this), 100);
}
render () {
return (
<div style={{opacity: this.state.opacity}}>
Hello {this.props.name}
</div>
);
}
}
ReactDOM.render(
<Hello name="world"/>,
document.body
);
例2
class Button extends React.Component {
constructor(props) {
super(props);
this.state = {data: 0};
this.setNewNumber = this.setNewNumber.bind(this);
}
setNewNumber() {
this.setState({data: this.state.data + 1})
}
render() {
return (
<div>
<button onClick = {this.setNewNumber}>INCREMENT</button>
<Content myNumber = {this.state.data}></Content>
</div>
);
}
}
class Content extends React.Component {
componentWillMount() {
console.log('Component WILL MOUNT!')
}
componentDidMount() {
console.log('Component DID MOUNT!')
}
componentWillReceiveProps(newProps) {
console.log('Component WILL RECEIVE PROPS!')
}
shouldComponentUpdate(newProps, newState) {
return true;
}
componentWillUpdate(nextProps, nextState) {
console.log('Component WILL UPDATE!');
}
componentDidUpdate(prevProps, prevState) {
console.log('Component DID UPDATE!')
}
componentWillUnmount() {
console.log('Component WILL UNMOUNT!')
}
render() {
return (
<div>
<h3>{this.props.myNumber}</h3>
</div>
);
}
}
ReactDOM.render(
<div>
<Button />
</div>,
document.getElementById('example')
);
11. React AJAX
获取网络数据:
componentDidMount中使用Ajax来获取,
可以将获取的数据存储在state中,再用this.setState方法重新渲染UI。
注意:
1. 组件卸载前使用 componentWillUnmount 来取消未完成的请求
例1
class UserGist extends React.Component {
constructor(props) {
super(props);
this.state = {username: '', lastGistUrl: ''};
}
componentDidMount() {
this.serverRequest = $.get(this.props.source, function (result) {
var lastGist = result[0];
this.setState({
username: lastGist.owner.login,
lastGistUrl: lastGist.html_url
});
}.bind(this));
}
componentWillUnmount() {
this.serverRequest.abort();
}
render() {
return (
<div>
{this.state.username} 用户最新的 Gist 共享地址:
<a href={this.state.lastGistUrl}>{this.state.lastGistUrl}</a>
</div>
);
}
}
ReactDOM.render(
<UserGist source="https://api.github.com/users/octocat/gists" />,
document.getElementById('example')
);
12. React 表单与事件
在HTML当中,像 <input>, <textarea>, 和 <select> 这类表单元素会维持自身状态,并根据用户输入进行更新。
在React中,可变的状态通常保存在组件的状态属性中,并且只能用 setState() 方法进行更新。
例1
class HelloMessage extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'Hello World!'};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
var value = this.state.value;
return <div>
<input type="text" value={value} onChange={this.handleChange} />
<h4>{value}</h4>
</div>;
}
}
ReactDOM.render(
<HelloMessage />,
document.getElementById('example')
);
例2
class Content extends React.Component {
render() {
return <div>
<input type="text" value={this.props.myDataProp} onChange={this.props.updateStateProp} />
<h4>{this.props.myDataProp}</h4>
</div>;
}
}
class HelloMessage extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'Hello World!'};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
var value = this.state.value;
return <div>
<Content myDataProp = {value}
updateStateProp = {this.handleChange}></Content>
</div>;
}
}
ReactDOM.render(
<HelloMessage />,
document.getElementById('example')
);
例3(Select 下拉菜单)
在 React 中,不使用 selected 属性,而在根 select 标签上用 value 属性来表示选中项。
class FlavorForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'coconut'};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('Your favorite flavor is: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
选择您最喜欢的网站
<select value={this.state.value} onChange={this.handleChange}>
<option value="gg">Hello</option>
<option value="rn">Baidu</option>
<option value="tb">Taobao</option>
<option value="fb">Google</option>
</select>
</label>
<input type="submit" value="提交" />
</form>
);
}
}
ReactDOM.render(
<FlavorForm />,
document.getElementById('example')
);
例4(多个input元素)
当处理多个input元素时,可以通过给每个input元素添加一个name属性,来让处理函数根据 event.target.name 的值来选择做什么。
class Reservation extends React.Component {
constructor(props) {
super(props);
this.state = {
isGoing: true,
numberOfGuests: 2
};
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
return (
<form>
<label>
是否离开:
<input
name="isGoing"
type="checkbox"
checked={this.state.isGoing}
onChange={this.handleInputChange} />
</label>
<br />
<label>
访客数:
<input
name="numberOfGuests"
type="number"
value={this.state.numberOfGuests}
onChange={this.handleInputChange} />
</label>
</form>
);
}
}
例5 ( onClick事件 )
class HelloMessage extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'Hello Runoob!'};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({value: 'hello world'})
}
render() {
var value = this.state.value;
return <div>
<button onClick={this.handleChange}>点我</button>
<h4>{value}</h4>
</div>;
}
}
ReactDOM.render(
<HelloMessage />,
document.getElementById('example')
);
例6
class Content extends React.Component {
render() {
return <div>
<button onClick = {this.props.updateStateProp}>点我</button>
<h4>{this.props.myDataProp}</h4>
</div>
}
}
class HelloMessage extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'Hello Runoob!'};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({value: 'hello world'})
}
render() {
var value = this.state.value;
return <div>
<Content myDataProp = {value}
updateStateProp = {this.handleChange}></Content>
</div>;
}
}
ReactDOM.render(
<HelloMessage />,
document.getElementById('example')
);
13. React Refs
Ref可以用来绑定到render()输出的任何组件上,然后就可以使用ref来获取组件的引用
例1
class MyComponent extends React.Component {
handleClick() {
// 使用原生的 DOM API 获取焦点
this.refs.myInput.focus();
}
render() {
// 当组件插入到 DOM 后,ref 属性添加一个组件的引用于到 this.refs
return (
<div>
<input type="text" ref="myInput" />
<input
type="button"
value="点我输入框获取焦点"
onClick={this.handleClick.bind(this)}
/>
</div>
);
}
}
ReactDOM.render(
<MyComponent />,
document.getElementById('example')
);
网友评论