0
点赞
收藏
分享

微信扫一扫

React学习笔记,文档中的大部分代码都有注释


React主要用于构建UI。你可以在React里传递多种类型的参数,如声明代码,帮助你渲染出UI、也可以是静态的HTML DOM元素、也可以传递动态变量、甚至是可交互的应用组件。

1.使用(以下源码如果复制使用请去掉注释)

导入三个js库
<script src="../js/react.development.js"></script>
<script src="../js/react-dom.development.js"></script>
<script src="../js/babel.min.js"></script>

2.元素渲染

代码示例:

<div id="example"></div>
<script type="text/babel">//注意这里是type=’text/babel’
    function tick() {
        const element = (
            <div>
                <h1>Hello, world!</h1>
                <h2>现在是 {new Date().toLocaleTimeString()}.</h2>
            </div>
        );
        ReactDOM.render(
            element,//上方const对象,也

可以直接将element的内容写在这里
            document.getElementById('example')//放入该dom中
        );
    }
    setInterval(tick, 1000);//定时器,每隔一秒钟调用一次
</script>

 

3.jsx

ReactDOM.render(

<h1>Hello, world!</h1>,.

document.getElementById('example')

);

我们可以在以上代码中嵌套多个 HTML 标签,需要使用一个 div 元素包裹它,实例中的 p 元素添加了自定义属性 data-myattribute,添加自定义属性需要使用 data- 前缀。

ReactDOM.render(

<div>

<h1>菜鸟教程</h1>

<h2>欢迎学习 React</h2>

<p data-myattribute = "somevalue">这是一个很不错的 JavaScript 库!</p> </div> , document.getElementById('example')

);

我们可以在jsx中使用js表达式,表达式写在{}中

ReactDOM.render(
    <div>
        <h1>{1+1}</h1>
    </div>
    ,
    document.getElementById('example')
)

 

jsx中不能使用if  else 但是可以使用三元运算符

 

var i=1;
ReactDOM.render(
    <div>
        <h1>{i == 1 ? 'True!' : 'False'}</h1>
    </div>
    ,
    document.getElementById('example')
)

 

React推荐使用内联样式,使用camelCase 语法来设置内联样式. React 会在指定元素数字后自动添加 px 。以下实例演示了为 h1 元素添加 myStyle 内联样式:

//内联样式
    var myStyle = {
        fontSize: 100,
        color: '#FF0000'
    };
    ReactDOM.render(
        <h1 style = {myStyle}>菜鸟教程</h1>,
        document.getElementById('example2')
    );

 

注释需要写在花括号中,实例如下:

 

jsx允许在模版中插入数组,数组会自动展开所有成员

var arr = [
    <h1>菜鸟教程</h1>,
    <h2>学的不仅是技术,更是梦想!</h2>,
];
ReactDOM.render(
    <div>{arr}</div>,
    document.getElementById('example3')
);

 

React 可以渲染 HTML 标签 (strings) 或 React 组件 (classes)。

要渲染 HTML 标签,只需在 JSX 里使用小写字母的标签名。

var myDivElement = <div className="foo"/>
ReactDOM.render(myDivElement, document.getElementById('example4'));

 

要渲染 React 组件,只需创建一个大写字母开头的本地变量。

var MyComponent = React.createClass({/*...*/});//创建的组件
var myElement = <MyComponent someProperty={true} />;
ReactDOM.render(myElement, document.getElementById('example5'));

 

4.组件

定义组件

<script type="text/babel">
    function HelloMessage(props) {
        return <h1>Hello World!</h1>;
    }
    const element = <HelloMessage/>;//定义组件
    ReactDOM.render(
        element,
        document.getElementById('example')
    );
</script>

 

如果需要向组件传递参数

function HelloMessage(props) {
    return <h1>Hello World!{props.name}</h1>;//获取参数
}
const element = <HelloMessage name="123"/>;//设置参数

 

参数传递需要注意:如果参数名为class或者for则不行,class 属性需要写成 className ,for 属性需要写成 htmlFor 

 

复合组件  类似方法调用方法

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="菜鸟教程" />//调用上方的方法
            <Url url="http://www.runoob.com" />//调用上方的方法
            <Nickname nickname="Runoob" />//调用上方的方法
        </div>
    );
}
ReactDOM.render(
    <App />,//直接调用,也可以创建const对象调用App方法然后将const对象放在这里
    document.getElementById('example2')
);

 

