手机网站 cms,网站规划与栏目结构诊断,wordpress js 插件,建设网站企业邮箱网站建设服务createContext
createContext要和useContext配合使用#xff0c;可以理解为 “React自带的redux或mobx” #xff0c;事实上redux就是用context来实现的。但是一番操作下来我还是感觉#xff0c;简单的context对视图的更新的细粒度把控比不上mobx#xff0c;除非配合memo等…createContext
createContext要和useContext配合使用可以理解为 “React自带的redux或mobx” 事实上redux就是用context来实现的。但是一番操作下来我还是感觉简单的context对视图的更新的细粒度把控比不上mobx除非配合memo等优化手段来优化。redux只用过一次不是很会
以下demo介绍context的简单使用
// store.ts
import { createContext } from react;
export const ThemeContext createContext({theme: light,setTheme: (_theme: string): void {},
});
export const AuthContext createContext({name: boyiao,roleType: admin,setRoleType: (_roleType: string): void {},setName: (_name: string): void {},
});// Demo.js
import { useContext, useState } from react;
import { ThemeContext, AuthContext } from ./store;const PageA () {console.log(Page Render);const { theme, setTheme } useContext(ThemeContext);const { name, setName, roleType, setRoleType } useContext(AuthContext);return (divbutton onClick{() setTheme(theme light ? dark : light)}Set Theme In Page/buttondiv{theme}/divdiv{name}/div/div);
};const OtherPage () {console.log(OtherPage Render);return (div style{{ border: 1px solid red }}divOtherPage/div/div);
};function Demo() {const [theme, setTheme] useState(light);const [name, setName] useState(boyiao);const [roleType, setRoleType] useState(admin);console.log(App Render);return (ThemeContext.Provider value{{ theme, setTheme }}AuthContext.Provider value{{ name, setName, roleType, setRoleType }}button onClick{() setTheme(theme light ? dark : light)}Change Theme/buttonbutton onClick{() setRoleType(roleType admin ? user : admin)}Change Auth/buttonPage //AuthContext.Provider/ThemeContext.ProviderOtherPage / // 注意这玩意在Context之外/);
}export default Demo;为什么说“简单的context对视图的更新的细粒度把控比不上mobx”项目中常见的情况是我们有一些需要在全局共享的状态需要管理如userName、roleType、themeToken等如果用context来管理为了保证这些变量可改变并触发视图重新渲染我们不得不在根组件里用useState来保存这些全局变量。但是如果这些全局的变量改变重新渲染影响到的范围是从最顶层往下的成本未免太高。 见上面那个demoOtherPage不会用到context中的变量但是每次全局变量更新OtherPage也要重新渲染。
但是当然有办法解决 用memo、useMemo来缓存就好了或者我们把需要共享的变量进行一些作用域的划分即不要把所有需要共享的变量都生命在Root组件里面这些都是为了降低重绘排的成本 只是需要在别的地方下点功夫。只是如果用mobx则方便很多因为mobx要求开发者用observer来显示地绑定要监听的组件
// store.ts
import { observable, configure, action } from mobx;
configure({ enforceActions: always });
interface AccountMobx {userName: string;roleType: admin | user;setUserName: (name: string) void;setRoleType: (role: admin | user ) void;
}
const accountMobx observableAccountMobx({userName: boyiao,roleType: admin,setUserName(name: string) {this.userName name;},setRoleType(role: admin | user ) {this.roleType role;},},{setUserName: action,setRoleType: action}
);
export default accountMobx;import { observer } from mobx-react-lite;
import accountMobx from ./store;
const Demo observer(() {console.log(Demo render);return (divdiv{accountMobx.userName}/divbuttononClick{() accountMobx.setUserName(accountMobx.userName 1)}set userName/button/div);
});
export default Demo;import ./App.css;
import Demo from ./ReactMobxDemo/index.tsx
function App() {console.log(App rendered);return (Demo /div classNameApp// App Content/div/);
}
export default App;这样的好处很直接Demo的更新影响不到App中的其他内容。 错误边界
只用过类组件的主要借助类组件的 componentDidCatch 他可以捕获子组件渲染过程中的错误并在渲染组件树的过程中向上冒泡直到它被捕获为止。生命周期。函数组件要实现这种功能相对复杂。
import React from react;
class ErrorBoundary extends React.Component {constructor(props) {super(props);this.state { hasError: false };}static getDerivedStateFromError(error) {// 更新状态以干掉后续的渲染return { hasError: true };}componentDidCatch(error, errorInfo) {// 可以在此处记录错误信息console.error(错误捕获, error, errorInfo);}render() {if (this.state.hasError) {// 自定义的降级 UIreturn h1出了点问题请稍后再试。/h1;}return this.props.children;}
}伟大无需多言。 createPoralReact DOM的API
最近发布了一个图钉组件到npm上面这个组件的实现就是借助了createPoral。 portal 允许组件将它们的某些子元素渲染到 DOM 中的不同位置。这使得组件的一部分可以“逃脱”它所在的容器。例如组件可以在页面其余部分上方或外部显示模态对话框和提示框。 portal 只改变 DOM 节点的所处位置。在其他方面渲染至 portal 的 JSX 的行为表现与作为 React 组件的子节点一致。该子节点可以访问由父节点树提供的 context 对象、事件将从子节点依循 React 树冒泡到父节点。 考虑下面这个需求在我的React应用中有一个比待标记的节点我希望将它标记出来即在页面的空白处打上一个「图钉」并将「图钉」与这个「待标记的节点」用线连接怎么画线先不管在我的组件里我用div来做这么简单怎么来。 然后我希望实现的代码逻辑如下
const HomePage () {return (div idcontainerdiv idtargetDOM这是你想要连接的元素/divThumbtackpopupContainerIdcontainertargetElementIdtargetDOM/Thumbtack/div);
};问题来了Thumbtack要如何封装才能让他里面的某些DOM节点跑到#container里面和#targetDOM产生交互这就是createPoral的作用。
const Thumbtack ({popupContainerId,targetElementId
}: { popupContainerId: string,targetElementId: string
}) {const [popupContainer, setPopupContainer] useState(null);useEffect(() {const popupRef document.getElementById(popupContainerId);popupRef setPopupContainer(popupRef);}, [])return (div idthumbtack{ popupContainer createPortal(div/div, popupContainer) }/div)
}最基本的思路就如上面这样简单把挂载到和#targetDOM的同一个父节点下面方便后续的绝对定位、transform等操作。但是createPoral改变的只是DOM元素渲染的位置在上面那个Thumbtack中 div /div可以使用在Thumbtack里面定义的变量而且他的事件冒泡也是冒泡到#thumbtack那里而非#container那里。 forwardRef
forwardRef最常见的还是配合useImperativeHandle一块用但单独使用也可以把自组件的某些DOM节点暴露给父组件。我都懒得写了直接看人家React官网吧。 keep-alive在react里如何实现
现有的方案react-activation推荐关注的React18 的 Offscreen自己实现保留关键数据如滚动的距离、需要记录的关键状态……