React_doc

React =》 构建用户界面的JS库,用户界面是由按钮、文本和图像等小的单元内容构建。
React可以组合成可重用、可嵌套的组件。

组件案例

function Profile() {
return (
<img src='https://i.xxx.com/test.jpg' alt=''/>
)
}
export default function Gallery() {
return (
<section>
<h1>Amazing scientists</h1>
<Profile/>
<Profile/>
<Profile/>
</section>
)
}

利用props传递数据给子组件

React组件会使用props来进行组件之间的通讯,每个父组件可以通过为子组件提供props的方式来传递信息
props =》 可以传递:对象、数组、函数、JSX等

function Card({ children }) {
return (
<div className='card'>
{children}
</div>
)
}
function Avatar({person, size}) {
return (
<img
className='avatar'
src={getImageUrl(person)}
alt={person.name}
width={size}
height={size}/>
)
}
export default function Profile() {
return (
<Card>
<Avatar
size={100}
person={{
name: 'T',
imageId: '12asd',
}}/>
</Card>
)
}

条件渲染

根据不同的条件来显示不同的东西,在React中,可使用JS语法,eg: if、&&、?:操作符实现有条件的渲染JSX

function Item({name, isPacked}) {
return (
<li className='item'>
{name} {isPacked && '✔'}
</li>
)
}
export default function PackingList() {
return (
<section>
<h1> Sally Ride's Packing List</h1>
<ul>
<Item isPacked={true} name='Space suit'/>
<Item isPacked={true} name='Helmet with a golden leaf'/>
<Item isPacked={false} name='Photo of Tam'/>
</ul>
</section>
)
}

渲染列表

针对数据集合进行遍历,在React中使用filter()和map()实现数组的过滤和转换,将数据数组转换为组件数组;

对于数组的每个元素向,指定一个key;