5.React State(状态)

React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。

React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。

以下实例创建一个名称扩展为 React.Component 的 ES6 类,在 render() 方法中使用 this.state 来修改当前的时间。

添加一个类构造函数来初始化状态 this.state,类组件应始终使用 props 调用基础构造函数。

class Clock extends React.Component {

constructor(props) {

super(props); this.state = {date: new Date()};

}

render() {

return (

<div> <h1>Hello, world!</h1> <h2>现在是 {this.state.date.toLocaleTimeString()}.</h2> </div>

); } }

ReactDOM.render(

<Clock />, document.getElementById('example')

);

在具有许多组件的应用程序中,在销毁时释放组件所占用的资源非常重要。

每当 Clock 组件第一次加载到 DOM 中的时候,我们都想生成定时器,这在 React 中被称为挂载

同样,每当 Clock 生成的这个 DOM 被移除的时候,我们也会想要清除定时器,这在 React 中被称为卸载

我们可以在组件类上声明特殊的方法,当组件挂载或卸载时,来运行一些代码:

class Clock extends React.Component {
    constructor(props) {//构造函数
        super(props);
        this.state = {date: new Date()};
    }
    componentDidMount() {//生命周期钩子,在组件输出到 DOM 后会执行
        this.timerID = setInterval(//定时器,一秒执行一次
            () => this.tick(), 1000
        );
    }
    componentWillUnmount() {//生命周期钩子
        clearInterval(this.timerID);//this.timerID为计算器的id,卸载计算器
    }
    tick() {
        this.setState({//更新state(状态)
            date: new Date()
        });
    }
    render() {
        return (
            <div>
                <h1>Hello, world!</h1>
                <h2>现在是 {this.state.date.toLocaleTimeString()}.</h2>
            </div>
        );
    }
}
ReactDOM.render(
    <Clock/>,
    document.getElementById('example')
);

代码的执行顺序:

  1. 当 <Clock /> 被传递给 ReactDOM.render() 时,React 调用 Clock 组件的构造函数。 由于 Clock 需要显示当前时间,所以使用包含当前时间的对象来初始化 this.state 。 我们稍后会更新此状态。

 

2.React 然后调用 Clock 组件的 render() 方法。这是 React 了解屏幕上应该显示什么内容,然后 React 更新 DOM 以匹配 Clock 的渲染输出。

3.当 Clock 的输出插入到 DOM 中时,React 调用 componentDidMount() 生命周期钩子。 在其中,Clock 组件要求浏览器设置一个定时器,每秒钟调用一次 tick()。

 

4.浏览器每秒钟调用 tick() 方法。 在其中,Clock 组件通过使用包含当前时间的对象调用 setState() 来调度UI更新。 通过调用 setState() ,React 知道状态已经改变,并再次调用 render() 方法来确定屏幕上应当显示什么。 这一次,render() 方法中的 this.state.date 将不同,所以渲染输出将包含更新的时间,并相应地更新 DOM。

 

5.一旦 Clock 组件被从 DOM 中移除,React 会调用 componentWillUnmount() 这个钩子函数,定时器也就会被清除。

 

数据自顶向下流动

父组件或子组件都不能知道某个组件是有状态还是无状态,并且它们不应该关心某组件是被定义为一个函数还是一个类。

这就是为什么状态通常被称为局部或封装。 除了拥有并设置它的组件外,其它组件不可访问。

以下实例中 FormattedDate 组件将在其属性中接收到 date 值,并且不知道它是来自 Clock 状态、还是来自 Clock 的属性、亦或手工输入

function FormattedDate(props) {//和上方代码的区别就是这里加了个方法,然后render在标签中调用了这个方法并传了一个date参数
    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>
        );
    }
}
ReactDOM.render(
    <Clock/>,
    document.getElementById('example')
);

这通常被称为自顶向下或单向数据流。 任何状态始终由某些特定组件所有,并且从该状态导出的任何数据或 UI 只能影响树中下方的组件。

如果你想象一个组件树作为属性的瀑布,每个组件的状态就像一个额外的水源,它连接在一个任意点,但也流下来。

所有的组件都是隔离的,你可以通过再新建一个function,里面return多个</Clock> ,然后在ReactDOM.render中直接放入你新建的function,那么也将会有n个显示.

