【译】State and Lifecycle (State和生命周期)
Consider the ticking clock example from one of the previous sections. 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); 打开试试 In this section,we will learn how to make the Clock component truly reusable and encapsulated. We can start by encapsulating how the clock looks: 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()} />,1000); 打开试试 However,it misses a crucial requirement: the fact that the Clock sets up a timer and updates the UI every second should be an implementation detail of the Clock. Ideally we want to write this once and have the Clock update itself: ReactDOM.render( <Clock />,document.getElementById('root') ); To implement this,we need to add "state" to the Clock component. State is similar to props,but it is private and fully controlled by the component. We mentioned before that components defined as classes have some additional features. Local state is exactly that: a feature available only to classes. Converting a Function to a Class将一个函数转换为一个类 You can convert a functional component like Clock to a class in five steps:
class Clock extends React.Component { render() { return ( <div> <h1>Hello,world!</h1> <h2>It is {this.props.date.toLocaleTimeString()}.</h2> </div> ); } } 打开试试 Clock is now defined as a class rather than a function. This lets us use additional features such as local state and lifecycle hooks. Adding Local State to a Class添加State We will move the date from props to state in three steps: 1) Replace this.props.date with this.state.date in the render() method: class Clock extends React.Component { render() { return ( <div> <h1>Hello,world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } } 2) Add a 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> ); } } Note how we pass props to the base constructor: constructor(props) { super(props); this.state = {date: new Date()}; } Class components should always call the base constructor with props. 3) Remove the date prop from the <Clock /> element: ReactDOM.render( <Clock />,document.getElementById('root') ); We will later add the timer code back to the component itself. 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') ); 打开试试 Adding Lifecycle Methods to a Class添加周期方法到类组件 In applications with many components,it's very important to free up resources taken by the components when they are destroyed. We want to set up a timer whenever the Clock is rendered to the DOM for the first time. This is called "mounting" in React. We also want to clear that timer whenever the DOM produced by the Clock is removed. This is called "unmounting" in React. We can declare special methods on the component class to run some code when a component mounts and unmounts: class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } componentDidMount() { } componentWillUnmount() { } render() { return ( <div> <h1>Hello,world!</h1> <h2>It is {this.state.date.toLocaleTimeString()}.</h2> </div> ); } } These methods are called "lifecycle hooks". The componentDidMount() hook runs after the component output has been rendered to the DOM. This is a good place to set up a timer: componentDidMount() { this.timerID = setInterval( () => this.tick(),1000 ); } Note how we save the timer ID right on this. While this.props is set up by React itself and this.state has a special meaning,you are free to add additional fields to the class manually if you need to store something that is not used for the visual output. If you don't use something in render(),it shouldn't be in the state. We will tear down the timer in the componentWillUnmount() lifecycle hook: componentWillUnmount() { clearInterval(this.timerID); } Finally,we will implement the tick() method that runs every second. It will use this.setState() to schedule updates to the component local 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 ( <div> <h1>Hello,document.getElementById('root') ); 打开试试 Now the clock ticks every second. Let's quickly recap what's going on and the order in which the methods are called: 1) When <Clock /> is passed to ReactDOM.render(),React calls the constructor of the Clock component. 4) Every second the browser calls the tick() method. 5) If the Clock component is ever removed from the DOM,React calls the componentWillUnmount() lifecycle hook so the timer is stopped. Using State Correctly正确使用 There are three things you should know about setState(). Do Not Modify State Directly别直接修改 For example,this will not re-render a component: // Wrong this.state.comment = 'Hello'; Instead,use setState(): // Correct this.setState({comment: 'Hello'}); The only place where you can assign this.state is the constructor. State Updates May Be Asynchronous
React may batch multiple setState() calls into a single update for performance. Because this.props and this.state may be updated asynchronously,you should not rely on their values for calculating the next state. For example,this code may fail to update the counter: // Wrong this.setState({ counter: this.state.counter + this.props.increment,}); To fix it,use a second form of setState() that accepts a function rather than an object. // Correct this.setState((prevState,props) => ({ counter: prevState.counter + props.increment })); We used an arrow function above,but it also works with regular functions: // Correct this.setState(function(prevState,props) { return { counter: prevState.counter + props.increment }; }); State Updates are Merged
When you call setState(),React merges the object you provide into the current state. For example,your state may contain several independent variables: constructor(props) { super(props); this.state = { posts: [],comments: [] }; } Then you can update them independently with separate setState() calls: componentDidMount() { fetchPosts().then(response => { this.setState({ posts: response.posts }); }); fetchComments().then(response => { this.setState({ comments: response.comments }); }); } The merging is shallow,so this.setState({comments}) leaves this.state.posts intact,but completely replaces this.state.comments. The Data Flows Down单向数据流 Neither parent nor child components can know if a certain component is stateful or stateless,and they shouldn't care whether it is defined as a function or a class. 所有的父组件或者子组件都不知道一个组件是 This is why state is often called local or encapsulated. It is not accessible to any component other than the one that owns and sets it. A component may choose to pass its state down as props to its child components: <h2>It is {this.state.date.toLocaleTimeString()}.</h2> This also works for user-defined components: <FormattedDate date={this.state.date} /> The FormattedDate component would receive the date in its props and wouldn't know whether it came from the Clock's state,from the Clock's props,or was typed by hand: function FormattedDate(props) { return <h2>It is {props.date.toLocaleTimeString()}.</h2>; } 打开试试 This is commonly called a "top-down" or "unidirectional" data flow. Any state is always owned by some specific component,and any data or UI derived from that state can only affect components "below" them in the tree. If you imagine a component tree as a waterfall of props,each component's state is like an additional water source that joins it at an arbitrary point but also flows down. To show that all components are truly isolated,we can create an App component that renders three <Clock>s: function App() { return ( <div> <Clock /> <Clock /> <Clock /> </div> ); } ReactDOM.render( <App />,document.getElementById('root') ); 打开试试 Each Clock sets up its own timer and updates independently. In React apps,whether a component is stateful or stateless is considered an implementation detail of the component that may change over time. You can use stateless components inside stateful components,and vice versa. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |