当前位置: 首页 > news >正文

做网站都需要买什么wordpress静态优化

做网站都需要买什么,wordpress静态优化,永久免费的财务软件,花市小说网站那里进文章目录 用 State 响应输入声明式地考虑 UI步骤 1#xff1a;定位组件中不同的视图状态步骤 2#xff1a;确定是什么触发了这些状态的改变步骤 3#xff1a;通过 useState 表示内存中的 state步骤 4#xff1a;删除任何不必要的 state 变量步骤 5#xff1a;连接事件处理… 文章目录 用 State 响应输入声明式地考虑 UI步骤 1定位组件中不同的视图状态步骤 2确定是什么触发了这些状态的改变步骤 3通过 useState 表示内存中的 state步骤 4删除任何不必要的 state 变量步骤 5连接事件处理函数以设置 state 选择 State 结构构建 state 的原则 在组件间共享状态举例说明一下状态提升第 1 步: 从子组件中移除状态第 2 步: 从公共父组件传递硬编码数据第 3 步: 为公共父组件添加状态受控组件和非受控组件 对 state 进行保留和重置状态与渲染树中的位置相关相同位置的相同组件会使得 state 被保留下来相同位置的不同组件会使 state 重置在相同位置重置 state方法一将组件渲染在不同的位置方法二使用 key 来重置 state为被移除的组件保留 state 迁移状态逻辑至 Reducer 中使用 reducer 整合状态逻辑第 1 步: 将设置状态的逻辑修改成 dispatch 的一个 action第 2 步: 编写一个 reducer 函数语法示例使用场景注意事项示例第 3 步: 在组件中使用 reducer 对比 useState 和 useReducer 使用 Context 深层传递参数传递 props 带来的问题Context传递 props 的另一种方法写在你使用 context 之前Context 的使用场景 使用 Reducer 和 Context 拓展你的应用应急方案使用 ref 引用值给你的组件添加 refref 和 state 的不同之处useRef 内部是如何运行的 何时使用 refref 的最佳实践 使用 ref 操作 DOM获取指向节点的 ref访问另一个组件的 DOM 节点 使用 Effect 同步如何编写 Effect第一步声明 Effect第二步指定 Effect 依赖第三部按需添加清理cleanup函数订阅事件触发动画初始化应用时不需要使用 Effect 的情形 你可能不需要 Effect响应式 Effect 的生命周期Effect 的生命周期 []以下内容来自官方文档 https://zh-hans.react.dev/learn](https://zh-hans.react.dev/learn) 用 State 响应输入 React 控制 UI 的方式是声明式的。你不必直接控制 UI 的各个部分只需要声明组件可以处于的不同状态并根据用户的输入在它们之间切换 声明式地考虑 UI 你已经从上面的例子看到如何去实现一个表单了为了更好地理解如何在 React 中思考接下来你将会学到如何用 React 重新实现这个 UI 定位你的组件中不同的视图状态确定是什么触发了这些 state 的改变表示内存中的 state需要使用 useState删除任何不必要的 state 变量连接事件处理函数去设置 state 步骤 1定位组件中不同的视图状态 在计算机科学中你或许听过可处于多种“状态”之一的 “状态机”首先你需要去可视化 UI 界面中用户可能看到的所有不同的“状态” 无数据表单有一个不可用状态的“提交”按钮。输入中表单有一个可用状态的“提交”按钮。提交中表单完全处于不可用状态加载动画出现。成功时显示“成功”的消息而非表单。错误时与输入状态类似但会多错误的消息。 步骤 2确定是什么触发了这些状态的改变 你可以触发 state 的更新来响应两种输入 人为输入。比如点击按钮、在表单中输入内容或导航到链接。计算机输入。比如网络请求得到反馈、定时器被触发或加载一张图片 你需要改变 state 以响应几个不同的输入 改变输入框中的文本时人为应该根据输入框的内容是否是空值从而决定将表单的状态从空值状态切换到输入中或切换回原状态。点击提交按钮时人为应该将表单的状态切换到提交中的状态。网络请求成功后计算机应该将表单的状态切换到成功的状态。网络请求失败后计算机应该将表单的状态切换到失败的状态与此同时显示错误信息 步骤 3通过 useState 表示内存中的 state 如果你很难立即想出最好的办法那就先从添加足够多的 state 开始确保所有可能的视图状态都囊括其中 const [isEmpty, setIsEmpty] useState(true);const [isTyping, setIsTyping] useState(false);const [isSubmitting, setIsSubmitting] useState(false);const [isSuccess, setIsSuccess] useState(false);const [isError, setIsError] useState(false);你最初的想法或许不是最好的但是没关系重构 state 也是步骤中的一部分 步骤 4删除任何不必要的 state 变量 在清理之后你只剩下 3 个从原本的 7 个_必要_的 state 变量 const [answer, setAnswer] useState();const [error, setError] useState(null);const [status, setStatus] useState(typing); // typing, submitting, or success步骤 5连接事件处理函数以设置 state 练习1 import {useState} from react export default function Picture() {const [imgcss,setImgcss] useState(false)function handleBgd(){setImgcss(false)}function handleImg(e){e.stopPropagation()setImgcss(true)}let bgdClassName backgroundlet imgClassName pictureif(imgcss){imgClassName picture--active //注意此处的空格}else{bgdClassName background--active //注意此处的空格}return (div className{bgdClassName} onClick{()setImgcss(false)}imgonClick{e {e.stopPropagation();setImgcss(true)}}className{imgClassName}altRainbow houses in Kampung Pelangi, Indonesiasrchttps://i.imgur.com/5qwVYb1.jpeg//div); } 练习2 import {useState} from react export default function EditProfile() {const [edit,setEdit] useState(false)const [firstName,setFirstName] useState()const [lastName,setLastName] useState()return (form onSubmit{(e){e.preventDefault()setEdit(!edit)}}labelFirst name:{ }{edit? input onChange{(e){setFirstName(e.target.value)}} value{firstName}/:b{firstName}/b}/labellabelLast name:{ }{edit? input onChange{(e){setLastName(e.target.value)}} value{lastName}/:b{lastName}/b}/labelbutton typesubmit{edit? save : edit } Profile/buttonpiHello, {firstName} {lastName}!/i/p/form); } 选择 State 结构 构建 state 的原则 当你编写一个存有 state 的组件时你需要选择使用多少个 state 变量以及它们都是怎样的数据格式。尽管选择次优的 state 结构下也可以编写正确的程序但有几个原则可以指导您做出更好的决策 合并关联的 state。如果你总是同时更新两个或更多的 state 变量请考虑将它们合并为一个单独的 state 变量。避免互相矛盾的 state。当 state 结构中存在多个相互矛盾或“不一致”的 state 时你就可能为此会留下隐患。应尽量避免这种情况。避免冗余的 state。如果你能在渲染期间从组件的 props 或其现有的 state 变量中计算出一些信息则不应将这些信息放入该组件的 state 中。避免重复的 state。当同一数据在多个 state 变量之间或在多个嵌套对象中重复时这会很难保持它们同步。应尽可能减少重复。避免深度嵌套的 state。深度分层的 state 更新起来不是很方便。如果可能的话最好以扁平化方式构建 state。 在组件间共享状态 有时候你希望两个组件的状态始终同步更改。要实现这一点可以将相关 state 从这两个组件上移除并把 state 放到它们的公共父级再通过 props 将 state 传递给这两个组件。这被称为“状态提升”这是编写 React 代码时常做的事 举例说明一下状态提升 在这个例子中父组件 Accordion 渲染了 2 个独立的 Panel 组件。 Accordion PanelPanel 每个 Panel 组件都有一个布尔值 isActive用于控制其内容是否可见 import { useState } from react;function Panel({ title, children }) {const [isActive, setIsActive] useState(false);return (section classNamepanelh3{title}/h3{isActive ? (p{children}/p) : (button onClick{() setIsActive(true)}显示/button)}/section); }export default function Accordion() {return (h2哈萨克斯坦阿拉木图/h2Panel title关于阿拉木图人口约200万是哈萨克斯坦最大的城市。它在 1929 年到 1997 年间都是首都。/PanelPanel title词源这个名字来自于 span langkk-KZалма/span哈萨克语中“苹果”的意思经常被翻译成“苹果之乡”。事实上阿拉木图的周边地区被认为是苹果的发源地i langlaMalus sieversii/i 被认为是现今苹果的祖先。/Panel/); } 假设现在您想改变这种行为以便在任何时候只展开一个面板。在这种设计下展开第 2 个面板应会折叠第 1 个面板。您该如何做到这一点呢”要协调好这两个面板我们需要分 3 步将状态“提升”到他们的父组件中。 从子组件中 移除 state 。从父组件 传递 硬编码数据。为共同的父组件添加 state 并将其与事件处理函数一起向下传递 第 1 步: 从子组件中移除状态 你将把 Panel 组件对 isActive 的控制权交给他们的父组件。这意味着父组件会将 isActive 作为 prop 传给子组件 Panel。我们先从 Panel 组件中 删除下面这一行 const [isActive, setIsActive] useState(false);然后把 isActive 加入 Panel 组件的 props 中 function Panel({ title, children, isActive }) {第 2 步: 从公共父组件传递硬编码数据 import { useState } from react;export default function Accordion() {return (h2哈萨克斯坦阿拉木图/h2Panel title关于 isActive{true}阿拉木图人口约200万是哈萨克斯坦最大的城市。它在 1929 年到 1997 年间都是首都。/PanelPanel title词源 isActive{true}这个名字来自于 span langkk-KZалма/span哈萨克语中“苹果”的意思经常被翻译成“苹果之乡”。事实上阿拉木图的周边地区被认为是苹果的发源地i langlaMalus sieversii/i 被认为是现今苹果的祖先。/Panel/); }function Panel({ title, children, isActive }) {return (section classNamepanelh3{title}/h3{isActive ? (p{children}/p) : (button onClick{() setIsActive(true)}显示/button)}/section); } 第 3 步: 为公共父组件添加状态 状态提升通常会改变原状态的数据存储类型。在这个例子中一次只能激活一个面板。这意味着 Accordion 这个父组件需要记录 哪个 面板是被激活的面板。我们可以用数字作为当前被激活 Panel 的索引而不是 boolean 值 const [activeIndex, setActiveIndex] useState(0);import { useState } from react;export default function Accordion() {const [activeIndex, setActiveIndex] useState(0);return (h2哈萨克斯坦阿拉木图/h2Paneltitle关于isActive{activeIndex 0}onShow{() setActiveIndex(0)}阿拉木图人口约200万是哈萨克斯坦最大的城市。它在 1929 年到 1997 年间都是首都。/PanelPaneltitle词源isActive{activeIndex 1}onShow{() setActiveIndex(1)}这个名字来自于 span langkk-KZалма/span哈萨克语中“苹果”的意思经常被翻译成“苹果之乡”。事实上阿拉木图的周边地区被认为是苹果的发源地i langlaMalus sieversii/i 被认为是现今苹果的祖先。/Panel/); }function Panel({title,children,isActive,onShow }) {return (section classNamepanelh3{title}/h3{isActive ? (p{children}/p) : (button onClick{onShow}显示/button)}/section); } 受控组件和非受控组件 通常我们把包含“不受控制”状态的组件称为“非受控组件”。例如最开始带有 isActive 状态变量的 Panel 组件就是不受控制的因为其父组件无法控制面板的激活状态。相反当组件中的重要信息是由 props 而不是其自身状态驱动时就可以认为该组件是“受控组件”。这就允许父组件完全指定其行为。最后带有 isActive 属性的 Panel 组件是由 Accordion 组件控制的练习1 import { useState } from react;export default function SyncedInputs() {const [text,setText] useState()function handleChange(e){setText(e.target.value)}return (Input label第一个输入框 onChange{handleChange} text{text}/Input label第二个输入框 onChange{handleChange} text{text}//); }function Input({ label, onChange, text }) {//const [text, setText] useState();/*function handleChange(e) {setText(e.target.value);}*/return (label{label}{ }inputvalue{text}onChange{onChange}//label); } 练习2 import { useState } from react; import { foods, filterItems } from ./data.js;export default function FilterableList() {const [query,setQuery] useState()function handleQuery(e){setQuery(e.target.value)}return (SearchBar text{query} onChange{handleQuery}/hr /List items{foods} text{query}//); }function SearchBar({text,onChange}) {/*const [query, setQuery] useState();function handleChange(e) {setQuery(e.target.value);}*/return (label搜索{ }inputvalue{text}onChange{onChange}//label); }function List({ items,text }) {return (tabletbody{filterItems(items,text).map(food (tr key{food.id}td{food.name}/tdtd{food.description}/td/tr))}/tbody/table); } 对 state 进行保留和重置 状态与渲染树中的位置相关 只有当在树中相同的位置渲染相同的组件时React 才会一直保留着组件的 state import { useState } from react;export default function App() {const [showB, setShowB] useState(true);return (divCounter /{showB Counter /} labelinputtypecheckboxchecked{showB}onChange{e {setShowB(e.target.checked)}}/渲染第二个计数器/label/div); }function Counter() {const [score, setScore] useState(0);const [hover, setHover] useState(false);let className counter;if (hover) {className hover;}return (divclassName{className}onPointerEnter{() setHover(true)}onPointerLeave{() setHover(false)}h1{score}/h1button onClick{() setScore(score 1)}加一/button/div); } 注意当你停止渲染第二个计数器的那一刻它的 state 完全消失了。这是因为 React 在移除一个组件时也会销毁它的 state当你重新勾选“渲染第二个计数器”复选框时另一个计数器及其 state 将从头开始初始化score 0并被添加到 DOM 中。 相同位置的相同组件会使得 state 被保留下来 import { useState } from react;export default function App() {const [isFancy, setIsFancy] useState(false);return (div{isFancy ? (Counter isFancy{true} / ) : (Counter isFancy{false} / )}labelinputtypecheckboxchecked{isFancy}onChange{e {setIsFancy(e.target.checked)}}/使用好看的样式/label/div); }function Counter({ isFancy }) {const [score, setScore] useState(0);const [hover, setHover] useState(false);let className counter;if (hover) {className hover;}if (isFancy) {className fancy;}return (divclassName{className}onPointerEnter{() setHover(true)}onPointerLeave{() setHover(false)}h1{score}/h1button onClick{() setScore(score 1)}加一/button/div); } 相同位置的不同组件会使 state 重置 在这个例子中勾选复选框会将 替换为一个 import { useState } from react;export default function App() {const [isPaused, setIsPaused] useState(false);return (div{isPaused ? (p待会见/p ) : (Counter / )}labelinputtypecheckboxchecked{isPaused}onChange{e {setIsPaused(e.target.checked)}}/休息一下/label/div); }function Counter() {const [score, setScore] useState(0);const [hover, setHover] useState(false);let className counter;if (hover) {className hover;}return (divclassName{className}onPointerEnter{() setHover(true)}onPointerLeave{() setHover(false)}h1{score}/h1button onClick{() setScore(score 1)}加一/button/div); } 并且当你在相同位置渲染不同的组件时组件的整个子树都会被重置。要验证这一点可以增加计数器的值然后勾选复选框 import { useState } from react;export default function App() {const [isFancy, setIsFancy] useState(false);return (div{isFancy ? (divCounter isFancy{true} / /div) : (sectionCounter isFancy{false} //section)}labelinputtypecheckboxchecked{isFancy}onChange{e {setIsFancy(e.target.checked)}}/使用好看的样式/label/div); }function Counter({ isFancy }) {const [score, setScore] useState(0);const [hover, setHover] useState(false);let className counter;if (hover) {className hover;}if (isFancy) {className fancy;}return (divclassName{className}onPointerEnter{() setHover(true)}onPointerLeave{() setHover(false)}h1{score}/h1button onClick{() setScore(score 1)}加一/button/div); } 在相同位置重置 state 方法一将组件渲染在不同的位置 import { useState } from react;export default function Scoreboard() {const [isPlayerA, setIsPlayerA] useState(true);return (div{isPlayerA Counter personTaylor /}{!isPlayerA Counter personSarah /}button onClick{() {setIsPlayerA(!isPlayerA);}}下一位玩家/button/div); }function Counter({ person }) {const [score, setScore] useState(0);const [hover, setHover] useState(false);let className counter;if (hover) {className hover;}return (divclassName{className}onPointerEnter{() setHover(true)}onPointerLeave{() setHover(false)}h1{person} 的分数{score}/h1button onClick{() setScore(score 1)}加一/button/div); } 方法二使用 key 来重置 state key 不只可以用于列表你可以使用 key 来让 React 区分任何组件。默认情况下React 使用父组件内部的顺序“第一个计数器”、“第二个计数器”来区分组件。但是 key 可以让你告诉 React 这不仅仅是 第一个 或者 第二个 计数器而且还是一个特定的计数器——例如Taylor 的 计数器。这样无论它出现在树的任何位置 React 都会知道它是 Taylor 的 计数器 import { useState } from react;export default function Scoreboard() {const [isPlayerA, setIsPlayerA] useState(true);return (div{isPlayerA ? (Counter keyTaylor personTaylor /) : (Counter keySarah personSarah /)}button onClick{() {setIsPlayerA(!isPlayerA);}}下一位玩家/button/div); }function Counter({ person }) {const [score, setScore] useState(0);const [hover, setHover] useState(false);let className counter;if (hover) {className hover;}return (divclassName{className}onPointerEnter{() setHover(true)}onPointerLeave{() setHover(false)}h1{person} 的分数{score}/h1button onClick{() setScore(score 1)}加一/button/div); } 为被移除的组件保留 state 在真正的聊天应用中你可能会想在用户再次选择前一个收件人时恢复输入 state。对于一个不可见的组件有几种方法可以让它的 state “活下去” 与其只渲染现在这一个聊天你可以把 所有 聊天都渲染出来但用 CSS 把其他聊天隐藏起来。这些聊天就不会从树中被移除了所以它们的内部 state 会被保留下来。这种解决方法对于简单 UI 非常有效。但如果要隐藏的树形结构很大且包含了大量的 DOM 节点那么性能就会变得很差。你可以进行 状态提升 并在父组件中保存每个收件人的草稿消息。这样即使子组件被移除了也无所谓因为保留重要信息的是父组件。这是最常见的解决方法。除了 React 的 state你也可以使用其他数据源。例如也许你希望即使用户不小心关闭页面也可以保存一份信息草稿。要实现这一点你可以让 Chat 组件通过读取 localStorage 对其 state 进行初始化并把草稿保存在那里。 练习1 import { useState } from react;export default function App() {const [showHint, setShowHint] useState(false);if (showHint) {return (divpi提示你最喜欢的城市/i/pForm /button onClick{() {setShowHint(false);}}隐藏提示/button/div);}return (divp/pForm /button onClick{() {setShowHint(true);}}显示提示/button/div); }function Form() {const [text, setText] useState();return (textareavalue{text}onChange{e setText(e.target.value)}/); } 练习2 import { useState } from react;export default function App() {const [reverse, setReverse] useState(false);let checkbox (labelinputtypecheckboxchecked{reverse}onChange{e setReverse(e.target.checked)}/调换顺序/label);if (reverse) {return (Field label姓氏 key姓氏 / Field label名字 key名字/{checkbox}/);} else {return (Field label名字 key名字/ Field label姓氏 key姓氏/{checkbox}/); } }function Field({ label }) {const [text, setText] useState();return (label{label}inputtypetextvalue{text}placeholder{label}onChange{e setText(e.target.value)}//label); } 练习3,4,5 迁移状态逻辑至 Reducer 中 使用 reducer 整合状态逻辑 随着组件复杂度的增加你将很难一眼看清所有的组件状态更新逻辑。例如下面的 TaskApp 组件有一个数组类型的状态 tasks并通过三个不同的事件处理程序来实现任务的添加、删除和修改 import { useState } from react; import AddTask from ./AddTask.js; import TaskList from ./TaskList.js;export default function TaskApp() {const [tasks, setTasks] useState(initialTasks);function handleAddTask(text) {setTasks([...tasks,{id: nextId,text: text,done: false,},]);}function handleChangeTask(task) {setTasks(tasks.map((t) {if (t.id task.id) {return task;} else {return t;}}));}function handleDeleteTask(taskId) {setTasks(tasks.filter((t) t.id ! taskId));}return (h1布拉格的行程安排/h1AddTask onAddTask{handleAddTask} /TaskListtasks{tasks}onChangeTask{handleChangeTask}onDeleteTask{handleDeleteTask}//); }let nextId 3; const initialTasks [{id: 0, text: 参观卡夫卡博物馆, done: true},{id: 1, text: 看木偶戏, done: false},{id: 2, text: 打卡列侬墙, done: false}, ]; 你可以通过三个步骤将 useState 迁移到 useReducer 第 1 步: 将设置状态的逻辑修改成 dispatch 的一个 action 移除所有的状态设置逻辑。只留下三个事件处理函数 handleAddTask(text) 在用户点击 “添加” 时被调用。handleChangeTask(task) 在用户切换任务或点击 “保存” 时被调用。handleDeleteTask(taskId) 在用户点击 “删除” 时被调用。 使用 reducers 管理状态与直接设置状态略有不同。它不是通过设置状态来告诉 React “要做什么”而是通过事件处理程序 dispatch 一个 “action” 来指明 “用户刚刚做了什么”。而状态更新逻辑则保存在其他地方因此我们不再通过事件处理器直接 “设置 task”而是 dispatch 一个 “添加/修改/删除任务” 的 action。这更加符合用户的思维。 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,}); }第 2 步: 编写一个 reducer 函数 reducer 函数就是你放置状态逻辑的地方。它接受两个参数分别为当前 state 和 action 对象并且返回的是更新后的 state在这个例子中要将状态设置逻辑从事件处理程序移到 reducer 函数中你需要 声明当前状态tasks作为第一个参数声明 action 对象作为第二个参数从 reducer 返回 下一个 状态React 会将旧的状态设置为这个最新的状态 function tasksReducer(tasks, action) {if (action.type added) {return [...tasks,{id: action.id,text: action.text,done: false,},];} else if (action.type changed) {return tasks.map((t) {if (t.id action.task.id) {return action.task;} else {return t;}});} else if (action.type deleted) {return tasks.filter((t) t.id ! action.id);} else {throw Error(未知 action: action.type);} }上面的代码使用了 if/else 语句但是在 reducers 中使用 switch 语句 是一种惯例。两种方式结果是相同的但 switch 语句读起来一目了然 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(未知 action: action.type);}} }Reduce()reduce() 是 JavaScript 中数组的高阶函数之一用于将数组中的元素归纳为单个值。它接受一个回调函数作为参数这个回调函数可以进行累积操作。 语法 array.reduce(callback(accumulator, currentValue, currentIndex, array), initialValue);callback用于处理数组中每个元素的回调函数包含四个参数 accumulator累积器累积计算的结果。currentValue当前处理的元素。currentIndex可选当前处理的元素的索引。array可选调用 reduce 的数组。 initialValue可选作为第一次调用 callback 时的第一个参数 accumulator 的初始值。如果不提供将使用数组的第一个元素作为初始值且 callback 不会在数组的第一个元素上调用。 示例 // 计算数组元素的和 const numbers [1, 2, 3, 4, 5]; const sum numbers.reduce((accumulator, currentValue) accumulator currentValue, 0); console.log(sum); // 输出 15// 找出数组中的最大值 const max numbers.reduce((accumulator, currentValue) Math.max(accumulator, currentValue)); console.log(max); // 输出 5使用场景 累加或累乘计算数组中所有元素的和或积。查找最值找出数组中的最大值或最小值。转换数据格式将数组转换成其他数据结构。归纳操作进行复杂的归纳计算。 注意事项 在没有提供 initialValue 的情况下reduce 将从数组的第二个元素开始调用回调函数。如果数组为空且没有提供 initialValuereduce 将抛出 TypeError。回调函数在数组有元素时至少会被调用一次。reduce 不会改变原数组。 示例 // 使用 reduce 计算数组中的平均值 const numbers [10, 20, 30, 40, 50]; const average numbers.reduce((accumulator, currentValue, index, array) {accumulator currentValue;if (index array.length - 1) {return accumulator / array.length;} else {return accumulator;} }, 0);console.log(average); // 输出 30在这个示例中reduce 被用于计算数组 numbers 中所有元素的平均值。 第 3 步: 在组件中使用 reducer 你需要将 tasksReducer 导入到组件中。记得先从 React 中导入 useReducer Hook import { useReducer } from react;接下来你就可以替换掉之前的 useState: const [tasks, setTasks] useState(initialTasks);只需要像下面这样使用 useReducer: const [tasks, dispatch] useReducer(tasksReducer, initialTasks);useReducer 和 useState 很相似——你必须给它传递一个初始状态它会返回一个有状态的值和一个设置该状态的函数在这个例子中就是 dispatch 函数。但是它们两个之间还是有点差异的。useReducer 钩子接受 2 个参数 一个 reducer 函数一个初始的 state 它返回如下内容 一个有状态的值一个 dispatch 函数用来 “派发” 用户操作给 reducer 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 (h1布拉格的行程安排/h1AddTask onAddTask{handleAddTask} /TaskListtasks{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(未知 action: action.type);}} }let nextId 3; const initialTasks [{id: 0, text: 参观卡夫卡博物馆, done: true},{id: 1, text: 看木偶戏, done: false},{id: 2, text: 打卡列侬墙, done: false} ]; 对比 useState 和 useReducer 代码体积 通常在使用 useState 时一开始只需要编写少量代码。而 useReducer 必须提前编写 reducer 函数和需要调度的 actions。但是当多个事件处理程序以相似的方式修改 state 时useReducer 可以减少代码量。可读性 当状态更新逻辑足够简单时useState 的可读性还行。但是一旦逻辑变得复杂起来它们会使组件变得臃肿且难以阅读。在这种情况下useReducer 允许你将状态更新逻辑与事件处理程序分离开来。可调试性 当使用 useState 出现问题时, 你很难发现具体原因以及为什么。 而使用 useReducer 时 你可以在 reducer 函数中通过打印日志的方式来观察每个状态的更新以及为什么要更新来自哪个 action。 如果所有 action 都没问题你就知道问题出在了 reducer 本身的逻辑中。 然而与使用 useState 相比你必须单步执行更多的代码。可测试性 reducer 是一个不依赖于组件的纯函数。这就意味着你可以单独对它进行测试。一般来说我们最好是在真实环境中测试组件但对于复杂的状态更新逻辑针对特定的初始状态和 action断言 reducer 返回的特定状态会很有帮助。个人偏好 并不是所有人都喜欢用 reducer没关系这是个人偏好问题。你可以随时在 useState 和 useReducer 之间切换它们能做的事情是一样的 练习1,2,3,4 暂时搁置 使用 Context 深层传递参数 通常来说你会通过 props 将信息从父组件传递到子组件。但是如果你必须通过许多中间组件向下传递 props或是在你应用中的许多组件需要相同的信息传递 props 会变的十分冗长和不便。Context 允许父组件向其下层无论多深的任何组件提供信息而无需通过 props 显式传递 传递 props 带来的问题 Context传递 props 的另一种方法 Context 让父组件可以为它下面的整个组件树提供数据你可以通过以下三个步骤来实现它 创建 一个 context。你可以将其命名为 LevelContext, 因为它表示的是标题级别。)在需要数据的组件内 使用 刚刚创建的 context。Heading 将会使用 LevelContext。在指定数据的组件中 提供 这个 context。 Section 将会提供 LevelContext。 写在你使用 context 之前 使用 Context 看起来非常诱人然而这也意味着它也太容易被过度使用了。如果你只想把一些 props 传递到多个层级中这并不意味着你需要把这些信息放到 context 里在使用 context 之前你可以考虑以下几种替代方案 从 传递 props 开始。 如果你的组件看起来不起眼那么通过十几个组件向下传递一堆 props 并不罕见。这有点像是在埋头苦干但是这样做可以让哪些组件用了哪些数据变得十分清晰维护你代码的人会很高兴你用 props 让数据流变得更加清晰。抽象组件并 将 JSX 作为children传递 给它们。 如果你通过很多层不使用该数据的中间组件并且只会向下传递来传递数据这通常意味着你在此过程中忘记了抽象组件。举个例子你可能想传递一些像 posts 的数据 props 到不会直接使用这个参数的组件类似 。取而代之的是让 Layout 把 children 当做一个参数然后渲染 。这样就减少了定义数据的组件和使用数据的组件之间的层级 Context 的使用场景 主题 如果你的应用允许用户更改其外观例如暗夜模式你可以在应用顶层放一个 context provider并在需要调整其外观的组件中使用该 context。当前账户 许多组件可能需要知道当前登录的用户信息。将它放到 context 中可以方便地在树中的任何位置读取它。某些应用还允许你同时操作多个账户例如以不同用户的身份发表评论。在这些情况下将 UI 的一部分包裹到具有不同账户数据的 provider 中会很方便。路由 大多数路由解决方案在其内部使用 context 来保存当前路由。这就是每个链接“知道”它是否处于活动状态的方式。如果你创建自己的路由库你可能也会这么做。状态管理 随着你的应用的增长最终在靠近应用顶部的位置可能会有很多 state。许多遥远的下层组件可能想要修改它们。通常 将 reducer 与 context 搭配使用来管理复杂的状态并将其传递给深层的组件来避免过多的麻烦 传递 Context 的方法: 通过 export const MyContext createContext(defaultValue) 创建并导出 context。在无论层级多深的任何子组件中把 context 传递给 useContext(MyContext) Hook 来读取它。在父组件中把 children 包在 MyContext.Provider value{…} 中来提供 context。 使用 Reducer 和 Context 拓展你的应用 下面将介绍如何结合使用 reducer 和 context 创建 context。将 state 和 dispatch 放入 context。在组件树的任何地方 使用 context 应急方案 使用 ref 引用值 当你希望组件“记住”某些信息但又不想让这些信息 触发新的渲染 时你可以使用 ref 。 给你的组件添加 ref 你可以通过从 React 导入 useRef Hook 来为你的组件添加一个 ref import { useRef } from react;在你的组件内调用 useRef Hook 并传入你想要引用的初始值作为唯一参数。例如这里的 ref 引用的值是“0” const ref useRef(0);useRef 返回一个这样的对象: { current: 0 // 你向 useRef 传入的值}import { useRef } from react;export default function Counter() {let ref useRef(0);function handleClick() {ref.current ref.current 1;alert(你点击了 ref.current 次);}return (button onClick{handleClick}点击我/button); } 组件不会在每次递增时重新渲染。 与 state 一样React 会在每次重新渲染之间保留 ref。但是设置 state 会重新渲染组件更改 ref 不会 ref 和 state 的不同之处 useRef 内部是如何运行的 尽管 useState 和 useRef 都是由 React 提供的原则上 useRef 可以在 useState的基础上 实现。 你可以想象在 React 内部useRef 是这样实现的 // React 内部function useRef(initialValue) {const [ref, unused] useState({ current: initialValue });return ref;}何时使用 ref 存储 timeout ID存储和操作 DOM 元素我们将在 下一页 中介绍存储不需要被用来计算 JSX 的其他对象 ref 的最佳实践 遵循这些原则将使你的组件更具可预测性 将 ref 视为应急方案。 当你使用外部系统或浏览器 API 时ref 很有用。如果你很大一部分应用程序逻辑和数据流都依赖于 ref你可能需要重新考虑你的方法。不要在渲染过程中读取或写入 ref.current。 如果渲染过程中需要某些信息请使用 state 代替。由于 React 不知道 ref.current 何时发生变化即使在渲染时读取它也会使组件的行为难以预测。唯一的例外是像 if (!ref.current) ref.current new Thing() 这样的代码它只在第一次渲染期间设置一次 ref。 使用 ref 操作 DOM 获取指向节点的 ref 要访问由 React 管理的 DOM 节点首先引入 useRef Hook import { useRef } from react;然后在你的组件中使用它声明一个 ref const myRef useRef(null);Finally, pass your ref as the ref attribute to the JSX tag for which you want to get the DOM node: div ref{myRef}useRef Hook 返回一个对象该对象有一个名为 current 的属性。最初myRef.current 是 null。当 React 为这个 创建一个 DOM 节点时React 会把对该节点的引用放入 myRef.current。然后你可以从 事件处理器 访问此 DOM 节点并使用在其上定义的内置 浏览器 API。 // 你可以使用任意浏览器 API例如myRef.current.scrollIntoView();在上面的例子中ref 的数量是预先确定的。但有时候你可能需要为列表中的每一项都绑定 ref 而你又不知道会有多少项。像下面这样做是行不通的 ul{items.map((item) {// 行不通const ref useRef(null);return li ref{ref} /;})}/ul这是因为 Hook 只能在组件的顶层被调用。不能在循环语句、条件语句或 map() 函数中调用 useRef 访问另一个组件的 DOM 节点 默认情况下React 不允许组件访问其他组件的 DOM 节点。甚至自己的子组件也不行想要 暴露其 DOM 节点的组件必须选择该行为。一个组件可以指定将它的 ref “转发”给一个子组件。下面是 MyInput 如何使用 forwardRef API const MyInput forwardRef((props, ref) {return input {...props} ref{ref} /;});使用 Effect 同步 有些组件需要与外部系统同步。例如你可能希望根据 React state 控制非 React 组件、设置服务器连接或在组件出现在屏幕上时发送分析日志。Effects 会在渲染后运行一些代码以便可以将组件与 React 之外的某些系统同步在本文和后续文本中Effect 在 React 中是专有定义——由渲染引起的副作用。为了指代更广泛的编程概念也可以将其称为“副作用side effect 不要随意在你的组件中使用 Effect。记住Effect 通常用于暂时“跳出” React 代码并与一些 外部 系统进行同步。这包括浏览器 API、第三方小部件以及网络等等。如果你想用 Effect 仅根据其他状态调整某些状态那么 你可能不需要 Effect。 如何编写 Effect 第一步声明 Effect 首先在 React 中引入 useEffect Hook import { useEffect } from react;然后在组件顶部调用它并传入在每次渲染时都需要执行的代码 function MyComponent() {useEffect(() {// 每次渲染后都会执行此处的代码});return div /;}一般来说Effect 会在 每次 渲染后执行而以下代码会陷入死循环中 const [count, setCount] useState(0);useEffect(() {setCount(count 1);});每次渲染结束都会执行 Effect而更新 state 会触发重新渲染。但是新一轮渲染时又会再次执行 Effect然后 Effect 再次更新 state……如此周而复始从而陷入死循环 第二步指定 Effect 依赖 一般来说Effect 会在 每次 渲染时执行。但更多时候并不需要每次渲染的时候都执行 Effect。 有时这会拖慢运行速度。因为与外部系统的同步操作总是有一定时耗在非必要时可能希望跳过它。例如没有人会希望每次用键盘打字时都重新连接聊天服务器。有时这会导致程序逻辑错误。例如组件的淡入动画只需要在第一轮渲染出现时播放一次而不是每次触发新一轮渲染后都播放。 useEffect(() {if (isPlaying) { // isPlaying 在此处使用……// ...} else {// ...}}, [isPlaying]); // ……所以它必须在此处声明指定 [isPlaying] 会告诉 React如果 isPlaying 在上一次渲染时与当前相同它应该跳过重新运行 Effect没有依赖数组作为第二个参数与依赖数组位空数组 [] 的行为是不一致的 useEffect(() {// 这里的代码会在每次渲染后执行});useEffect(() {// 这里的代码只会在组件挂载后执行}, []);useEffect(() {//这里的代码只会在每次渲染后并且 a 或 b 的值与上次渲染不一致时执行}, [a, b]);第三部按需添加清理cleanup函数 可以在 Effect 中返回一个 清理cleanup 函数。useEffect(() {const connection createConnection();connection.connect();return () {connection.disconnect();};}, []);每次重新执行 Effect 之前React 都会调用清理函数组件被卸载时也会调用清理函数 订阅事件 如果 Effect 订阅了某些事件清理函数应该退订这些事件 useEffect(() {function handleScroll(e) {console.log(window.scrollX, window.scrollY);}window.addEventListener(scroll, handleScroll);return () window.removeEventListener(scroll, handleScroll);}, []);触发动画 如果 Effect 对某些内容加入了动画清理函数应将动画重置 useEffect(() {const node ref.current;node.style.opacity 1; // 触发动画return () {node.style.opacity 0; // 重置为初始值};}, []);初始化应用时不需要使用 Effect 的情形 某些逻辑应该只在应用程序启动时运行一次。比如验证登陆状态和加载本地程序数据。你可以将其放在组件之外 if (typeof window ! undefined) { // 检查是否在浏览器中运行checkAuthToken();loadDataFromLocalStorage();}function App() {// ……}你可能不需要 Effect Effect 是 React 范式中的一个逃脱方案。它们让你可以 “逃出” React 并使组件和一些外部系统同步比如非 React 组件、网络和浏览器 DOM。如果没有涉及到外部系统例如你想根据 props 或 state 的变化来更新一个组件的 state你就不应该使用 Effect 响应式 Effect 的生命周期 Effect 的生命周期 每个 React 组件都经历相同的生命周期 当组件被添加到屏幕上时它会进行组件的 挂载。当组件接收到新的 props 或 state 时通常是作为对交互的响应它会进行组件的 更新。当组件从屏幕上移除时它会进行组件的 卸载
http://www.sadfv.cn/news/76196/