在 React 应用程序中,组件是有状态还是无状态被认为是可能随时间而变化的组件的实现细节。

我们可以在有状态组件中使用无状态组件,也可以在无状态组件中使用有状态组件。

 

关于挂载时的 setInterval 中调用 tick() 的方式 ()=>this.tick():

1.()=>this.tick()

()=>this.tick() 是 ES6 中声明函数的一种方式,叫做箭头函数表达式,引入箭头函数有两个方面的作用:更简短的函数并且不绑定 this。

var f = ([参数]) => 表达式(单一)

// 等价于以下写法

var f = function([参数]){ return 表达式;}

基本语法为:

(参数1, 参数2, …, 参数N) => { 函数声明 }

(参数1, 参数2, …, 参数N) => 表达式(单一)//相当于:(参数1, 参数2, …, 参数N) =>{ return 表达式; }

// 当只有一个参数时,圆括号是可选的:

(单一参数) => {函数声明}单一参数 => {函数声明}

// 没有参数的函数应该写成一对圆括号。

() => {函数声明}

根据以上概念,尝试将 setInterval 中调用 tick() 的方式改为通常声明方式:

this.timerID = setInterval(function(){

return this.tick();//但是会报错,tick() 不是一个方法。

},1000);

2.this.tick();

this.tick() 中的 this 指代的是 function,而不是我们想要的指代所在的组件类 Clock,所以我们要想办法让 this 能被正常指代。这里我们采用围魏救赵的办法:

let that = this;

this.timerID = setInterval(function () {

return that.tick();},1000);

在闭包函数的外部先用 that 引用组件 Clock 中挂载组件方法 componentDidMount() 中 this 的值,然后在 setInterval 中闭包函数中使用that,that 无法找到声明,就会根据作用域链去上级(上次层)中继承 that,也就是我们引用的组件类 Clock 中的 this。

到此为止,将 () => this.tick()等价代换为了我们熟悉的形式。

 

6.Props

state 和 props 主要的区别在于 props 是不可变的,而 state 可以根据与用户交互来改变。这就是为什么有些容器组件需要定义 state 来更新和修改数据。 而子组件只能通过 props 来传递数据。

function HelloMessage(props) {
    return <h1>Hello {props.name}!</h1>;
}
const element = <HelloMessage name="Runoob"/>;//这个name上方的方法通过props.name获取
ReactDOM.render(
    element,
    document.getElementById('example')
);

 

创建默认的props

//创建默认的props
class HelloMessage2 extends React.Component {
    render() {
        return (
            <h1>Hello, {this.props.name}</h1>
        );
    }
}
HelloMessage2.defaultProps = {//赋予默认的属性
    name: 'Runoob'
};
const element2 = <HelloMessage2/>;//调用
ReactDOM.render(
    element2,
    document.getElementById('example2')
);

 

State和Props组合一起使用

以下实例演示了如何在应用中组合使用 state 和 props 。我们可以在父组件中设置 state, 并通过在子组件上使用 props 将其传递到子组件上。在 render 函数中, 我们设置 name 和 site 来获取父组件传递过来的数据。

//stateprops组合使用
class WebSite extends React.Component {
    constructor() {
        super();
        this.state = {
            name: "菜鸟教程",//改变状态,在构造函数中就给予参数值
            site: "https://www.runoob.com"
        }
    }
    render() {
        return (
            <div>
                <Name name={this.state.name} /><!--调用下方的class-->
                <Link site={this.state.site} /><!--调用下方的class-->
            </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('example3')
);

 

props验证

在15版本后,转移到prop-types库中,首先导入这个库

<script src="../js/prop-types.js"></script>

代码示例:

// var title = "菜鸟教程";
var title = 123;//因为这个变量不是string所有浏览器会报错
class MyTitle extends React.Component {
    render() {
        return (
            <h1>Hello, {this.props.title}</h1>
        );
    }
}
MyTitle.propTypes = {//验证,为string
    title: PropTypes.string
};
ReactDOM.render(
    <MyTitle title={title} />,
    document.getElementById('example4')
);

 

更多验证器说明:

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!');
        }
    }
}
}

 

7.事件处理

React 元素的事件处理和 DOM 元素类似。但是有一点语法上的不同:

  • React 事件绑定属性的命名采用驼峰式写法,而不是小写。
  • 如果采用 JSX 的语法你需要传入一个函数作为事件处理函数,而不是一个字符串(DOM 元素的写法)

 

