React从入门到精通系列之(5)state管理和生命周期钩子

作者:日期:2017-02-19 13:15:14 点击:325

 

State和生命周期

考虑前面部分中的滴答时钟示例(第三章)••。
到目前为止,我们只学习了一种更新UI的方法••。
我们调用ReactDOM.render()来转变渲染输出:

function tick() {
    const element = (
                    Hell world            It is {new Date().toLocaleTimeString()}            );
    ReactDOM.render(
        element,
        document.getElementById('root')
    );
}
setInterval(tick, 1000);

在本节中,我们将学习如何使Clock组件真正可重用和封装••。 它将设置自己的计时器并每秒更新一次••。
我们可以从封装时钟的外观开端:

function Clock(props) {
    return (
                    hello world            It is {props.date.toLocaleTimeString()}            );
}
function tick() {
   ReactDOM.render(
       ,
       document.getElementById('root')
   );
}
setInterval(tick, 1000);

然而,它缺乏了一个要害请求:时钟设置一个定时器和每秒更新UI的事实应当是时钟的实现细节••。理想情况下,我们要写这一次,并由时钟本身来更新时间:

ReactDOM.render(
    ,
    document.getElementById('root')
);

要实现这一点,我们需要添加“state”到时钟组件••。

state类似于props,但它是私有的,完整由组件把持••。

我们之前提到,定义为类组件具有一些附加功效••。 内部state就是:一个只有类组件可用的功效••。

将函数情势组件改为类情势组件

您可以通过五个步骤将功效组件(如Clock)转换为类组件 :

  1. 创立一个与扩大React.Component雷同名称的ES6类••。

  2. 为它添加一个单一的空方法render()••。

  3. 将函数的主体移动到render()方法中••。

  4. render()主体中用this.props调换props••。

  5. 删除剩余的空函数声明••。

class Clock extends React.Component {
   render() {
       return (
                          hello world               It is {this.props.date.toLocaleTimeString()}.                  )
   };
}

Clock现在已经重新定义为类组件而不是之前的功效组件了••。
这使我们可以应用额外的功效,如内部state和生命周期钩子••。

向类组件中添加state

我们将分为三个步骤把dateprops移动到state

1)在render()方法中将this.props.date调换为this.state.date
class Clock extends React.Component {
    render() {
        return (
                            hello world                It is {this.state.date.toLocaleTimeString()}.                    );
    }
}
2)添加一个赋值初始this.state的类结构函数:
class Clock extends React.Component {
    constructor(props) {
        super(props);
        this.state = {date: new Date()};
    }
    
    render() {
        return (
                            hello world                It is {this.state.date.toLocalTimeString()}.                    );
    }
}

注意我们如何将props传递给基类的结构函数:

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

类组件应当总是用props调用基类结构函数••。

3)从元素中删除date prop:
ReactDOM.render(
    ,
    document.getElementById('root')
);

我们稍后将定时器代码添加回组件本身••。成果如下所示:

class Clock extends React.Component {
    constructor(props) {
        super(props);
        this.state = {date: new Date()};
    }
    
    render() {
       return (
                          hello world               It is {this.state.date.toLocaleTimeString()}.                  );
    }
}
ReactDOM.render(
    ,
    document.getElementById('root')
);

接下来,我们将使时钟设置自己的定时器,并每秒更新一次••。

向类中添加声明周期方法

在具有许多组件的利用程序中,释放组件在烧毁时占用的资源非常重要••。
我们想要在第一次将时钟渲染到DOM时设置一个计时器••。 这在React中称为“安装(mounting)”••。
我们还想扫除定时器,当时钟产生的DOM被删除••。 这在React中称为“卸载(unmounting)"••。
我们可以在组件类上声明特别方法,以便在组件装入和卸载时运行一些代码:

class Clock extends React.Component {
    constructor(props) {
        super(props);        this.state = {date: new Date()};
    }
    
    componentDidMount() {        // 组件已经安装完毕
    }
    
    componentWillUnmount() {        // 组件将要被卸载
    }
    
    render() {
       return (
                          hello world               It is {this.state.date.toLocaleTimeString()}.                  );
    }
}

这些方法称为“生命周期钩子”••。
componentDidMount()子在组件输出浮现到DOM之后运行••。 这是设置计时器的利益所:

componentDidMount() {
    this.timerID = setInterval(
        () => this.tick(),
        1000
    )
}

注意我们如何保存计时器ID就在这••。
虽然this.props是由React本身设置的,并且this.state有一个特别的含义,如果你需要存储不用于视觉输出的东西,你可以手动地添加额外的字段到类中••。
如果你不应用render()中的东西,它不应当放置在state中••。
我们将拆除componentWillUnmount()生命周期钩子中的计时器:

componentWillUnmount() {
    clearInterval(this.timerID);
}

