React 快速上手 - 04 基礎特性 JSX、Props、State、Lifecycle、Event、Style

react

目錄

React 快速上手 - 04 基礎特性 JSX、Props、State、Lifecycle、Event、Style

目標

掌握 react 的基礎特性javascript

  • 語法 JSX
  • 屬性 Props
  • 狀態 State
  • 生命週期 Lifecycle
  • 事件 Event
  • 樣式 Style

基礎特性

react.js 自己只是一個精簡的類庫,提供了幾個特性或者說是工具,每一個話題深刻均可以長篇大論。css

我這裏只關注使用,畢竟輪子造出來仍是用的,而不是觀賞。html

1. JSX 語法

個人理解 jsx 就是 html + 表達式 的混合體前端

1.1 請用 ( ... ) 把 jsx 代碼包含起來

const Element1 = () => (<h2>組件1 - 常量</h2>)

這樣寫的理由java

  • 低版本的兼容性
  • 多行書寫不會報錯
固然新版裏若是你單行能夠省略

1.2 必須有個頂級標籤

錯誤
let Element4 = () => {
  return (
    <h2>組件4 - es6 箭頭函數</h2>
    <h2>組件4 - es6 箭頭函數</h2>
  )
}

正確
let Element4 = () => {
  return (
    <div>
      <h2>組件4 - es6 箭頭函數</h2>
      <h2>組件4 - es6 箭頭函數</h2>
    </div>
  )
}
若是你只有一個標籤,本身自己就是頂級標籤,無需加

1.3 { ... } 開始你的js表達式

function ElementShow(props) {
  return (
    <div>
      <p>字符串: {props.name} </p>
      <p>日期變量: {props.date.toLocaleTimeString()}</p>
    </div>
  )
}

分別打印 屬性值、時間函數react

1.4 對於沒有子元素的標籤來講,請關閉標籤

<div>
  <ElementProps />
</div>

結束符 /git

codepenes6

https://codepen.io/ducafecat/...github

2. 屬性 Props

2.1 屬性值 都是隻讀的