html中的写法为:

<button onclick="activateLasers()">激活按钮</button>

React中的写法为

<button onClick={activateLasers}> 激活按钮</button>

在React中不能使用返回false的方式阻止行为,你必须使用preventDefault

在html中的写法:

<a href="#" onclick="console.log('点击链接'); return false"> 点我</a>

示例:

 

class Toggle extends React.Component {
    constructor(props) {
        super(props);
        this.state = {isToggleOn: true};

this.state={name:’123’}//可以在这里传参数,然后在下方的render的代码中调用
        // 这边绑定是必要的,这样 `this` 才能在回调函数中使用
        this.handleClick = this.handleClick.bind(this);
    }
    handleClick() {
        this.setState(prevState => ({//点击时触发的事件
            isToggleOn: !prevState.isToggleOn//赋予相反的属性
        }));
    }
    render() {
        return (
            <button onClick={this.handleClick}>
                {this.state.isToggleOn ? 'ON'+ ( this.state.name)//调用上方的name参数 

 : 'OFF'}<!--三元运行符判断-->

            </button>
        );
    }
}
ReactDOM.render(
    <Toggle />,
    document.getElementById('example')
);

 

你必须谨慎对待 JSX 回调函数中的 this,类的方法默认是不会绑定 this 的。如果你忘记绑定 this.handleClick 并把它传入 onClick, 当你调用这个函数的时候 this 的值会是 undefined。

这并不是 React 的特殊行为;它是函数如何在 JavaScript 中运行的一部分。通常情况下,如果你没有在方法后面添加 () ,例如 onClick={this.handleClick},你应该为这个方法绑定 this。

如果使用 bind 让你很烦,这里有两种方式可以解决。如果你正在使用实验性的属性初始化器语法,你可以使用属性初始化器来正确的绑定回调函数: 见上方代码注释

 

8.条件渲染

在 React 中,你可以创建不同的组件来封装各种你需要的行为。然后还可以根据应用的状态变化只渲染其中的一部分。

React 中的条件渲染和 JavaScript 中的一致,使用 JavaScript 操作符 if 或条件运算符来创建表示当前状态的元素,然后让 React 根据它们来更新 UI。

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(
    // Try changing to isLoggedIn={true}:
    <Greeting isLoggedIn={true} />,//传递参数
    document.getElementById('example')
);

 

元素变量

你可以使用变量来储存元素。它可以帮助你有条件的渲染组件的一部分,而输出的其他部分不会更改。

在下面的例子中,我们将要创建一个名为 LoginControl 的有状态的组件。

它会根据当前的状态来渲染 <LoginButton /> 或 <LogoutButton />,它也将渲染前面例子中的 <Greeting />

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};//默认为false
    }
    handleLoginClick() {//改变isLoggedIn的状态
        this.setState({isLoggedIn: true});
    }
    handleLogoutClick() {//改变isLoggedIn的状态
        this.setState({isLoggedIn: false});
    }
    render() {
        const isLoggedIn = this.state.isLoggedIn;//获取isLoggedIn的状态
        let button = null;
        if (isLoggedIn) {//判断条件显示不同的按钮
            button = <LogoutButton onClick={this.handleLogoutClick} />;//onClick为参数
        } else {
            button = <LoginButton onClick={this.handleLoginClick} />;
        }
        return (//调用判断的方法
            <div>
                <Greeting isLoggedIn={isLoggedIn} />
                {button}
            </div>
        );
    }
}
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 />;
}
function LoginButton(props) {
    return (
        <button onClick={props.onClick}>
            登陆
        </button>
    );
}
function LogoutButton(props) {
    return (
        <button onClick={props.onClick}>
            退出
        </button>
    );
}
ReactDOM.render(
    <LoginControl />,
    document.getElementById('example2')
);

与运算符 &&

你可以通过用花括号包裹代码在 JSX 中嵌入任何表达式 ,也包括 JavaScript 的逻辑与 &&,它可以方便地条件渲染一个元素

