Complex state 复杂状态
如果我们的应用需要一个更复杂的状态怎么办?在大多数情况下,实现这一点的最简单和最好的方法是多次使用 useState 函数来创建单独的状态“片段”。
import React, { useState } from "react"
const App = () => {
const [left, setLeft] = useState(0)
const [right, setRight] = useState(0)
return (
<div>
{left}
<button onClick={() => setLeft(left + 1)}>
left
</button>
<button onClick={() => setRight(right + 1)}>
right
</button>
{right}
</div>
)
}
export default App
如果将2个状态存储在一个对象中呢?
import React, { useState } from "react"
const App = () => {
const [clicks, setClicks] = useState({
left: 0, right: 0
})
const handleLeftClick = () => {
const newClicks = {
left: clicks.left + 1,
right: clicks.right
}
setClicks(newClicks)
}
-- snip --
return (
<div>
{clicks.left}
<button onClick={handleLeftClick}>
left
</button>
-- snip --
</div>
)
}
export default App
我们可以通过使用对象的展开语法更加整洁地定义新的状态对象
对象的相同属性会被合并
let obj1 = { foo: 'bar', x: 42 };
let obj2 = { foo: 'baz', y: 13 };
let clonedObj = { ...obj1 };
// Object { foo: "bar", x: 42 }
let mergedObj = { ...obj1, ...obj2 };
// Object { foo: "baz", x: 42, y: 13 }
因此设置新状态可以改写为
const handleLeftClick = () =>
setClicks({ ...clicks, left: clicks.left + 1 })
const handleRightClick = () =>
setClicks({ ...clicks, right: clicks.right + 1 })
你可能会问为什么不直接更新状态?像这样:
const handleLeftClick = () => {
clicks.left++
setClicks(clicks)
}
这违反了React 中状态不可直接修改的原则,因为它会导致意想不到的副作用。 必须始终通过将状态设置为新对象来更改状态。 如果之前的状态没有变化,属性仅仅需要简单地复制,就是通过将这些属性复制到新的对象中,并将其设置为新状态。
对于这个特定的应用来说,将所有状态存储在单个状态对象中是一个糟糕的选择; 没有明显的好处,还会导致产生的应用要复杂得多。 在这种情况下,将点击计数器存储到单独的状态块中是一个更合适的选择。
在某些情况下,将一段应用状态存储在更复杂的数据结构中是有益的。
Handling arrays 处理数组
向数组中添加新元素是通过concat方法完成的,该方法不改变现有数组,而是返回数组 新副本,并将元素添加到该数组中。如果用push方法会直接改变状态,这违反react原则
数组可以通过join方法连接成一个字符串
import React, { useState } from "react"
const App = () => {
const [left, setLeft] = useState(0)
const [right, setRight] = useState(0)
const [allClicks, setAll] = useState([])
const handleLeftClick = () => {
setAll(allClicks.concat('L'))
setLeft(left + 1)
}
-- snip --
return (
<div>
{left}
<button onClick={handleLeftClick}>
left
</button>
-- snip --
<p>{allClicks.join(' ')}</p>
</div>
)
}
export default App
Conditional rendering 条件渲染
import React, { useState } from "react"
const History = (props) => {
if (props.allClicks.length === 0) {
return (
<div>
the app is used by pressing the buttons
</div>
)
}
return (
<div>
button press history: {props.allClicks.join(' ')}
</div>
)
}
const App = () => {
-- snip --
return (
<div>
-- snip --
<History allClicks={allClicks} />
</div>
)
}
export default App
Debugging React applications 调试应用
典型的开发人员的大部分时间都花在调试和读取现有代码上。 我们时不时地会写一两行新代码,但是我们的大部分时间都花在试图弄明白为什么有些东西坏了,或者某些东西是如何工作的上面。 出于这个原因,良好的调试实践和工具非常重要。
The first rule of web development web开发第一原则
Keep the browser’s developer console open at all times. 始终打开浏览器的开发控制台 The Console tab in particular should always be open, unless there is a specific reason to view another tab. 尤其是Console 选项卡应该始终处于打开状态,除非有特定的原因需要查看另一个选项卡。
老派的,基于打印的调试总是一个好主意。注意在console.log里用逗号分隔,而不是+号
console.log('props value is', props)
你可以在 Chrome 开发者控制台的debugger 中暂停应用代码的执行,只需在代码中的任何地方写入命令debugger即可。
强烈建议在 Chrome 中添加 React developer tools扩展。 它为开发工具增加了一个新的 Components 选项卡。新的开发者工具页可以用来检查不同的React 元素,以及它的属性和状态:
Rules of Hooks
不能从循环、条件表达式或任何不是定义组件的函数的地方调用 useState 和 useEffect
这样做是为了确保Hook总是以相同的顺序调用,如果不是这样,应用的行为就会不规则。
const App = () => {
// these are ok
const [age, setAge] = useState(0)
const [name, setName] = useState('Juha Tauriainen')
if ( age > 10 ) {
// this does not work!
const [foobar, setFoobar] = useState(null)
}
for ( let i = 0; i < age; i++ ) {
// also this is not good
const [rightWay, setRightWay] = useState(false)
}
const notGood = () => {
// and this is also illegal
const [x, setX] = useState(-1000)
}
return (
//...
)
}
Event Handling Revisited 复习事件处理
下面的代码,当组件渲染时会在控制台打出字符串,但是点击按钮时什么也不会发生,console.log方法的返回值是undefined
<button onClick={console.log('clicked the button')}>
button
</button>
事件处理程序必须始终是函数或对函数的引用
只有单击按钮时才调用该函数。
<button onClick={() => console.log('clicked the button')}>
button
</button>
在按钮的属性中直接定义事件处理程序不一定是最好的方法。
您经常会看到事件处理程序定义在一个单独的位置。
Function that returns a function 返回函数的函数
定义事件处理程序的另一种方法是使用返回函数的函数。
const App = () => {
const [value, setValue] = useState(10)
const hello = (who) => {
const handler = () => {
console.log('hello', who)
}
return handler
}
return (
<div>
{value}
<button onClick={hello('world')}>button</button>
<button onClick={hello('react')}>button</button>
<button onClick={hello('function')}>button</button>
</div>
)
}
将
组件渲染时,返回的函数赋值给了onClick
可以将hello函数简写如下:
const hello = (who) => () => {
console.log('hello', who)
}
Do Not Define Components Within Components
不要在组件内部定义组件,这种方法没有任何好处,而且会导致许多不愉快的问题。最大的问题是React 在每次渲染时,会将内部的组件当作一个新的组件。这回导致React 无法去优化组件。
Exercises 1.6.-1.14
create-react-app 会自动使项目成为一个 git 仓库,除非应用是在已有仓库中创建的。 而您很可能不希望项目成为一个存储库,因此可以在项目的根目录中运行命令
rm -rf .git
在某些情况下,您可能还必须从项目的根目录运行如下命令:
rm -rf node_modules/ && npm i
1.6 - 1.11 unicafe调查统计
import React, { useState } from 'react'
const Button = ({ handleClick, text }) => (
<button onClick={handleClick}>{text}</button>
)
const Statistic = ({ text, value }) => (
<tr>
<td>{text}</td>
<td>{value}</td>
</tr>
)
const Statistics = ({ good, neutral, bad, all, average, positive }) => {
if (all === 0) {
return <div>No feedback given</div>
} else return (
<table>
<tbody>
<Statistic text={'good'} value={good} />
<Statistic text={'neutral'} value={neutral} />
<Statistic text={'bad'} value={bad} />
<Statistic text={'all'} value={all} />
<Statistic text={'average'} value={average} />
<Statistic text={'positive'} value={positive} />
</tbody>
</table>
)
}
const App = () => {
const [good, setGood] = useState(0)
const [neutral, setNeutral] = useState(0)
const [bad, setBad] = useState(0)
let all = good + neutral + bad
let average = good - bad / 3
let positive = all !== 0 ? (good / all) * 100 + '%' : '0'
return (
<div>
<h1>Give Feedback</h1>
<div>
<Button handleClick={() => setGood(good + 1)} text={'good'} />
<Button handleClick={() => setNeutral(neutral + 1)} text={'neutral'} />
<Button handleClick={() => setBad(bad + 1)} text={'bad'} />
</div>
<h2>Statistics</h2>
<Statistics good={good} neutral={neutral} bad={bad}
all={all} average={average} positive={positive} />
</div>
)
}
export default App;
1.12 - 1.14 anecdotes
重点:
- 用map生成零填充数组
- 找出最大得分
- 随机数生成
- 复制数组
- 利用setState后页面刷新的原理 ```javascript import React, { useState } from ‘react’
const Anecdote = ({ anecdote, point, title }) => ( <>
{title}
const App = () => { const anecdotes = [ ‘If it hurts, do it more often’, ‘Adding manpower to a late software project makes it later!’, ‘The first 90 percent of the code accounts for the first 90 percent of the development time…The remaining 10 percent of the code accounts for the other 90 percent of the development time.’, ‘Any fool can write code that a computer can understand. Good programmers write code that humans can understand.’, ‘Premature optimization is the root of all evil.’, ‘Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.’, ‘Programming without an extremely heavy use of console.log is same as if a doctor would refuse to use x-rays or blod tests when dianosing patients’ ]
const [selected, setSelected] = useState(0) const [points, setPoints] = useState(anecdotes.map(item => 0))
const randomNumber = () => { const num = Math.floor(Math.random() * anecdotes.length) setSelected(num) }
const changePoints = () => { const newPoints = […points] newPoints[selected] += 1 setPoints(newPoints) }
const findMostVotes = () => { let maxNumber = points[0] for (let i = 1; i < points.length; i++) { if (points[i] > maxNumber) { maxNumber = points[i] } }
return points.indexOf(maxNumber)
}
return (
export default App ```