import {people} from './data.js';
ipmort { getImageUrl } from './utils.js';
export default function List() {
const listItems = people.map(person =>
<li key={person.id}>
<img src={getImageUrl(person)} alit=""/>
<p>
<b>{person.name}</b>
{' ' + person.name + ' ' }
known for { person.accomplishment }
</p>
</li>
};
return (
<article>
<h1>Scientist</h1>
<ul>{listItems}</ul>
</article>
)

Pure - Component

  • 只负责自己的任务,不会更改在该函数调用前已经存在的对象或变量
  • 输入相同,输出也相同:在输入相同的情况下,对纯函数来说总是返回相同的结果
let guest = 0;
function Cup() {
// Bad: changing a preexisting variable
guest = guest + 1;
return <h2>Tea Cup for guest ${guest}</h2>;
}
export default function TeaSet() {
return (
<>
<Cup/>
<Cup/>
<Cup/>
</>
)
}
// 通过传递props使得组件变得纯粹,而非修改已经有的变量
function Cup({ guest }) {
return <h2>Tea Cup for guest #{guest}</h2>;
}
export default function TeaSet() {
return (
<>
<Cup guest={1}/>
<Cup guest={2}/>
<Cup guest={3}/>
</>
)
}

在JSX中通过大括号使用JS

JSX允许在JS中编写类似HTML的标签,从而使得渲染的逻辑和内容可以融合在一起

  • JSX的大括号内引用JS变量
  • JSX的大括号内调用JS函数
  • JSX的大括号内使用JS对象
export default function Avatar() {
const avatar = 'http://www.baidu.com';
const desc == 'test ---------- ';
const userName = 'Wangz';
return (
<span>userName: {userName}</span>
<img className='avatar' src={avatar} alt={desc}/>
)
}
// 使用“双大括号”:JSX中的CSS和对象
// 除了字符串、数字和其他JS表达式,甚至可以在JSX中传递对象
// 对象也是用大括号表示 eg: {name: "wangzz", age: 24}
export default function TodoList() {
return (
<ul style={{backgroundColor: 'black', color: 'pink'}}>
<li> Improve the videophone</li>
<li> Perpare ae</li>
<li> Test</li>
</ul>
)
}

JSX小结:

  1. JSX引号内的值会作为字符串传递给属性
  2. 大括号可以将JS的逻辑和变量带入标签中
  3. 会在JSX标签中的内容区域或紧随属性的 = 后起到作用
  4. {{}}不会特殊语法: 只是包含在JSX大括号内的JS对象

将Props传递给组件

React组件会使用props互相同通信,每个父组件都可以提供props给子组件
从而将一些信息传递给子组件,props可传递对象、数组和函数

Props是不可变的,当一个组件需要改变其props的时候
将不得不请求其父组件传递不同的props —— 一个新对象,旧的props会被丢弃,
最终JS引擎会回收他们占用的内存;

不要尝试“更改props",当需要响应用户输入的时候,可以设置”state"可以在

添加交互

界面上的控件会根据用户的输入而更新,
点击按钮切换轮播图的显示,在React中,随着时间变化的数据称之为状态(state)
可以向任何组件添加状态,并按照需求进行更新。

响应事件

React允许向JSX中添加事件处理程序等,事件处理程序即函数,在用户交互的时候触发
eg: click、hover、focus、input、blur等

交互事件

界面控件根据用户的输入而更新,eg: 点击按钮切换轮播图的展示,在React中,随着时间变化的数据被称为状态(state)
可以向任何组件添加状态,并按照需求进行更新。

  • 响应事件
function Button({ onClick, children }) {
return (
<button onClick={onClick}>
{children}
</button>
)
}
function Toobar({ onPlayMovie, onUploadImage }){
return (
<Button onClick={onPlayMovie}>
Play Movie
</Button>
<Button onClick={onUploadImage}>
Upload Image
</Button>
)
}
export default function App() {
return (
<Toolbar
onPlayMovie={() => alert('Playing')}
onUploadImage={() => alert('UP}
/>
)
}

State: 组件的记忆

组件通常需要根据交互改变屏幕上的内容,在表单中键入更新输入栏
useState Hook为组件添加状态,Hook能够让组件使用React功能的特殊函数
useState声明一个状态变量,接收初始状态并返回一对值:当前状态以及一个更新状态的设置函数

const [index, setIndex] = useState(0);
const [showMore, setShowMore] = useState(false);
  • example
import { useState } from 'react';
import { sculptureList } from './data.js';
export default function Gallery() {
const [index, setIndex] = useState(0);
const [showMore, setShowMore] = useState(false);
const hasNext = index < sculptureList.length - 1;
function handleNextClick() {
if (hasNext) {
setIndex(index + 1);
} else {
setIndex(0);
}
}
function handleMoreClick() {
setShowMore(!showMore);
}
let sculpture = sculpture[index];
return (
<>
<button onClick={handleNextClick}>
Next
</button>
<h2>
<i>{sculpture.name}</i>
by {sculpture.artist}
</h2>
<h3>
({index+1} of {sculptureList.length})
</h3>
<button onClick={handleMore Click}>
{showMore ? 'Hide' : 'show'} details
</button>
<img src={sculpture.url} alt={ssculpture.alt}/>
</>
)
}

渲染和提交

在组件显示在屏幕之前需要由React进行渲染

  1. 触发渲染(将订单送到厨房)
  2. 渲染组件(按照订单准备)
  3. 提交到DOM(将订单送到桌前)

快照的状态

和普通JS变量不同,React状态的行为更像一个快照,设置它不会改变已有的状态变量,而是触发一次重新渲染

console.log(count); // 0
setCount(count + 1); // 请求用1重新渲染
console.log(count); // 0

更新状态中的对象

状态可以持有任何类型的JS值,(对象),★无法直接改变在React状态中持有的对象和数组。
当需要更新一个对象和数组的时候,需要创建一个新的对象(或复制现有的对象)
用这个副本来更新状态。
... 展开语法来赋值想要改变的对象和数组

import { useState } from 'react'
export default function Form() {
const [person, setPerson] = useState({
});
}

什么是mutation?

在state中存放任意类型的JS值

const [x, setX] = useState(0);
setX(5); // 从0 =》 5,数字0本身没有变,JS中无法内置原始值 eg: 数字、字符串和布尔值进行更改
// state存放对象
const [position, setPosition] = useState({x: 0, y: 0});
// 可以改变对象自身的内容,但是会制造一个mutation
position.x = 5;
// 因此你应该替换它们的值,而不是对它们进行修改。

★ 将state视为只读的

应该将所有存放在state中的JS对象都视为只读的

...展开语法本质是是“浅拷贝” —— 只会复制一层,可以提高执行速度

  • eg: 使用一个事件处理函数来更新多个字段
import { useState } from 'react'
export default function Form() {
const [person, setPerson] = useState({
firstName: 'Barbara',
lastName: 'Hepworth',
email: 'bheppasdfjpijpia',
});
function handleChange(e) {
setPerson({...person, [e.target.name]: e.target.value});
}
return (
<>
<label>
First name: <input/>
</label>
</>
)
}

更新一个嵌套对象

const [person, setPerson] = useState({
name: 'Niki',
artwork: {
title: 'Blue Nana',
city: 'Hamburg',
image: 'www.baidu.com',
}
});
// 将state视为不可变的,为了修改city的值,需要创建一个新的artwork对象
const nextArtwork = { ...person.artwork, city: 'NewDelhi'}
const nextPerson = { ...person, artwork: nextArtwork };
setPerson(nextPerson);
// or
setPerson({
...person, // 复制其他字段的数据
artwork: {
...person.artwork, // 复制之前person.artwork中的数据
city: 'New Delhi',
}
});
// ★ 使用Immer编写简介的更新逻辑
// 可以使用Immer刘幸苦来实现更为便捷的改变嵌套展开效果
updatePerson(draft => {
draft.artowork.city = 'Lagos';
});
// Immer提供的draft是一种特殊类型的对象,称之为Proxy
// 会记录所有的擦欧总,Immer会弄清楚draft对象的那些部分改变了,根据修改生成新对象

使用Immer

  1. npm install use-immer // 添加immer依赖
  2. import { useImmer } from 'use-immer' => 替换掉import { useState } from 'react'
import { useImmer } from 'use-immer';
export default function Form() {
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;
});
}
function handleTitleChange(e) {
updatePerson(draft => {
draft.artwork.title = e.target.value;
});
}
function handleCityChange(e) {
updatePerson(draft => {
draft.artwork.city = e.target.value;
});
}
function handleImageChange(e) {
updatePerson(draft => {
draft.artwork.image = e.target.value;
});
}
return (
<>
<label>
Name:
<input
value={person.name}
onChange={handleNameChange}
/>
</label>
<label>
Title:
<input
value={person.artwork.title}
onChange={handleTitleChange}
/>
</label>
<label>
City:
<input
value={person.artwork.city}
onChange={handleCityChange}
/>
</label>
<label>
Image:
<input
value={person.artwork.image}
onChange={handleImageChange}
/>
</label>
<p>
<i>{person.artwork.title}</i>
{' by '}
{person.name}
<br />
(located in {person.artwork.city})
</p>
<img
src={person.artwork.image}
alt={person.artwork.title}
/>
</>
);
}