function ElementShow(props) {
  props.isShow = true // 只讀不能修改
  ...

打印截圖編程

props-read-only

2.2 特殊的幾個 屬性值

記憶就行,和咱們以前寫 html 有些差別

我看着都是由於 js 中保留字關係

jsx html
tabIndex index
className class
htmlFor for

示例

const ElementProps = () => (
  <div tabIndex="0" className="divbg" >
    JSX 屬性 tabIndex、className
  </div>
)

2.3 默認使用駝峯格式 camelCase

錯誤
<Foo
  UserName="hello"
  phone_number={12345678}
/>

正確
<Foo
  userName="hello"
  phoneNumber={12345678}
/>

2.4 若是屬性值爲 true, 能夠直接省略

錯誤
<Foo
  hidden={true}
/>

正確
<Foo
  hidden
/>

2.5 key 屬性是怎麼回事

示例

<ul>
  {NavRoutes.map((route, index) => (
    <li key={route.id}>
      {route.title}
    </li>
  ))}
</ul>

若是不寫呢

list-key

看來是繞不過的

總結

react利用key來識別組件,它是一種身份標識標識
同層的惟一就行,不用全局惟一
避免使用數組的index做爲 key

2.6 防注入攻擊

代碼

const jsContent = `
<script type="text/javascript">
alert("JSX 防注入攻擊!")
</script>`
const ElementInject = () => <div>{jsContent}</div>

打印

jsx-xss

內容在渲染以前都被轉換成了字符串,這樣能夠有效地防止 XSS(跨站腳本) 攻擊

2.7 childen 表示子節點對象

這個屬性表示,當前組件嵌套的對象集合

render() {
  return(
    <RadioGroup>
      <RadioButton value="first">First</RadioButton>
      <RadioButton value="second">Second</RadioButton>
      <RadioButton value="third">Third</RadioButton>
    </RadioGroup>
  )
}
RadioGroupprops.childen 就是這三個 RadioButton

你可能會問這有什麼用,咱們能夠用來加工呀,各類循環、克隆、修改,固然我是不太推薦這樣去修改 dom 對象

codepen

https://codepen.io/ducafecat/...

3. 狀態 State

組件內部的數據管理對象,自動狀態控制

有的同窗可能對 MVVM 比較瞭解,必定會說 React 怎麼沒有雙向綁定

這可能就是設計思想問題了,不想給工具賦予過多負重,輕巧才靈活,下一章,我會經過一個函數解決雙向綁定來處理表單操做,就幾行代碼

這裏咱們仍是談談基礎操做,懂得同窗能夠 PASS

3.1 初始化

class ElementStateStatic extends Component {
  constructor(props) {
    super(props)
    this.state = {date: new Date()}
  }
  render() {
    return <p>初始時間 => {this.state.date.toLocaleString()}</p>
  }
}
constructor 是組件構造函數,給 this.state 初始值時 要用 key/val 形式的對象
jsx 中使用時直接 this.state.data 對象調用便可

3.2 更新

class ElementStateUpdate extends Component {
  constructor(props) {
    super(props)
    this.date = props.date
    this.state = {date: new Date()}
  }
  componentDidMount() {
    if (this.date !== undefined) {
      // 傳值方式
      this.setState({date: this.date})
      // 函數方式
      // this.setState((state, props) => {
      //   return {date: this.date}
      // })
    }
  }
  render() {
    return <p>更新時間 => {this.state.date.toLocaleString()}</p>
  }
}
須要使用 this.setState 函數設置

簡單操做,直接 傳入 key / value 的對象

  • 若是須要以前的 state 或者組件屬性 props ,須要寫成
this.setState((state, props) => {
  let date = state.data
  date = date.addDay(10)
  return {date}
})

codepen

https://codepen.io/ducafecat/...

4. 生命週期 Lifecycle

4.1 組件有三種狀態

狀態 說明
Mount 已插入真實 DOM
Update 正在被從新渲染
Unmount 已移出真實 DOM

4.2 組件周期函數

react-lifecycle

狀態 說明
componentDidMount 在第一次渲染後調用,只在客戶端。
shouldComponentUpdate 返回一個布爾值。在組件接收到新的props或者state時被調用。
componentDidUpdate 在組件完成更新後當即調用。在初始化時不會被調用。
componentWillUnmount 在組件從 DOM 中移除的時候馬上被調用。
getDerivedStateFromProps 組件實例化後和接受新屬性時將會調用 新增
getSnapshotBeforeUpdate 在最新的渲染輸出提交給DOM前將會當即調用 新增

4.3 示例打印週期過程

代碼

class ElementLifecycle extends Component {
  constructor(props) {
    super(props)
    this.date = props.date
    this.state = {date: this.date}
  }
  componentDidMount() {
    console.log('componentDidMount 在第一次渲染後調用')
    if (this.date !== undefined) {
      this.setState({date: this.date})
    }
  }
  shouldComponentUpdate(nextProps, nextState) {
    console.log(
      'shouldComponentUpdate 在組件接收到新的props或者state時被調用',
      nextProps,
      nextState
    )
    return true // 返回一個布爾值,你們能夠試着在這裏返回 false
  }
  componentDidUpdate(prevProps, prevState) {
    console.log(
      'componentDidUpdate 在組件完成更新後當即調用',
      prevProps,
      prevState
    )
  }
  componentWillUnmount() {
    console.log('componentWillUnmount 在組件從 DOM 中移除的時候馬上被調用')
  }
  render() {
    return <p>時間 => {this.state.date.toLocaleString()}</p>
  }
}

打印截圖

react-lifecycle-console

codepen

https://codepen.io/ducafecat/...

4.4 react 16.x 新版本變更

4.4.1 計劃取消 3 個生命週期函數

分別是 componentWillMount, componentWillReceiveProps, componentWillUpdate

理由是在新的升級中,存在漏洞(在Facebook上,他們維護了超過50,000個React組件。 )

注意:
棄用警告將在將來的16.x版本中啓用,但舊版生命週期將繼續運行至17.x版。
即便在17.x版中,仍然可使用它們,但它們會以『UNSAFE_』爲前綴被重命名,以代表它們可能會引發問題。咱們還準備了一個自動化的腳原本在現有代碼中對它們從新命名。
UNSAFE_componentWillMount() UNSAFE_componentWillReceiveProps() UNSAFE_componentWillUpdate()
4.4.2 新增 getDerivedStateFromProps
組件實例化後和接受新屬性時將會調用

代碼

// 組件
class ElementLifecycleNew extends Component {
  constructor(props) {
    super(props)
    this.state = {}
  }
  static getDerivedStateFromProps(nextProps, prevState) {
    console.log(
      'getDerivedStateFromProps 組件實例化後和接受新屬性時將會調用',
      nextProps,
      prevState
    )
    // return null // 無需改變 返回 null
    return {
      date: new Date('2011-11-11 11:11:11')
    }
  }
  render() {
    return <p>{this.state.date.toLocaleString()}</p>
  }
}

// 調用
<ElementLifecycleNew date={new Date('2009-09-09 09:09:09')} />

若是你不想改變狀態 state , 返回 null

4.4.3 新增 getSnapshotBeforeUpdate + componentDidUpdate
getSnapshotBeforeUpdate() 在最新的渲染輸出提交給DOM前將會當即調用。它讓你的組件能在當前的值可能要改變前得到它們。這一輩子命週期返回的任何值將會 做爲參數被傳遞給 componentDidUpdate()
// 代碼
class ElementLifecycleNew2 extends Component {
  listRef = React.createRef()
  constructor(props) {
    super(props)
    this.state = {
      date: props.date
    }
  }
  componentDidMount() {
    console.log('componentDidMount')
    this.setState({date: new Date('2011-11-22 22:22:22')})
  }
  getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log('getSnapshotBeforeUpdate', prevProps, prevState, this.state)
    return {
      offset: 80
    }
  }
  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log('componentDidUpdate', snapshot)
    this.listRef.current.style.top = `${snapshot.offset}px`
  }
  render() {
    return (
      <div
        style={{
          height: 200,
          width: 150,
          backgroundColor: 'blue',
          position: 'relative',
          color: '#fff'
        }}
      >
        <p>{this.state.date.toLocaleString()}</p>
        <div
          ref={this.listRef}
          style={{
            height: 20,
            width: 150,
            backgroundColor: 'red',
            top: 0,
            position: 'absolute'
          }}
        />
      </div>
    )
  }
}