最后,我们将实现每秒运行的tick()方法••。
它将应用this.setState()来调度组件本地state的更新:

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()}.                  );
    }
}
ReactDOM.render(
    ,
    document.getElementById('root')
);

现在时钟每秒钟都在滴答地走,棒不棒••。••。••。••。

让我们快速回想一下产生了什么以及调用方法的次序:

  • 1)当将传递给ReactDOM.render()时,React调用Clock组件的结构函数••。由于Clock需要显示当前时间,它应用包含当前时间的对象初始化this.state••。我们稍后将更新此state••。

  • 2)React然后调用Clock组件的render()方法••。这是React如何学习应当在屏幕上显示什么••。 React然后更新DOM以匹配时钟的渲染输出••。

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

  • 4)每秒钟浏览器调用tick()方法••。在其中,Clock组件通过调用setState()和包含当前时间的对象来调度UI更新••。由于setState()调用,React知道state已更改,并再次调用render()方法来懂得屏幕上应当显示的内容••。这个时候,render()方法中的this.state.date将会不同,因此渲染输出将包含更新的时间••。 React相应地更新DOM••。

  • 5)如果时钟组件从DOM中被移除,React将调用componentWillUnmount()生命周期钩子,因此定时器结束••。

正确应用state

关于setState()你应当懂得三件事情:

不要直接修正state

例如,这将不会重新渲染组件:

// 这是毛病的this.state.comment = 'hello';

应当应用setState()代替:

// 这是正确的this.setState({comment: 'hello'});

唯一可以分配this.state的处所是结构函数••。

state更新可能是异步的

React可以将多个setState()用批处理为单个更新以实现较高的性能••。
因为this.propsthis.state可能是异步更新的,你不应当依附它们的值来盘算下一个state••。
例如,此代码可能无法更新计数器:

// 这是毛病的this.setState({
    counter: this.state.counter + this.props.increment,
});

要解决它,应当应用回调函数而不是对象来调用setState()••。 回调函数将吸收先前的state作为第一个参数,并将利用更新时的props作为第二个参数:

// 这是正确的this.setState((prevState, props) => ({
    counter: prevState.counter + props.increment
}));

我们应用上面的箭头函数,但它也可以与惯例函数一起应用:

// 这同样也是正确的,将剪头函数改为普通函数
this.setState(function(prevState, props) {
   return {
       counter: prevState.counter + prps.increment
   }
});
state更新是经过合并的

当调用setState()时,React会将您供给的对象合并到当前state••。
例如,您的state可能包含几个独立变量:

constructor(props) {
    super(props);
    this.state = {
        posts: [],
        comments: []
    }
}

然后,您可以应用单独的setState()来独立地更新它们:

componentDidMount() {
    fetchPosts().then(response => {
        this.setState({
            posts: response.posts
        });
    });
    
    fetchComments().then(response => {
        this.setState({
            comments: response.comments
        }});
    });
}

合并很浅,所以this.setState({comments})不会波及this.state.posts••。仅仅只是完整调换了this.state.comments而已••。

数据是向下流动的

父组件和子组件都不能知道某个组件是有State的还是无State的,并且它们不应当关心它是否为功效组件或类组件••。

这就是为什么State通常被设置为局部变量或封装到组件内部••。 除了拥有和设置它的组件之外的其他任何组件都不能访问它••。

组件可以选择将其state作为props传递给其子组件:

Is is {this.state.date.toLocaleTimeString()}.

这也实用于用户定义的组件:


FormattedDate组件将在其props中吸收date,并且不知道它是来自时钟的stateprops还是手动输入

function FormattedData(props) {
    return Is is {props.date.toLocaleTimeString()}.;
}

这通常被称为“自顶向下”“单向”数据流••。 任何state总是由一些特定组件拥有,并且从该state派生的任何数据或UI只能影响树中的“下面”组件••。

如果你想象一个组件树作为props的瀑布流,每个组件的state就像一个额外的水源,它可以在任意点连接它,但也向下流••。

为了显示所有组件都是真正隔离的,我们可以创立一个App组件来渲染三个

function App() {
    return (
                                                        );
}
ReactDOM.render(
    ,
    document.getElementById('root')
);

每个时钟设置自己的定时器并独立更新••。在React利用程序中,组件是有状态还是无状态被视为可能随时间更改的组件的实现细节••。 您可以在有状态组件内应用无状态组件,反之亦然••。

 

上一篇: 应用Node实现Http代理

下一篇: React从入门到精通系列之(6)事件处理

玖玖网彩票官网 澳洲快3开户网 重庆幸运农场直播网 网易红彩彩票手机app 飞速赛车登入平台 澳洲快乐十分投注网 澳洲快3如投注 网易红彩彩票开奖走势图 艾米彩票开户平台 极速牛牛投注平台