事件处理函数变得更加简洁方便;随意在一个组件中同时使用useState和useImmer

为什么在React中无法推荐直接修改state?

将 React 中所有的 state 都视为不可直接修改的。
当你在 state 中存放对象时,直接修改对象并不会触发重渲染,并会改变前一次渲染“快照”中 state 的值。
不要直接修改一个对象,而要为它创建一个 新 版本,并通过把 state 设置成这个新版本来触发重新渲染。
你可以使用这样的 {...obj, something: 'newValue'} 对象展开语法来创建对象的拷贝。
对象的展开语法是浅层的:它的复制深度只有一层。
想要更新嵌套对象,你需要从你更新的位置开始自底向上为每一层都创建新的拷贝。
想要减少重复的拷贝代码,可以使用 Immer。

更新state中的数组

数组是另外一种存储在state中的JS对象,

slice => 拷贝数组或数组的一部分
splice => 会直接修改原始数组(插入或删除元素

数组正确修改方法

setArtists(
[
...artists, // 新数组包含原数组的所有元素
{id: nextId++, name: name} // 在末尾添加一个新的元素
]
);

从数组中删除元素 =》 filter或map来进行过滤

import { useState } from 'react';
let initialArtists = [
{ id: 0, name: 'Marta Colvin Andrate'},
{ id: 1, name: 'Lamidi Olonde Fakeye'},
{ id: 2, name: 'Louise Nevelson'},
]
export default function List() {
const [artists, setArtists] = useState(initialArtists);
return (
<>
{artists.map(artist => (
<li key={artist.id}>
{artist.name}{' '}
<button onClick={() => {
setArtists(
artists.filter(a =>
a.id !== artist.id
)
);
}}>
删除
</button>
</li>
))}
</>
)
}

转换数组

想要改变数组中的某些或全部元素,可以使用map()创建一个新数组,传入map的函数决定
要根据每个元素的值或索引对元素做出处理

map()和filter()不会直接修改原始数组,reverse()和sort()会改变原始数组

状态管理

=> 数据 =》 组件之间流动

状态响应输入

使用React,无需直接从Code层面修改UI,eg: 不需要编写‘禁用按钮’、‘启用按钮’、‘显示成功消息’等,只需要描述组件在不同状态(初始状态、输入状态、成功状态),
根据用户输入触发状态更改。

import { useState } from 'react'
export default function Form() {
const [anser, setAnswer] = useState('');
const [error, setError] = useState(null);
const [status, setStatus] = useState('typing');
if(status === 'success') {
return <h1>答对了!</h1>
}
async function handleSubmit(e) {
e.preventDefault();
setStatus('submitting');
try {
await submitForm(answer);
setStatus('success');
} catch (err) {
setStatus('typing');
setError(err);
}
}
function handleTextareaChange(e) {
setAnswer(e.target.value);
}
return (
<>
<form onSubmit={handleSubmit}>
<textarea
value={answer}
onChange={handleTextareaChange}
disabled={status === 'submitting'}
/>
<button disabled={answer.length === 0 || status === 'submitting'}>
提交
</button>
{
error !== null && <p className='Error'> {error.message} </p>
}
</form>
</>
)
}
function submitForm(answer) {
// 模拟接口请求
return new Promise((resolve, reject) => {
setTimeout(() => {
let shouldError = answer.toLowerCase() !== 'lima'
if(shouldError) {
reject(new Error('Right'));
} else {
resolve();
}
}, 1500);
});
}

状态结构

// 状态不应该包含冗余或重复的信息
// 如果包含一些多余的状态会忘记去更新,从而导致问题产生

import { useState } from 'react';
export default function Form() {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [fullName, setFullName] = useState('');
function handleFirstNameChange(e) {
setFirstName(e.target.value);
setFullName(e.target.value + '' + lastName);
}
}

在组件之间共享状态

希望两个组件的状态始终同步更改,将相关状态从两个组件上移除,
并将这些状态移动到最近的父级别组件,通过props将状态传递给两个组件
称之为“状态提升"

import { useState } from 'react'
export default function Accordion() {
const [activeIndex, setActiveIndex] = useState(0);
return (
<>
<Panel
title='关于'
isActive={activeIndex === 0}
onShow={() => setActiveIndex(0)}/>
</>
)
}

提取状态逻辑到reducer中

将需要更新多个状态的组件来说,在组件外部将所有状态更新逻辑合并到一个成为“reducer”的函数
事件处理程序会变得很简洁,只需要指定用户的“actions”,在文件底部,reducer函数指定状态
应该如何更新以响应每个action

import { useReducer } from 'react'
import AddTask from './AddTask.js'
import TaskList from './TaskList.js'
export default function TaskApp() {
const [tasks, dispatch] = useReducer(
tasksReducer,
initialTasks
);
function handleAddTask(text) {
dispatch({
type: 'added',
id: nextId++,
text: text,
});
}
function handleChangeTask(task) {
dispatch({
type: 'changed',
task: task,
})
}
function handleDeleteTask(taskId) {
dispatch({
type: 'deleted',
id: taskId,
});
}
return (
<>
<AddTask onAddTask={handleAddTask}/>
<TaskList
tasks={tasks}
onChangeTask={handleChangeTask}
onDeleteTask={handleDeleteTask}/>
</>
)
}
function tasksReducer(tasks, action) {
switch (action.type) {
case 'added': {
return [...tasks, {
id: action.id,
text: action.text,
done: false,
}]
}
case 'changed': {
return tasks.map(t => {
if(t.id === action.task.id) {
return action.task;
} else {
return t;
}
});
}
case 'deleted': {
return tasks.filter(t => t.id !== action.id);
}
default: {
throw Error('Unknown Operation: ' + action.type);
}
}
}
let nextId = 3;
const initialTasks = [
{ id: 0, text: '参观卡夫卡博物馆', done: true },
{ id: 1, text: '看木偶戏', done: false },
{ id: 2, text: '列侬墙图片', done: false }
];

使用Reducer和Context进行状态扩展

  • Reducer: 合并组件的状态更新逻辑
  • Context:将信息深入传递给其他组件
    利用Reducer和Context组合在一起实现对复杂应用状态的管理

使用reducer来管理一个具有复杂状态的父组件
组件树中任何深度的其他组件都可以通过context读取状态,
再通过dispatch来更新状态

posted @   Felix_Openmind  阅读(29)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
*{cursor: url(https://files-cdn.cnblogs.com/files/morango/fish-cursor.ico),auto;}
点击右上角即可分享
微信分享提示