// 調用

這個例子的流程是:

1. `componentDidMount` 中修改了 state 觸發 `getSnapshotBeforeUpdate`
2. `getSnapshotBeforeUpdate` 獲取修改前的 屬性、狀態,已修改的 狀態,而後一個修改值 `offset`
3. `componentDidUpdate` 中的 `snapshot` 獲取修改值 ,直接 `ref` `dom` 修改 `style`
簡單說就是不修改 state 更新機制,來維護 dom,好比改改 寬 高 位置

5. 事件 Event

在 react 裏使用事件,寫法不少,這裏採用官方推薦的方式

5.1 無參數傳遞

  • 採用非箭頭函數定義事件
handleChange(e) {
    ...
  }
  • 在構造函數時綁定事件
class InputView extends Component {
  constructor(props) {
    ...
    this.handleChange = this.handleChange.bind(this)
  }
使用 bind(this) 方式
  • 綁定事件
<input
   ...
   onChange={this.handleChange}
/>

5.2 有參數傳遞

  • 事件參數要放在最後
handleChangeVal(val, e) {
    console.log(val)
    this.setState({value: e.target.value})
  }
  • 綁定事件
<input
  ...
  onChange={this.handleChangeVal.bind(this, '123')}
/>

5.3 例子

class InputView extends Component {
  constructor(props) {
    super(props)
    this.state = {value: ''}
    this.handleChange = this.handleChange.bind(this)
    this.handleSubmit = this.handleSubmit.bind(this)
  }
  handleChange(e) {
    this.setState({value: e.target.value})
  }
  handleChangeVal(val, e) {
    console.log(val)
    this.setState({value: e.target.value})
  }
  handleSubmit(e) {
    e.preventDefault() // 阻止事件
    console.log('handleSubmit')
  }
  render() {
    return (
      <form onSubmit={this.handleSubmit} style={{display: 'inline-flex'}}>
        <input
          type="text"
          value={this.state.value}
          onChange={this.handleChange}
        />
        <input
          type="text"
          value={this.state.value}
          onChange={this.handleChangeVal.bind(this, '123')}
        />
        <input type="submit" value="提交" />
        <p>{this.state.value}</p>
      </form>
    )
  }
}

codepen

https://codepen.io/ducafecat/...

6. 樣式 Style

有兩種方式維護樣式

6.1 import 樣式文件

  • base.css 樣式文件
.bg {
  background-color: rgb(101, 40, 241);
  color: white;
  font-weight: 500;
}
  • .js 中引入
import './base.css'

6.2 直接編寫 style

  • 編寫 style 對象
const styles = {}

styles.fill = {
  position: 'relative',
  height: '200px',
  width: '500px'
}

...
  • 傳值使用
<div style={styles.fill}>...
  • 混合使用
<div
    style={{
      ...styles.fill,
      ...styles.hsl,
      background: `hsl(${params.h}, ${params.s}%, ${params.l}%)`
    }}
  >

能夠發現這樣的寫法,對編程控制樣式仍是頗有用的

本文代碼

參考


© 會煮咖啡的貓咪
微信 ducafecat
鏡像 Git

相關文章
相關標籤/搜索