Re: 新手让网页 act 起来: Day14 - Lift state

前言

刚学习 React 的时候,一定会碰到一个小问题,就是当我们有个值需要存在在两个元件之间,该怎麽处理? 而这个问题的答案很简单就是将 State 提升,今天就让我们来学习这个概念吧!

提升 State

假设今天有个需求是要做一个 BMI 计算功能。画面需要有两个 input 分别接收身高与体重与一个按钮,按下按钮会跑出 alert 视窗并显示 BMI。

import { useState } from 'react'

function calculateBMI({ height, weight }){
  if (!Number(height)) return NaN

  const heightNum = Number(height) / 100
  const weightNum = Number(weight)
  const result = weightNum / (heightNum * heightNum)

  return result.toFixed(2)
}

function BMIform(){
  const [ bodyInfo, setBodyInfo ] = useState({
    height: '',
    weight: ''
  })

  const { height, weight } = bodyInfo

  const onChangeHandler = (e) => {
    const { name, value } = e.target
    setBodyInfo(prev => ({...prev, [name]: value }))
  }

  const onSubmitHandler = (e) => {
    e.preventDefault()
    const result = calculateBMI(bodyInfo)
    const text = isNaN(result) ? '请输入正确的身高与体重' : `您的 BMI 为:${result}`
    
    alert(text)
  }

  return(
    <form onSubmit={onSubmitHandler}>
      <label>
        身高(公分):
        <input name='height' value={height} onChange={onChangeHandler}/>
      </label>
      <label>
        体重(公斤):
        <input name='weight' value={weight} onChange={onChangeHandler}/>
      </label>
      <button>计算</button>
    </form>
  )
}


function App() {
  return (
    <div className="App">
      <BMIform />
      <Display type='BMI' value={}/>
    </div>
  )
}

export default App

顺利的话当我们输入正确的身高与体重,按下计算,会成功跳出 alert 来。

接下来假设拿到了需求变更,用 alert 觉得不太好,想要改成在下方显示 BMI。然後我们已经有一个 Display 的元件,但需要传入 type 与 value 才能显示。

function Display({ type, value }){
  return(
    <div>
      您的 {type} 为: {value}
    </div>
  )
}

function App() {
  return (
    <div className="App">
      <BMIform />
      <Display type="BMI" />
    </div>
  )
}

这个时候我们必须把 state 从 BMIform 元件中提升到 App 元件,也就是共用 state 的两个元件最近的父层元件。将 state 提升後,在分别将 state 作为两个元件 props 传递下去。所以我们可以将范例改成这样:

function BMIform({ bodyInfo, onBMIChange }){
  const { height, weight } = bodyInfo

  return(
    <form>
      <label>
        身高(公分):
        <input name='height' value={height} onChange={onBMIChange}/>
      </label>
      <label>
        体重(公斤):
        <input name='weight' value={weight} onChange={onBMIChange}/>
      </label>
    </form>
  )
}

function Display({ type = 'BMI', value = null }){
  return(
    <div>
      您的 { type } 为: {value}
    </div>
  )
}

function App() {
  const [ bodyInfo, setBodyInfo ] = useState({
    height: '',
    weight: ''
  })
  
  const result = calculateBMI(bodyInfo)

  const onBMIChange = (e) => {
    const { name, value } = e.target
    
    setBodyInfo(prev => ({ ...prev, [name]: value }))
  }

  return (
    <div className="App">
      <BMIform bodyInfo={bodyInfo} onBMIChange={onBMIChange}/>
      { isNaN(result) ? '请输入正确的身高与体重' : <Display type='BMI' value={result}/> }
    </div>
  )
}

export default App

透过将共享的 state 提升到最靠近它们的共同父层,能够确保两个元件的资料来源都是通一份,并且保持同步。以上就是今天关於提升 state 的练习与介绍。有问题都欢迎在下方留言。

该文章同步发布於:我的部落格


<<:  Python 练习

>>:  Day 29 - Android Studio 这几天以来的统整

DAY 30- NFT & 结语

最後一篇,想说讲一下最近很夯的NFT,做为一个结尾。 NFT Non-Fungible Token,...

【如何快速学习新技能 ?】软技能 : 十步学习法

学习「怎样学习」 大纲 学习能力 软技能:代码之外的生存指南 十步学习法「概述」 第一部分:「研究」...

Day 17 | Flutter的常用 widgets - Container、Row、Column

StatefulWidget 的build 回到昨天 StatefulWidget 的 build ...

Day 26:扩充性

谈到扩充性,JUCE 以 Modules 为基础,开发者可提供自制 Module,供其他人使用。如下...

Day2-LeetCode 118. Pascal's Triangle

Problem: 118. Pascal's Triangle(Easy) Pascal's Tri...