react学习笔记
Hook
以 use 开头的函数被称为 Hook。Hook 比普通函数更为严格,只能在组件(或其他 Hook)的 顶层 调用 Hook。如果想在一个条件或循环中使用 useState,请提取一个新的组件并在组件内部使用它。
useState
useState是 React 提供的一个内置 Hook,用来创建响应式变量。
import { useState } from 'react';
const [count, setCount] = useState(0);
你将从 useState 中获得两样东西:当前的 state(count),以及用于更新它的函数(setCount)。
创建多个变量可以通过对象的键值对
const [user, setUser] = useState({
id: 1,
name: '张三',
auth: 'admin'
});
setUser({
...user,
name: '李四'
});
应该把state变量视为只读的
state初始值可以是任意js类型,修改时是创建新数据覆盖原有的数据,不应该直接更改,直接更改违背react设计理念。
const [user, setUser] = useState({
id: 1,
name: '张三',
auth: 'admin'
});
const changeUser = ()=>{
//不应该直接修改
user.name = '李四' // X
setUser({
...user,
name: '李四'
});
}
如果是一个多层嵌套的对象,可以用 Immer 库来创建对象
import { useImmer } from 'use-immer';
const [person, updatePerson] = useImmer({
name: 'Niki de Saint Phalle',
artwork: {
title: 'Blue Nana',
city: 'Hamburg',
image: 'https://i.imgur.com/Sd1AgUOm.jpg',
}
});
function handleNameChange(e) {
updatePerson(draft => {
draft.name = e.target.value;
});
}
state变量的不变性,不会立即更新
state变量在程序运行时不会改变, 始终是初始值,或者是上次改变的值。 渲染后, 再次读取state才是上次改变的值, 在函数里改变了state,如果接下来立即读取他, 读取到的是程序开始运行时的值,不是改变的值,即时在异步函数中对state做处理,读取到的也是初始值。
原因:React 会等到事件处理函数中的 所有 代码都运行完毕再处理你的 state 更新。 这就是为什么重新渲染只会发生在所有这些 setNumber() 调用 之后 的原因。
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(number + 5);//理解为:setNumber(0 + 5)
setTimeout(() => {
alert(number); //这里读取到的是初始值 0 不会是5,理解为 alert(0);
}, 1000);
}}>+5</button>
</>
)
}
如果想在函数执行期间修改state的值,可以用 更新函数 来改变。
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);
}}>+3</button>
</>
)
}
React 使用单向数据流,useState定义的数据通过组件层级结构从父组件传递数据至子组件。
设计 state 应该被放置于哪里:
- 通常情况下,你可以直接放置 state 于它们共同的父组件。
- 你也可以将 state 放置于它们父组件上层的组件。
- 如果你找不到一个有意义拥有这个 state 的地方,单独创建一个新的组件去管理这个 state,并将它添加到它们父组件上层的某个地方。
3000 x 10
下跌3% 2 6000
-
26000 - 780 + 6000 = 31220 32000
-
32000- 960 + 6000 = 37040 38000
-
38000 - 1140 + 6000 =42860 44000
-
44000 - 1320 + 6000 = 48680 50000
抛 止损
上涨2% 1 3000
-
26000 + 520 + 3000 = 29520
-
29000+ 520 + 580 + 3000 = 33100
-
31000 + 520 +580 +620 +3000 = 35720
组件
根组件 - 顶层组件 - 叶组件
React 组件是一段可以使用标签进行扩展的 JavaScript 函数,是构建用户界面(UI)的基础,组件的名称必须以大写字母开头,并返回JSX标签
export default function Profile() {
return (
<img
src="https://i.imgur.com/MK3eW3Am.jpg"
alt="Katherine Johnson"
/>
)
}
纯函数定义组件
纯函数:保持组件的纯粹,只负责自己的任务,输入相同,输出也相同。React 便围绕着这个概念进行设计。React 假设你编写的所有组件都是纯函数。
使用纯函数的好处: 避免一些奇怪的bug,逻辑更清晰, 对于一些确定输入相同的纯函数,可以跳过渲染,提升性能。
props组件通信
不要在组件中定义组件,那会是应用程序变的非常卡顿。 应该在文件的最顶层定义各个组件,父子组件可以通过prop传递数据。
- 如何向组件传递 props
- 如何从组件读取 props
- 如何为 props 指定默认值
- 如何给组件传递 JSX
- Props 如何随时间变化
props可以传递任何js值,包括对象,数组,函数。
父传子
//子组件 count = 1默认为1 没有传递count或者count={undefined}时生效。
const Mybutton = ({count = 1,play}) =>{
return <button onClick={play}>{count}</button>
}
//父组件
const Page = ()=>{
return (
<h3>html标题</h3>
<Mybutton count={1} play=()=>{console.log(121)}></Mybutton>
)
}
一个组件接收另一个组件作为参数,并渲染这个子组件.父组件将在名为 children 的 prop 中接收到该内容。
import Avatar from './Avatar.js';
function Card({ children }) {
return (
<div className="card">
{children}
</div>
);
}
export default function Profile() {
return (
<Card>
<Avatar
size={100}
person={{
name: 'Katsuko Saruhashi',
imageId: 'YfeOqp2'
}}
/>
</Card>
);
}
一个组件接受JSX代码作为参数,并渲染JSX代码
const Card = ({children}) => {
return (
<div className="card">
<div className="card-content">{children}</div>
</div>
);
};
export default function Profile() {
return (
<div>
<Card>
<h1>Photo</h1>
<img className="avatar" src="https://i.imgur.com/OKS67lhm.jpg" alt="Aklilu Lemma" width={70} height={70} />
</Card>
<Card>
<h1>About</h1>
<p>Aklilu Lemma was a distinguished Ethiopian scientis.</p>
</Card>
</div>
);
}
交互
响应事件
- 必须传递事件处理函数,而非函数调用!
onClick={handleClick},不是onClick={handleClick()}。带括号是立即调用,会在渲染阶段触发事件。
1.阻止事件冒泡 e.stopPropagation()
<button onClick={e => {
e.stopPropagation();
onClick();
}}>
点击按钮
</button>
2.阻止浏览器默认行为 e.preventDefault()
export default function Signup() {
return (
<form onSubmit={e => {
e.preventDefault();
alert('提交表单!');
}}>
<input />
<button>发送</button>
</form>
);
}
面试知识点
为什么多个JSX标签需要被一个父元素包裹?
JSX 虽然看起来很像 HTML,但在底层其实被转化为了 JavaScript 对象,你不能在一个函数中返回多个对象,除非用一个数组把他们包装起来。这就是为什么多个 JSX 标签必须要用一个父元素或者 Fragment(空标签) 来包裹。