react 初识记录(二)

107 阅读6分钟

​持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情

State&生命周期

学习到这,我只了解到一种更新UI界面的方法。通过调用root.render()来修改想要渲染的元素。

实际上state也可以更新UI界面,它与props类似,但state是私有化的,并且完全受控于当前组件

class Clock extends React.Component {
    constructor(props){
        super(props);
        this.state = {date: new Date()}
    }
render(){
  return (
      <div>
          <h1>计时器:{this.state.date.toLocaleTimeString()}</h1>
      </div>
  );
}
}
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(<Clock />)
​

将生命周期方法添加到Class中

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

当Clock组件第一次被渲染到DOM中的时候,就为其设置一个计时器。这在React中被称为挂载(mount

同时,当DOM中Clock组件被删除的时候,应该清除计时器。称为卸载(unmount)

class组件声明一些特殊的方法,当组件挂载或卸载时就会去执行这些方法,这些方法称为生命周期方法

componentDidMount()方法会在组件已经被渲染到DOM中后运行

componentWillUnmount()方法会在组件卸载前运行

正确使用State

1、不能直接修改State

要通过setSatate()来进行修改

2、State的更新可能是异步的

出于性能考虑,React可能会把多个setState()调用合并成一个调用

因为this.props和this.state可能会异步更新,所以不能依赖它们的值来更新下一个状态

例如:

this.setState({

count: this.state.a + this.props.b

})

//以上是错误写法

this.setState((state,props)=>{

​ count: state.a + props.b

})

// setState()接收一个函数而不是对象。这个函数用上一个state作为第一个参数,将此次更新被应用时的props作为第二个参数

3、State的更新会被合并

当调用setState()的时候,React会把提供的对象合并到当前的state

例如:state包含几个独立的变量

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

可以通过setState()来单独更新它们

this.setState({a:xxx})

this.setState({b:xxx})

这里的合并是浅合并,所以this.setState({a})完整保留了this.state.b,但是完全替换了this.state.a

4、数据是向下流动的

无论是父组件或者是子组件都无法知道某个组件是有状态的还是无状态的,并且它们也不关心它是函数组件还是class组件

这就是为什么称state为局部的或是封装的原因。除了拥有并设置了它的组件,其他组件都无法访问。

组件可以选择把它的state作为props向下传递到它的子组件中。

这通常会被叫做“自上而下”或者“单向”的数据流。任何的state总是属于特定的组件,而且从该state派生的任何数据或UI只能影响树中“低于”它们的组件

如果把一个组件构成的树想象成一个props的数据瀑布的话,那么每个组件的state就像是在任意一点上给瀑布增加额外的水源,但它是只能向下流动。

事件处理

React元素的事件处理和DOM元素的很相似

React事件的命名采用小驼峰式,而不是纯小写。

使用JSX语法时需要传入一个函数作为事件处理函数,而不是一个字符串

HTML:

React:

在React中另一个不同点是不能通过返回false的方式阻止默认行为。必须显式使用preventDefault。

例:HTML中阻止表单的默认提交行为。

    <form onsubmit="console.log('点击提交');return false;">
        <button type="submit">提交</button>
    </form>

React:

function Sub(){

function handleSubmit(e){
​ e.preventDefault();
​ console.log('点击提交');
​ }
}
return (
​ <form onSubmit={handleSubmit}>
​ <button type="submit">提交</button>
​ </form>
)

使用React时,一般不需要使用addEventListener为已创建的DOM元素添加监听器。只需在该元素初始渲染的时候添加监听器即可。

ES6 class语法定义一个组件的时候,通常的做法是将事件处理函数声明为class中的方法。

class App extends React.Component {
  constructor(props){
    super(props);
    this.state = { isOn:true }
    // 为了在回调中使用this,必须绑定
    this.handleClick = this.handleClick.bind(this); 
  }
  handleClick() {
    this.setState(prevState => ({
      isOn: !prevState.isOn
    }))
  }
  render(){
    return (
      <button onClick={this.handleClick}>
         {this.state.isOn?'ON':'OFF'}
      </button>
    )
  }
}

JSX回调函数中的this如果不绑定,那么调用时this的值为undefined。

这并不是React特有的行为,与JavaScript函数工作原理有关。通常情况下,如果没有在方法后面添加(),例如onClick={this.handleClick},你应该为这个方法绑定this

如果觉得使用bind很麻烦,有两种解决方式

(1)public class fields语法,可以使用class fields正确绑定回调函数

class App extends React.Component {

constructor(props){

​ super(props);

​ this.state = { isOn:true }

}

handleClick=()=> {

​ this.setState(prevState => ({

​ isOn: !prevState.isOn

​ }))

}

render(){

​ return (

​ {this.state.isOn?'ON':'OFF'}

​ )

}

}

tips:这是实验性的语法

Create React App默认启用此语法。

(2)在回调中使用箭头函数


class App extends React.Component {
  constructor(props){
    super(props);
    this.state = { isOn:true }
  }
  handleClick() {
    this.setState(prevState => ({
      isOn: !prevState.isOn
    }))
  }
  render(){
    return (
      <button onClick={()=>this.handleClick()}>
         {this.state.isOn?'ON':'OFF'}
      </button>
    )
  }
}
​

向事件处理程序传递参数

在循环中,通常会为事件处理函数传递额外的参数。

​ <button onClick={(e)=>this.handleClick(a,e)}>箭头写法

bind写法

以上两种方式是等价的,分别通过箭头函数和Function.prototype.bind来实现。

通过箭头函数的方式事件对象必须显式的进行传递,而通过bind的方式,事件对象以及更多的参数将会被隐式的进行传递。

条件渲染

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

元素变量

可以使用变量来储存元素。它可以有条件的渲染组件的一部分,而其它渲染部分并不会因此而改变。

声明一个变量并使用if语句进行条件渲染是不错的方式,同时也有方便进行元素的条件渲染

与运算符&&

通过花括号包裹代码,JSX中嵌入表达式,包括JavaScript中的逻辑与(&&)运算符。


function Mailbox(props) {
  const unreadMessages = props.unreadMessages;
  return (
    <div>
      <h1>Hello!</h1>
      {unreadMessages.length > 0 &&        <h2>          You have {unreadMessages.length} unread messages.        </h2>      }    </div>
  );
}
​
const messages = ['React', 'Re: React', 'Re:Re: React'];
​
const root = ReactDOM.createRoot(document.getElementById('root')); 
root.render(<Mailbox unreadMessages={messages} />);

之所以能这样做,是因为JavaScript中,true&&expression总是会返回expression,而false&&expression总是会返回false。

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

tips:falsy表达式会使&&后面的元素被跳过,但会返回falsy表达式的值。下面的例子中render方法的返回值是

0


render() {
  const count = 0;  
  return (
    <div>
      {count && <h1>Messages: {count}</h1>}    
     </div>
  );
}

三目运算符

JavaScript 中的三目运算符 condition ? true : false

阻止组件渲染

在极少数情况下,想要隐藏组件,即使它已经被其它组件渲染。若要完成此操作,可通过render方法直接返回null,而不进行任何渲染。

tips:render方法中返回null不会影响组件的生命周期。