function Mailbox(props) {
    const unreadMessages = props.unreadMessages;//获取到参数
    return (
        <div>
            <h1>Hello!</h1>
            {unreadMessages.length > 0 &&   //如果长度大于0就显示下面的
            <h2>
                您有 {unreadMessages.length} 条未读信息。
            </h2>
            }
        </div>
    );
}
const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(
    <Mailbox unreadMessages={messages} />,//调用组件并传参
    document.getElementById('example3')
);

在 JavaScript 中,true && expression 总是返回 expression,而 false && expression 总是返回 false

因此,如果条件是 true&& 右侧的元素就会被渲染,如果是 false,React 会忽略并跳过它。

 

三元运算符

function Mailbox(props) {
    const unreadMessages = props.unreadMessages;//获取到参数
    return (
        <div>
            <h1>Hello!</h1>
            {unreadMessages.length > 0 ?   //如果长度大于0就显示下面的
            <h2>
                您有 {unreadMessages.length} 条未读信息。
            </h2>:
                <h1>你没有信息,25仔</h1>
            }
        </div>
    );
}

阻止组件渲染

在极少数情况下,你可能希望隐藏组件,即使它被其他组件渲染。让 render 方法返回 null 而不是它的渲染结果即可实现。

在下面的例子中,<WarningBanner /> 根据属性 warn 的值条件渲染。如果 warn 的值是 false,则组件不会渲染:

function WarningBanner(props) {
    if (!props.warn) {//如果为false就返回
        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}/>//调用WarningBanner,参数为showWarning
                <button onClick={this.handleToggleClick}>
                    {this.state.showWarning ? '隐藏' : '显示'}//三元判断
                </button>
            </div>
        );
    }
}
ReactDOM.render(
    <Page/>,
    document.getElementById('example4')
);

组件的 render 方法返回 null 并不会影响该组件生命周期方法的回调。例如,componentWillUpdate 和 componentDidUpdate 依然可以被调用。

 

通过条件渲染页面:

首先建一个函数,来根据不同的情况返回不同的值,然后建一个类组建,先进行变量的初始化,对变量进行操作,在组件内进行小的渲染,最后通过 ReactDOM.render() 渲染到页面上。

为什么要进行变量的初始化?

一个软件所分配到的空间中极可能存在着以前其他软件使用过后的残留数据,这些数据被称之为垃圾数据。所以通常情况下我们为一个变量,为一个数组,分配好存储空间之后都要对该内存空间初始化。

简单来说就是清理残留数据。

 

9.列表 & Keys

创建一个列表

const numbers = [1, 2, 3, 4, 5];//创建一个数组
const listItems = numbers.map((numberss) =>//理解为循环,前面的numbers为数组,后面的为遍历的对象
    <li>{numberss}</li>
);
ReactDOM.render(
    <ul>{listItems}</ul>,//展现
    document.getElementById('example')
);

 

我们可以将以上实例重构成一个组件,组件接收数组参数,每个列表元素分配一个 key,不然会出现警告 a key should be provided for list items,意思就是需要包含 key:

function NumberList(props) {
    const numbers = props.numbers;//获取参数
    const listItems = numbers.map((number) =>//循环
        <li key={number.toString()}>
            {number}
        </li>
    );
    return (//直接返回一个ul
        <ul>{listItems}</ul>
    );
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
    <NumberList numbers={numbers} />,//调用并设置参数
    document.getElementById('example')
);

 

Keys

Keys 可以在 DOM 中的某些元素被增加或删除的时候帮助 React 识别哪些元素发生了变化。因此你应当给数组中的每一个元素赋予一个确定的标识。

 

一个元素的 key 最好是这个元素在列表中拥有的一个独一无二的字符串。通常,我们使用来自数据的 id 作为元素的 key

 

当元素没有确定的 id 时,你可以使用他的序列号索引 index 作为 key

用keys提取组件

元素的 key 只有在它和它的兄弟节点对比时才有意义。

比方说,如果你提取出一个 ListItem 组件,你应该把 key 保存在数组中的这个 <ListItem /> 元素上,而不是放在 ListItem 组件中的 <li> 元素上。

当你在 map() 方法的内部调用元素时,你最好随时记得为每一个元素加上一个独一无二的 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')
);

元素的 key 在他的兄弟元素之间应该唯一

数组元素中使用的 key 在其兄弟之间应该是独一无二的。然而,它们不需要是全局唯一的。当我们生成两个不同的数组时,我们可以使用相同的键。

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 (//返回两个ul
        <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('example2')
);

key 会作为给 React 的提示,但不会传递给你的组件。如果您的组件中需要使用和 key 相同的值,请将其作为属性传递:

 

const content = posts.map((post) =>

<Post

    key={post.id}

    id={post.id}

    title={post.title} />);

上面例子中,Post 组件可以读出 props.id,但是不能读出 props.key。

在 jsx 中嵌入 map()

在上面的例子中,我们声明了一个单独的 listItems 变量并将其包含在 JSX 中:

function NumberList(props) {

  const numbers = props.numbers;

  const listItems = numbers.map((number) =>

    <ListItem key={number.toString()}

              value={number} />

 

  );

  return (

    <ul>

      {listItems}

    </ul>

  );}

 

JSX 允许在大括号中嵌入任何表达式,所以我们可以在 map() 中这样使用:

function NumberList(props) {

const numbers = props.numbers;

return ( <ul> {

numbers.map((number) =>

<ListItem key={number.toString()}

value={number} />

)} </ul> );

}

需要注意的事项(请看注释):

var ListItem = (props) => {       //es6中箭头函数

return <li>{props.value}</li>;}

function NumberList(props) {

var numbers;    //声明在外面是因为 {} 中不能出现var,const,let等这种关键字

return (

<ul>

{

        numbers = props.numbers,   //注意这里要加逗号

        numbers.map((number) =>

<ListItem key={number}

         value={number} />

)}

</ul>

);}

var arr = [1,2,3];   //要传递的参数

ReactDOM.render(

<NumberList numbers={arr}/>,  //这里的numbers就是props下的numbers,即props.numbers

    document.all('example'));

 

10.组件API

七个方法:

  • 设置状态:setState
  • 替换状态:replaceState
  • 设置属性:setProps
  • 替换属性:replaceProps
  • 强制更新:forceUpdate
  • 获取DOM节点:findDOMNode
  • 判断组件挂载状态:isMounted

 

setState

setState(object nextState[, function callback])

  • nextState,将要设置的新状态,该状态会和当前的state合并
  • callback,可选参数,回调函数。该函数会在setState设置成功,且组件重新渲染后调用。

合并nextState和当前state,并重新渲染组件。setState是React事件处理函数中和请求回调函数中触发UI更新的主要方法。

关于setState

不能在组件内部通过this.state修改状态,因为该状态会在调用setState()后被替换。

setState()并不会立即改变this.state,而是创建一个即将处理的state。setState()并不一定是同步的,为了提升性能React会批量执行state和DOM渲染。

setState()总是会触发一次组件重绘,除非在shouldComponentUpdate()中实现了一些条件渲染逻辑。

 

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')
);

 

替换状态:replaceState

replaceState(object nextState[, function callback])

  • nextState,将要设置的新状态,该状态会替换当前的state
  • callback,可选参数,回调函数。该函数会在replaceState设置成功,且组件重新渲染后调用。

replaceState()方法与setState()类似,但是方法只会保留nextState中状态,原state不在nextState中的状态都会被删除。

 

设置属性:setProps

replaceProps(object nextProps[, function callback])

  • nextProps,将要设置的新属性,该属性会替换当前的props
  • callback,可选参数,回调函数。该函数会在replaceProps设置成功,且组件重新渲染后调用。

replaceProps()方法与setProps类似,但它会删除原有 props。

 

 

强制更新:forceUpdate

forceUpdate([function callback])

参数说明

  • callback,可选参数,回调函数。该函数会在组件render()方法调用后调用。

forceUpdate()方法会使组件调用自身的render()方法重新渲染组件,组件的子组件也会调用自己的render()。但是,组件重新渲染时,依然会读取this.props和this.state,如果状态没有改变,那么React只会更新DOM。

forceUpdate()方法适用于this.props和this.state之外的组件重绘(如:修改了this.state后),通过该方法通知React需要调用render()

一般来说,应该尽量避免使用forceUpdate(),而仅从this.props和this.state中读取状态并由React触发render()调用。

 

 

获取DOM节点:findDOMNode

DOMElement findDOMNode()

  • 返回值:DOM元素DOMElement

如果组件已经挂载到DOM中,该方法返回对应的本地浏览器 DOM 元素。当render返回null 或 false时,this.findDOMNode()也会返回null。从DOM 中读取值的时候,该方法很有用,如:获取表单字段的值和做一些 DOM 操作。

 