相关文章:

  • 品牌网站建设新闻青岛外贸建设网站
  • 广州站是哪个站东莞seoseo关键词排名优化
  • 网站图片引导页怎么做网络广告有哪些形式
  • 赤峰公司做网站wordpress 编辑器 修改
  • 域名个人用户可以做企业网站吗wordpress看不到图片
  • 网站开发设计实训实训总结创建手机网页
  • 为什么网站设计很少全屏自动翻译wordpress中文标签别名为英文
  • 什么网站赚的钱最多建设银行网站登不上
  • 效果图网站推荐大全无忧网站建设报价
  • 做网站建设小程序济南优化网站技术
  • 什么是网站黏着度互动网站制作
  • 深圳展厅设计装修模板网站怎么建设优化
  • 胶州网站制作电商平台网站大全
  • wordpress网站无法登陆设计网页价格
  • 天津建设协会网站程序员培训机构出来找工作好找吗
  • 河南网站建设首选公司网站开发实践体会
  • 如何把自己的网站推广出去企业网站自己可以做吗
  • 建立网站基本步骤成都网站优化排名
  • 管理网站建设公司好吗方案图网站
  • 如何检查网站死链网站百度排名提升
  • 公司企业网站源码网站技术培训学校
  • 省级示范校建设网站网站镜像上传到域名空间
  • 建设通网站怎么投诉网站开发语言市场有率
  • 导航网站的网站地图怎么做昆明微网站搭建
  • 重庆城市建设网站如何让建设一个简单的网站
  • 河北涿州住房和城乡建设厅网站网站开发兼职团队
  • wordpress 自定义插件清远市企业网站seo
  • 万户网络网站顾问电子报刊的传播媒体是什么
  • seo网站推广首页排名找人做软件网站
  • 建站赚钱灰色做php网站会员开店代码如何编写