React文档总结
元素渲染
更新元素渲染
计时器例子
function tick(){
const element = (
Hello, World!
It is {new Date().toLocaleTimeString()}.
);
ReactDOM.render(
element,
document.getElementById('root')
);
}
setInterval(tick, 1000)
react只会更新必要的部分,上面计时器的例子中,只会更新时间,而element中的Hello, World等则不会更新。
组建 & Props
自定义组件
自定义组件包括函数定义组件和类定义组件,下面例子中两种定义的效果是相同的。
// 函数定义组件
// function Welcome(props){
// return Hello, {props.name}
;
// }
// 类定义组件
class Welcome extends React.Component {
render() {
return Hello, {this.props.name}
;
}
}
const element = ;
ReactDOM.render(
element,
document.getElementById('root')
);
组合组件
组件可以在返回值中引用其它组件,从而实现组件之间的组合。
// 类定义组件
class Welcome extends React.Component {
render() {
return Hello, {this.props.name}
;
}
}
class App extends React.Component {
render(){
return (
);
}
}
ReactDOM.render(
,
document.getElementById('root')
);
提取组件
通过提取组件可以将一个大的组件拆分成由许多小的组件合并而成。
原始组件:
function Comment(props) {
return (
{props.author.name}
{props.text}
{formatData(props.date)}
);
}
拆分后:
// Avater组件
function Avater(props) {
return (
);
}
// UserInfo组件
function UserInfo(props) {
return (
{props.user.name}
);
}
// 修改后的Comment
function Comment(props) {
return (
{props.text}
{formatData(props.date)}
);
}
Props只能用来读取,不能被修改。
State & 生命周期
生命周期
用组件的生命周期改写上面的计时器功能
class Clock extends React.Component{
constructor(props) {
super(props);
this.state = {date: new Date()};
}
// 组件插入到DOM中启动定时器
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
// 组件移出DOM时去除定时器
componentWillMount() {
clearInterval(this.timerID);
}
tick(){
this.setState({
date: new Date()
});
}
render(){
return (
Hello, World!
It is {this.state.date.toLocaleTimeString()}.
);
}
}
ReactDOM.render(
,
document.getElementById('root')
);
正确使用状态
不可直接更新状态
以下代码不会重新渲染组件
this.state.comment = "Hello"
应当使用setState
状态更新可能是异步的
React可以将多个setState()调用合并成一个调用来提高性能。所以状态更新是异步的,不能根据上一个状态值来计算下一个状态值。
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
// Correct
this.setState((prevState, props) => ({
counter: prevState.counter + props.increment
}));
// Correct
this.setState(function(prevState, props) {
return (
counter: prevState.counter + props.increment
);
});
事件处理
React事件绑定属性命名采用驼峰写法,需要传入一个事件处理函数,而不是一个字符串。
// HTML
// React
在React中不能使用返回false的方式阻止默认行为,而要使用preventDefault。
// HTML
Click me
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
Click me
);
}
使用this绑定点击事件
class Toggle extends React.Component {
constructor(props){
super(props);
this.state = {
isToggleOn: true
};
// This blinding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render(){
return (
);
}
}
ReactDOM.render(
,
document.getElementById('root')
);
使用如下两种写法可以不用使用bind绑定this
handleClick = () => {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render(){
return (
);
}
向事件处理程序传递参数
有两种方式可以向事件处理程序传递参数
下面例子使用bind()传递参数
class Popper extends React.Component{
constructor(){
super();
this.state = {name:'Hello world'};
}
preventPop(name, e){ // 事件对象e要放在最后
e.preventDefault();
alert(name);
}
render(){
return (
hello
{/* Pass params via bind() method. */}
Click
);
}
}
ReactDOM.render(
,
document.getElementById('root')
);
条件渲染
与运算符 &&
function Mailbox(props) {
const unreadMessages = props.unreadMessages;
return (
Hello!
{unreadMessages.length > 0 &&
You have {unreadMessages.length} unread message.
}
);
}
const message = ['React', 'Re: React', 'Re:Re: React']
ReactDOM.render(
,
document.getElementById('root')
);
在JavaScript中,true && expression 总是返回expression,而 false && expression 总是返回false。
阻止组件渲染
function WarningBanner(props) {
if(!props.warn) {
return null;
}
return (
Warning!
);
}
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 (
);
}
}
ReactDOM.render(
,
document.getElementById('root')
);
列表 & Keys
渲染多个组件
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
{number}
);
ReactDOM.render(
{listItems}
,
document.getElementById('root')
);
基础列表组件
function NumberList(props) {
const number = props.numbers;
const listItems = numbers.map((number) =>
{number}
);
return (
{listItems}
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
,
document.getElementById('root')
);
注意,如果不加key属性,会得到一个警告 a key should be provided for list items。意思是每个列表元素都应该分配一个确定的标识。
Keys
一个元素的key最好是这个元素在列表中拥有的独一无二的字符串。通常我们使用来自数据的id作为元素的key。
const todoItems = todos.map((todo) =>
{todo.text}
);
当元素没有确定的id时,可以使用它的序列号索引index作为key
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
{todo.text}
);
元素的key在他的兄弟元素之间应该唯一,但是不需要全局唯一,也就是说不同列表的元素可以使用相同的key。
key会作为React的提示,但是不会传递给组件。如果组件中需要使用和key相同的值,可将其作为属性传递:
// Post组件可以读出props.id, 但是不能读出props.key
const content = posts.map((post) =>
);
表单
select
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 (
);
}
}
ReactDOM.render(
,
document.getElementById('root')
);
在React中并不使用之前的selected属性,而是在根select标签上用value来表示选中项。
多个输入
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 (
);
}
}
ReactDOM.render(
,
document.getElementById('root')
);
上面通过给元素一个name属性,让函数根据event.target.name的值来选择做什么,这样就可以在一个处理函数中处理多个input元素。
状态提升
状态提升是指多个组件共用一个值时,每次改变值时不进行双向绑定,而是将该值提升到上一级的共同组件中。
组合 VS 继承
包含关系
使用children属性将子元素直接传递到输出。
function FancyBorder(props) {
return (
{props.children}
);
}
function WelcomeDialog() {
return (
Welcome
Thank you for visiting our spacecraft!
);
}
ReactDOM.render(
,
document.getElementById('root')
);
除此之外,也可以使用自己约定的属性,在组件中需要多个入口的情况下非常有用。
function SplitPane(props) {
return (
{props.left}
{props.right}
);
}
function App() {
return (
} right={ } />
);
}
ReactDOM.render(
,
document.getElementById('root')
);
特殊实例
一个组件可以是另一个组件的实例。
function FancyBorder(props) {
return (
{props.children}
);
}
function Dialog(props) {
return(
{props.title}
{props.message}
);
}
function WelcomeDialog() {
return (
);
}
ReactDOM.render(
,
document.getElementById('root')
);
组合对于定义类的组件同样适用。
function FancyBorder(props) {
return (
{props.children}
);
}
function Dialog(props) {
return(
{props.title}
{props.message}
);
}
class SignUpDialog extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSignUp = this.handleSignUp.bind(this);
this.state = {login: ''};
}
render() {
return (
);
}
handleChange(e) {
this,this.setState({login: e.target.value});
}
handleSignUp() {
alert(`Welcome aboard, ${this.state.login}!`);
}
}
ReactDOM.render(
,
document.getElementById('root')
);