判断组件挂载状态:isMounted

bool isMounted()

  • 返回值:truefalse,表示组件是否已挂载到DOM中

isMounted()方法用于判断组件是否已挂载到DOM中。可以使用该方法保证了setState()forceUpdate()在异步场景下的调用不会出错。

 

11.组件生命周期

组件的生命周期可分成三个状态:

  • Mounting:已插入真实 DOM
  • Updating:正在被重新渲染
  • Unmounting:已移出真实 DOM

生命周期的方法有:

componentWillMount 在渲染前调用,在客户端也在服务端。

 

componentDidMount : 在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异步操作阻塞UI)。

 

componentWillReceiveProps 在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。

 

shouldComponentUpdate 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。 
可以在你确认不需要更新组件时使用。

 

componentWillUpdate在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。

 

componentDidUpdate 在组件完成更新后立即调用。在初始化时不会被调用。

 

componentWillUnmount在组件从 DOM 中移除之前立刻被调用。

 

以下实例在 Hello 组件加载以后,通过 componentDidMount 方法设置一个定时器,每隔100毫秒重新设置组件的透明度,并重新渲染

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属性
            opacity -= 0.05;//每次减0.05
            if (opacity < 0.1) {//如果小于0.1就变成1,也就是变得明显
                opacity = 1.0;
            }
            this.setState({//重新给属性赋值
                opacity: opacity
            });
        }.bind(this), 100);//绑定,0.1秒执行一次
    }
    render () {
        return (
            <div style={{opacity: this.state.opacity}}>//opacity为透明程度
                Hello {this.props.name}
            </div>
        );
    }
}
ReactDOM.render(
    <Hello name="world"/>,//调用Hello并传参
    document.body//作用在body
);

 

以下实例初始化 state , setNewnumber 用于更新 state。所有生命周期在 Content 组件中。生命周期的触发时间见上方

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('example2')
);

 

12.表单与事件

HTML 表单元素与 React 中的其他 DOM 元素有所不同,因为表单元素生来就保留一些内部状态。

在 HTML 当中,像 <input>, <textarea>, 和 <select> 这类表单元素会维持自身状态,并根据用户输入进行更新。但在React中,可变的状态通常保存在组件的状态属性中,并且只能用 setState() 方法进行更新。

 

onChange事件

在实例中我们设置了输入框 input 值 value = {this.state.data}。在输入框值发生变化时我们可以更新 state。我们可以使用 onChange 事件来监听 input 的变化

var Content = React.createClass({
    render: function () {
        return <div>
            <input type="text" value={this.props.myDataProp}//设置初始值
                   onChange={this.props.updateStateProp}/>//onChange事件
            <h4>{this.props.myDataProp}</h4>//将参数放入
        </div>;
    }
});
var HelloMessage = React.createClass({
    getInitialState: function () {//返回初始值
        return {value: 'Hello Runoob!'};
    },
    handleChange: function (event) {//更新,onChange会触发的事件
        this.setState({value: event.target.value});
    },
    render: function () {
        var value = this.state.value;//获取到value参数
        return <div>
            <Content myDataProp={value}//调用Content
                     updateStateProp={this.handleChange}></Content>//填入参数
        </div>;
    }
});
ReactDOM.render(
    <HelloMessage/>,//调用
    document.getElementById('example')//放到该节点
);

上面的代码将渲染出一个值为 Hello Runoob! 的 input 元素,并通过 onChange 事件响应更新用户输入的值。

 

 

在以下实例中我们将为大家演示如何在子组件上使用表单。 onChange 方法将触发 state 的更新并将更新的值传递到子组件的输入框的 value 上来重新渲染界面。

你需要在父组件通过创建事件句柄 (handleChange) ,并作为 prop (updateStateProp) 传递到你的子组件上。

 

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 Runoob!'};
        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')
);

 

上面两种代码差别不是很大,一个用class一个用的var,效果是一样的

 

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">Google</option>

            <option value="rn">Runoob</option>

            <option value="tb">Taobao</option>

            <option value="fb">Facebook</option>

          </select>

        </label>

        <input type="submit" value="提交" />

      </form>

    );

  }

}

 

ReactDOM.render(

  <FlavorForm />,

  document.getElementById('example')

);

 

 

 

举报

相关推荐

0 条评论