React中文文档之State and Lifecycle

    xiaoxiao2021-08-15  148

    state 和 生命周期 到目前为止,我们仅仅学习了一种方式来更新UI。 我们调用 'ReactDOM.render()' 来改变输出渲染: function tick() { const element = ( <div> <h1>Hello, world!</h1> <h2>It is {new Date().toLocaleTimeString()}.</h2> </div> ); ReactDOM.render( element, document.getElementById('root') ); } setInterval(tick, 1000); 在这一小节,我们将学习如何创建真实地可重用的以及封装的Clock组件。它将建立自己的计时器,并每秒更新自身。 我们可以通过封装来开始,如下: function Clock(props) { return ( <div> <h1>Hello, world!</h1> <h2>It is {props.date.toLocaleTimeString()}.</h2> </div> ); } function tick() { ReactDOM.render( <Clock date={new Date()} />, document.getElementById('root') ); } setInterval(tick, 1000); 然而,它忽略了一个重要的需求:事实上,Clock组件建立一个计时器,并且每秒更新UI,应该是 Clock 组件的一个实现细节. 理想情况下,我们应该只写一次,Clock组件就会更新自身。 ReactDOM.render( <Clock />, document.getElementById('root') ); 为了实现这个,我们需要添加 'state' 到 Clock 组件。 state同props非常相似,但是它是私有的,而且完全被组件自身控制 我们前面提到过,以类方式,定义的组件,有一些额外的特性。 局部state就是类可用的功能之一 将函数式组件,转换成类组件 你可以传唤一个函数式组件,例如 Clock组件,到一个类组件,通过5个步骤: 1.创建一个同名的ES6类,继承 React.Component 2.添加一个单个的render()空方法 3.移动之前的函数式组件的函数体到render()方法中 4.在render()体中,使用 'this.props' 替代 'props' 5.删除留下的空的函数声明 class Clock extends React.Component { render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.props.date.toLocaleTimeString()}.</h2> </div> ); } } 此时,Clock组件被定义为一个类,而不是函数 这就让我们可以使用 '附加特性',例如:局部state和生命周期钩子 向类中添加局部state 我们通过3步,将props中的data移动到state: 1.在render()方法中,使用 'this.state.data' 替换 'this.props.date' class Clock extends React.Component { render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> // 这行 </div> ); } } 2.添加类的构造器,分配初始的 'this.state': class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } } 注意:我们如何传入 'props' 给基础构造器(constructor) constructor(props) { super(props); this.state = {date: new Date()}; } 组件类,应该总是传入 'props' 调用基础构造器 3.从<Clock />元素上,移除 'date' prop ReactDOM.render( <Clock />, document.getElementById('root') ); 稍后,我们将会给组件重新添加计时器 此时修改后的结果如下: class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } render() { return ( <div> <h1>Hello, world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } } ReactDOM.render( <Clock />, document.getElementById('root') ); 向类中添加生命周期方法: 在由许多组件组成的应用中,很重要的一点是:当组件被销毁时,释放他们占用的资源。 clock组件首次渲染为dom时,我们设置了一个计时器(set up a timer)。这在React中,被称作 'mounting' 当移除由clock组件产生的dom时,我们清除mount设置的计时器。这在React中,被称作 'unmouting' 我们可以在组件类中,声明一些特殊的方法,当组件 mount 和 unmount 时,来运行我们设置的代码: class Clock extends React.Component { constructor(props) { super(props); this.state = {data: new Date()}; } componentDitMount(){ } componentWillUnmount(){ } render() { return ( <h1>It is {this.state.date.toLocaleTimeString()}.</h1> ); } } 这些方法被称作 '生命周期 钩子' 组件输出已经被渲染为dom后,运行 'componentDitMount()' 钩子。这是个设置计时器的好地方: componentDitMount(){ this.timerID = setInterval( () => this.tick(), 1000 ); } 注意我们是如何在 'this' 对象上,保存 计时器ID的。 this.props由React自身创建,而this.state有特殊的意义,如果你需要存储一些数据,且这些数据并不用于可见输出(the visual output),你可以手动给组件类添加额外的字段 如果你在 render() 中不使用其他字段,则不应该在 state 中添加这些字段 我们将在 'componentWillunmount' 生命周期钩子中,清除计时器 componentWillunmount() { clearInterval(this.timerID); } 最后,我们将实现 tick() 方法,将会每秒运行一次 tick() 方法,使用 'this.setState()' 来更新组件的私有state tick() { this.setState({ data: new Date(); }); } 正确使用 state 关于setState(),你应该知道3件事: 1.不要直接修改state 例如,下面的代码,将不会重新渲染组件: this.state.comment = 'Hello'; // 错误 使用 setState() 来代替: this.setState({comment: 'Hello'}); // 正确 2.state的更新,可能异步的 为了性能考虑,React可以在一次更新中,批量执行 setSate() 调用 因为 this.props 和 this.state 可能被异步更新,在计算接下来的state,你不应该依赖它们的值(可能还未被更新) 例如,下面的更新counter的代码可能失败: this.setState({ counter: this.state.counter + this.props.increment; // 错误 }); 为了修复上面的错误,使用setState()的第二种形式,接收一个函数(function),而非一个对象(object)。函数接收上一次的state作为第一个参数,更新应用时的props作为第二个参数: this.setState((prevState, props) => ({ counter : prevState.counter + props.increment // 正确 })); 上面,我们使用了一个 '箭头函数'(arrow function),也可以使用一般的函数: this.setState(function(prevState, props) { return { counter : prevState.counter + props.increment // 正确 } }); 3.state更新会被合并 当调用 'setState()'时,React会合并 你提供的对象(the object you provide) 到当前的state。 例如:提供的state可能包含几个独立的变量: constructor(props) { super(props); this.state = { posts: [], comments: [] }; } 接着,可以使用独立的 setState() 调用来分别更新它们: componentDitMount() { 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 数据流向下传递(the data flows down) 父组件和子组件,都不知道某个组件是 'stateful' 还是 'stateless',而且,它们不关心它们被定义为一个函数还是类。 这就是为什么state经常被称作 局部(local) 或者 包装、封装(encapsulated)。对于除了拥有并设置它的组件之外的任意其他组件,它是不可访问的。 同 props 一样,组件也可以选择,将它的state传递给它的子组件: <h2>It is {this.state.date.loLocaleTimeString()}.</h2> 对用户自定义组件也是有效的: <FormattedDate data={this.state.date} /> FormattedDate 组件接收props中的date,但是并不知道它是来自'clock' 的state,props,还是手动输入: function FormattedDate(props) { return <h2>It is {props.date.toLocaleTimeString()}.</h2>; } 这通常被称为“自上而下”或“单向”的数据流。任何由某些特定组件拥有的state,任何来自该state的数据或UI,仅仅会影响树形结构中,它们的下级组件. 想象一个组件树是一个props瀑布,每个组件的state就像一个附加的水源,在任意点加入到瀑布中,但是也向下流动。 为了显示所有的组件是完全隔离的,我们可以创建一个app组件,渲染3个 clock 组件: function App() { return ( <div> <Clock /> <Clock /> <Clock /> </div> ); } ReactDOM.render( <App />, document.getElementById('root') ); 每个Clock设置它自己的计时器,并且独立的更新。 在React应用中,不论组件是有状态的(stateful),还是无状态的(stateless),都被认为是组件的实现细节,可以随着时间而改变。你可以在有状态的组件中使用无状态组件,反过来也一样。
    转载请注明原文地址: https://ju.6miu.com/read-676354.html

    最新回复(0)