都匀网站建设公司成都商城网站开发设计
news/
2025/9/23 16:17:20/
文章来源:
都匀网站建设公司,成都商城网站开发设计,网络规划设计师 视频 网盘,云酒店网站建设目录
一、React 组件中怎么做事件代理#xff1f;它的原理是什么#xff1f;
二、React.Component 和 React.PureComponent 的区别
三、Component, Element, Instance 之间有什么区别和联系#xff1f;
四、React声明组件有哪几种方法#xff0c;有什么不同#xff1f…目录
一、React 组件中怎么做事件代理它的原理是什么
二、React.Component 和 React.PureComponent 的区别
三、Component, Element, Instance 之间有什么区别和联系
四、React声明组件有哪几种方法有什么不同
React 声明组件的三种方式
React.createClass与React.Component区别
五、React中可以在render访问refs吗为什么
六、React组件的构造函数有什么作用它是必须的吗
七、React中setState的第二个参数作用是什么
八、state 是怎么注入到组件的从 reducer 到组件经历了什么样的过程
九、React中怎么检验props验证props的目的是什么
十、React 废弃了哪些生命周期为什么
十一、React 16.X 中 props 改变后在哪个生命周期中处理
十二、React组件命名推荐的方式是哪个
十三、react 实现一个全局的 dialog
十四、在React中页面重新加载时怎样保留数据
十五、React必须使用JSX吗
十六、在React中怎么使用async/await
十七、如何配置 React-Router 实现路由切换
十八、react-router 里的 Link 标签和 a 标签的区别
十九、React-Router如何获取URL的参数和历史对象
1获取URL的参数
2获取历史对象
二十、Redux 中间件是什么接受几个参数柯里化函数两端的参数具体是什么
二十一、Redux 状态管理器和变量挂载到 window 中有什么区别
二十二、mobox 和 redux 有什么区别
1共同点
2区别
对比总结
二十三、React Hook 的使用限制有哪些
二十四、React Hooks在平时开发中需要注意的问题和原因
1不要在循环条件或嵌套函数中调用Hook必须始终在 React函数的顶层使用Hook
2使用useState时候使用pushpopsplice等直接更改数组对象的坑
3useState设置状态的时候只有第一次生效后期需要更新状态必须通过useEffect
4善用useCallback
5不要滥用useContext
二十五、React Hooks 和生命周期的关系
二十六、为什么React并不推荐优先考虑使用Context
二十七、React中refs的作用是什么有哪些应用场景
二十八、Redux中的connect有什么作用
connect负责连接React和Redux
1获取state
2包装原组件
3监听store tree变化 一、React 组件中怎么做事件代理它的原理是什么
React基于Virtual DOM实现了一个SyntheticEvent层合成事件层定义的事件处理器会接收到一个合成事件对象的实例它符合W3C标准且与原生的浏览器事件拥有同样的接口支持冒泡机制所有的事件都自动绑定在最外层上。
在React底层主要对合成事件做了两件事
事件委派React会把所有的事件绑定到结构的最外层使用统一的事件监听器这个事件监听器上维持了一个映射来保存所有组件内部事件监听和处理函数。自动绑定React组件中每个方法的上下文都会指向该组件的实例即自动绑定this为当前组件。 二、React.Component 和 React.PureComponent 的区别
PureComponent表示一个纯组件可以用来优化React程序减少render函数执行的次数从而提高组件的性能。
在React中当prop或者state发生变化时可以通过在shouldComponentUpdate生命周期函数中执行return false来阻止页面的更新从而减少不必要的render执行。React.PureComponent会自动执行 shouldComponentUpdate。
不过pureComponent中的 shouldComponentUpdate() 进行的是浅比较也就是说如果是引用数据类型的数据只会比较不是同一个地址而不会比较这个地址里面的数据是否一致。浅比较会忽略属性和或状态突变情况其实也就是数据引用指针没有变化而数据发生改变的时候render是不会执行的。如果需要重新渲染那么就需要重新开辟空间引用数据。PureComponent一般会用在一些纯展示组件上。
使用pureComponent的好处当组件更新时如果组件的props或者state都没有改变render函数就不会触发。省去虚拟DOM的生成和对比过程达到提升性能的目的。这是因为react自动做了一层浅比较。 三、Component, Element, Instance 之间有什么区别和联系
元素一个元素element是一个普通对象(plain object)描述了对于一个DOM节点或者其他组件component你想让它在屏幕上呈现成什么样子。元素element可以在它的属性props中包含其他元素(译注:用于形成元素树)。创建一个React元素element成本很低。元素element创建之后是不可变的。组件一个组件component可以通过多种方式声明。可以是带有一个render()方法的类简单点也可以定义为一个函数。这两种情况下它都把属性props作为输入把返回的一棵元素树作为输出。实例一个实例instance是你在所写的组件类component class中使用关键字this所指向的东西(译注:组件实例)。它用来存储本地状态和响应生命周期事件很有用。
函数式组件(Functional component)根本没有实例instance。类组件(Class component)有实例instance但是永远也不需要直接创建一个组件的实例因为React帮我们做了这些。 四、React声明组件有哪几种方法有什么不同
React 声明组件的三种方式
函数式定义的无状态组件ES5原生方式React.createClass定义的组件ES6形式的extends React.Component定义的组件
1无状态函数式组件
它是为了创建纯展示组件这种组件只负责根据传入的props来展示不涉及到state状态的操作
组件不会被实例化整体渲染性能得到提升不能访问this对象不能访问生命周期的方法
2ES5 原生方式 React.createClass // RFC
React.createClass会自绑定函数方法导致不必要的性能开销增加代码过时的可能性。
3E6继承形式 React.Component // RCC
目前极为推荐的创建有状态组件的方式最终会取代React.createClass形式相对于 React.createClass可以更好实现代码复用。
无状态组件相对于于后者的区别
与无状态组件相比React.createClass和React.Component都是创建有状态的组件这些组件是要被实例化的并且可以访问组件的生命周期方法。
React.createClass与React.Component区别
① 函数this自绑定
React.createClass创建的组件其每一个成员函数的this都有React自动绑定函数中的this会被正确设置。React.Component创建的组件其成员函数不会自动绑定this需要开发者手动绑定否则this不能获取当前组件实例对象。
② 组件属性类型propTypes及其默认props属性defaultProps配置不同
React.createClass在创建组件时有关组件props的属性类型及组件默认的属性会作为组件实例的属性来配置其中defaultProps是使用getDefaultProps的方法来获取默认组件属性的。React.Component在创建组件时配置这两个对应信息时他们是作为组件类的属性不是组件实例的属性也就是所谓的类的静态属性来配置的。
③ 组件初始状态state的配置不同
React.createClass创建的组件其状态state是通过getInitialState方法来配置组件相关的状态。React.Component创建的组件其状态state是在constructor中像初始化组件属性一样声明的。 五、React中可以在render访问refs吗为什么
span idname ref{this.spanRef}{this.state.title}/spanspan{ this.spanRef.current ? 有值 : 无值 }/span
/
不可以render 阶段 DOM 还没有生成无法获取 DOM。DOM 的获取需要在 pre-commit 阶段和 commit 阶段 六、React组件的构造函数有什么作用它是必须的吗
构造函数主要用于两个目的
通过将对象分配给this.state来初始化本地状态将事件处理程序方法绑定到实例上
所以当在React class中需要设置state的初始值或者绑定事件时需要加上构造函数官方Demo
class LikeButton extends React.Component {constructor() {super();this.state {liked: false};this.handleClick this.handleClick.bind(this);}handleClick() {this.setState({liked: !this.state.liked});}render() {const text this.state.liked ? liked : haven\t liked;return (div onClick{this.handleClick}You {text} this. Click to toggle./div);}
}
ReactDOM.render(LikeButton /,document.getElementById(example)
);
构造函数用来新建父类的this对象子类必须在constructor方法中调用super方法否则新建实例时会报错因为子类没有自己的this对象而是继承父类的this对象然后对其进行加工。如果不调用super方法子类就得不到this对象。
注意
constructor () 必须配上 super(), 如果要在constructor 内部使用 this.props 就要 传入props , 否则不用JavaScript中的 bind 每次都会返回一个新的函数, 为了性能等考虑, 尽量在constructor中绑定事件 七、React中setState的第二个参数作用是什么
setState 的第二个参数是一个可选的回调函数。这个回调函数将在组件重新渲染后执行。等价于在 componentDidUpdate 生命周期内执行。通常建议使用 componentDidUpdate 来代替此方式。在这个回调函数中你可以拿到更新后 state 的值
this.setState({key1: newState1,key2: newState2,...
}, callback) // 第二个参数是 state 更新完成后的回调函数 八、state 是怎么注入到组件的从 reducer 到组件经历了什么样的过程
通过connect和mapStateToProps将state注入到组件中
import { connect } from react-redux
import { setVisibilityFilter } from /reducers/Todo/actions
import Link from /containers/Todo/components/Linkconst mapStateToProps (state, ownProps) ({active: ownProps.filter state.visibilityFilter
})const mapDispatchToProps (dispatch, ownProps) ({setFilter: () {dispatch(setVisibilityFilter(ownProps.filter))}
})export default connect(mapStateToProps,mapDispatchToProps
)(Link)
上面代码中active就是注入到Link组件中的状态。 mapStateToPropsstateownProps中带有两个参数含义是∶
state-store管理的全局状态对象所有都组件状态数据都存储在该对象中。ownProps 组件通过props传入的参数。
reducer 到组件经历的过程
reducer对action对象处理更新组件状态并将新的状态值返回store。通过connectmapStateToPropsmapDispatchToPropsComponent对组件 Component进行升级此时将状态值从store取出并作为props参数传递到组件。
高阶组件实现源码∶
import React from react
import PropTypes from prop-types// 高阶组件 contect
export const connect (mapStateToProps, mapDispatchToProps) (WrappedComponent) {class Connect extends React.Component {// 通过对context调用获取storestatic contextTypes {store: PropTypes.object}constructor() {super()this.state {allProps: {}}}// 第一遍需初始化所有组件初始状态componentWillMount() {const store this.context.storethis._updateProps()store.subscribe(() this._updateProps()); // 加入_updateProps()至store里的监听事件列表}// 执行action后更新props使组件可以更新至最新状态类似于setState_updateProps() {const store this.context.store;let stateProps mapStateToProps ?mapStateToProps(store.getState(), this.props) : {} // 防止 mapStateToProps 没有传入let dispatchProps mapDispatchToProps ?mapDispatchToProps(store.dispatch, this.props) : {dispatch: store.dispatch} // 防止 mapDispatchToProps 没有传入this.setState({allProps: {...stateProps,...dispatchProps,...this.props}})}render() {return WrappedComponent {...this.state.allProps} /}}return Connect
} 九、React中怎么检验props验证props的目的是什么
React为我们提供了PropTypes以供验证使用。当我们向Props传入的数据无效向Props传入的数据类型和验证的数据类型不符就会在控制台发出警告信息。它可以避免随着应用越来越复杂从而出现的问题。并且它还可以让程序变得更易读。
import PropTypes from prop-types;class Greeting extends React.Component {render() {return (h1Hello, {this.props.name}/h1);}
}Greeting.propTypes {name: PropTypes.string
};
当然如果项目汇中使用了TypeScript那么就可以不用PropTypes来校验而使用TypeScript定义接口来校验props。 十、React 废弃了哪些生命周期为什么
被废弃的三个函数都是在render之前因为fber的出现很可能因为高优先级任务的出现而打断现有任务导致它们会被执行多次。另外的一个原因则是React想约束使用者好的框架能够让人不得已写出容易维护和扩展的代码这一点又是从何谈起可以从新增加以及即将废弃的生命周期分析入手
componentWillMount
首先这个函数的功能完全可以使用componentDidMount和 constructor来代替异步获取的数据的情况上面已经说明了而如果抛去异步获取数据其余的即是初始化而已这些功能都可以在constructor中执行除此之外如果在 willMount 中订阅事件但在服务端这并不会执行 willUnMount事件也就是说服务端会导致内存泄漏所以componentWilIMount完全可以不使用但使用者有时候难免因为各 种各样的情况在 componentWilMount中做一些操作那么React为了约束开发者干脆就抛掉了这个API
componentWillReceiveProps
在老版本的 React 中如果组件自身的某个 state 跟其 props 密切相关的话一直都没有一种很优雅的处理方式去更新 state而是需要在 componentWilReceiveProps 中判断前后两个 props 是否相同如果不同再将新的 props更新到相应的 state 上去。这样做一来会破坏 state 数据的单一数据源导致组件状态变得不可预测另一方面也会增加组件的重绘次数。类似的业务需求也有很多如一个可以横向滑动的列表当前高亮的 Tab 显然隶属于列表自身的时根据传入的某个值直接定位到某个 Tab。为了解决这些问题React引入了第一个新的生命周期getDerivedStateFromProps。它有以下的优点∶
getDSFP是静态方法在这里不能使用this也就是一个纯函数开发者不能写出副作用的代码开发者只能通过prevState而不是prevProps来做对比保证了state和props之间的简单关系以及不需要处理第一次渲染时prevProps为空的情况基于第一点将状态变化setState和昂贵操作tabChange区分开更加便于 render 和 commit 阶段操作或者说优化
componentWillUpdate
与 componentWillReceiveProps 类似许多开发者也会在 componentWillUpdate 中根据 props 的变化去触发一些回调 。 但不论是 componentWilReceiveProps 还 是 componentWilUpdate都有可能在一次更新中被调用多次也就是说写在这里的回调函数也有可能会被调用多次这显然是不可取的。与 componentDidMount 类 似 componentDidUpdate 也不存在这样的问题一次更新中 componentDidUpdate 只会被调用一次所以将原先写在 componentWillUpdate 中 的 回 调 迁 移 至 componentDidUpdate 就可以解决这个问题。
另外一种情况则是需要获取DOM元素状态但是由于在fber中render可打断可能在wilMount中获取到的元素状态很可能与实际需要的不同这个通常可以使用第二个新增的生命函数的解决 getSnapshotBeforeUpdate(prevProps, prevState)
getSnapshotBeforeUpdate(prevProps, prevState)
返回的值作为componentDidUpdate的第三个参数。与willMount不同的是getSnapshotBeforeUpdate会在最终确定的render执行之前执行也就是能保证其获取到的元素状态与didUpdate中获取到的元素状态相同。官方参考代码
class ScrollingList extends React.Component {constructor(props) {super(props);this.listRef React.createRef();}getSnapshotBeforeUpdate(prevProps, prevState) {// 我们是否在 list 中添加新的 items // 捕获滚动位置以便我们稍后调整滚动位置。if (prevProps.list.length this.props.list.length) {const list this.listRef.current;return list.scrollHeight - list.scrollTop;}return null;}componentDidUpdate(prevProps, prevState, snapshot) {// 如果我们 snapshot 有值说明我们刚刚添加了新的 items// 调整滚动位置使得这些新 items 不会将旧的 items 推出视图。//这里的 snapshot 是 getSnapshotBeforeUpdate 的返回值if (snapshot ! null) {const list this.listRef.current;list.scrollTop list.scrollHeight - snapshot;}}render() {return (div ref{this.listRef}{/* ...contents... */}/div);}
} 十一、React 16.X 中 props 改变后在哪个生命周期中处理
在getDerivedStateFromProps中进行处理。
这个生命周期函数是为了替代componentWillReceiveProps存在的所以在需要使用componentWillReceiveProps 时就可以考虑使用 getDerivedStateFromProps 来进行替代。
两者的参数是不相同的而 getDerivedStateFromProps 是一个静态函数也就是这个函数不能通过this访问到class的属性也并不推荐直接访问属性。而是应该通过参数提供的nextProps以及prevState来进行判断根据新传入的props来映射到state。
需要注意的是如果props传入的内容不需要影响到你的state那么就需要返回一个null这个返回值是必须的所以尽量将其写到函数的末尾
static getDerivedStateFromProps(nextProps, prevState) {const {type} nextProps;// 当传入的type发生变化的时候更新stateif (type ! prevState.type) {return {type,};}// 否则对于state不进行任何操作return null;
} 十二、React组件命名推荐的方式是哪个
通过引用而不是使用来命名组件displayName。
使用displayName命名组件
export default React.createClass({displayName: TodoApp,// ...
})
React推荐的方法
export default class TodoApp extends React.Component {// ...
} 十三、react 实现一个全局的 dialog
import React, { Component } from react;
import { is, fromJS } from immutable;
import ReactDOM from react-dom;
import ReactCSSTransitionGroup from react-addons-css-transition-group;
import ./dialog.css;let defaultState {alertStatus:false,alertTip:提示,closeDialog:function(){},childs:
}class Dialog extends Component{state {...defaultState};// css动画组件设置为目标组件FirstChild props {const childrenArray React.Children.toArray(props.children);return childrenArray[0] || null;}//打开弹窗open (options){options options || {};options.alertStatus true;var props options.props || {};var childs this.renderChildren(props,options.childrens) || ;console.log(childs);this.setState({...defaultState,...options,childs})}//关闭弹窗close(){this.state.closeDialog();this.setState({...defaultState})}renderChildren(props,childrens) {//遍历所有子组件var childs [];childrens childrens || [];var ps {...props, //给子组件绑定props_close:this.close //给子组件也绑定一个关闭弹窗的事件 };childrens.forEach((currentItem,index) {childs.push(React.createElement(currentItem,{...ps,key:index}));})return childs;}shouldComponentUpdate(nextProps, nextState){return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))}render(){return (ReactCSSTransitionGroupcomponent{this.FirstChild}transitionNamehidetransitionEnterTimeout{300}transitionLeaveTimeout{300}div classNamedialog-con style{this.state.alertStatus? {display:block}:{display:none}}{this.state.childs}/div/ReactCSSTransitionGroup);}
}
let div document.createElement(div);
let props {
};
document.body.appendChild(div);
let Box ReactD
子类
//子类jsx
import React, { Component } from react;
class Child extends Component {constructor(props){super(props);this.state {date: new Date()};}showValue(){this.props.showValue this.props.showValue()}render() {return (div classNameChilddiv classNamecontentChildbutton onClick{this.showValue}调用父的方法/button/div/div);}
}
export default Child;
css
.dialog-con{position: fixed;top: 0;left: 0;width: 100%;height: 100%;background: rgba(0, 0, 0, 0.3);
} 十四、在React中页面重新加载时怎样保留数据
这个问题就设计到了数据持久化主要的实现方式有以下几种
Redux将页面的数据存储在redux中在重新加载页面时获取Redux中的数据data.js使用webpack构建的项目可以建一个文件data.js将数据保存data.js中跳转页面后获取sessionStorge在进入选择地址页面之前componentWillUnMount的时候将数据存储到sessionStorage中每次进入页面判断sessionStorage中有没有存储的那个值有则读取渲染数据没有则说明数据是初始化的状态。返回或进入除了选择地址以外的页面清掉存储的sessionStorage保证下次进入是初始化的数据history APIHistory API 的 pushState 函数可以给历史记录关联一个任意的可序列化 state所以可以在路由 push 的时候将当前页面的一些信息存到 state 中下次返回到这个页面的时候就能从 state 里面取出离开前的数据重新渲染。react-router 直接可以支持。这个方法适合一些需要临时存储的场景。 十五、React必须使用JSX吗
React 并不强制要求使用 JSX。当不想在构建环境中配置有关 JSX 编译时不在 React 中使用 JSX 会更加方便。
每个 JSX 元素只是调用 React.createElement(component, props, ...children) 的语法糖。因此使用 JSX 可以完成的任何事情都可以通过纯 JavaScript 完成。
例如用 JSX 编写的代码
class Hello extends React.Component {render() {return divHello {this.props.toWhat}/div;}
}ReactDOM.render(Hello toWhatWorld /,document.getElementById(root)
);
可以编写为不使用 JSX 的代码
class Hello extends React.Component {render() {return React.createElement(div, null, Hello ${this.props.toWhat});}
}ReactDOM.render(React.createElement(Hello, {toWhat: World}, null),document.getElementById(root)
); 十六、在React中怎么使用async/await
async/await是ES7标准中的新特性。如果是使用React官方的脚手架创建的项目就可以直接使用。如果是在自己搭建的webpack配置的项目中使用可能会遇到 regeneratorRuntime is not defined 的异常错误。那么我们就需要引入babel并在babel中配置使用async/await。可以利用babel的 transform-async-to-module-method 插件来转换其成为浏览器支持的语法虽然没有性能的提升但对于代码编写体验要更好。 十七、如何配置 React-Router 实现路由切换
1使用 Route 组件
路由匹配是通过比较 Route 的 path 属性和当前地址的 pathname 来实现的。当一个 Route 匹配成功时它将渲染其内容当它不匹配时就会渲染 null。没有路径的 Route 将始终被匹配。
// when location { pathname: /about }
Route path/about component{About}/ // renders About/
Route path/contact component{Contact}/ // renders null
Route component{Always}/ // renders Always/
2结合使用 Switch 组件和 Route 组件
Switch 用于将 Route 分组。
SwitchRoute exact path/ component{Home} /Route path/about component{About} /Route path/contact component{Contact} /
/Switch
Switch 不是分组 Route 所必须的但他通常很有用。 一个 Switch 会遍历其所有的子 Route元素并仅渲染与当前地址匹配的第一个元素。
3使用 Link、 NavLink、Redirect 组件
Link 组件来在你的应用程序中创建链接。无论你在何处渲染一个 Link 都会在应用程序的 HTML 中渲染锚a。
NavLink 是一种特殊类型的 Link 当它的 to属性与当前地址匹配时可以将其定义为活跃的。
// location { pathname: /react }
NavLink to/react activeClassNamehurrayReact
/NavLink
// a href/react classNamehurrayReact/a
当我们想强制导航时可以渲染一个Redirect当一个Redirect渲染时它将使用它的to属性进行定向。 十八、react-router 里的 Link 标签和 a 标签的区别
从最终渲染的 DOM 来看这两者都是链接都是标签区别是∶
Link是react-router 里实现路由跳转的链接一般配合Route 使用react-router接管了其默认的链接跳转行为区别于传统的页面跳转Link 的“跳转”行为只会触发相匹配的Route对应的页面内容更新而不会刷新整个页面。
Link做了3件事情:
有onclick那就执行onclickclick的时候阻止a标签默认事件根据跳转href(即是to)用history (web前端路由两种方式之一history hash)跳转此时只是链接变了并没有刷新页面而a标签就是普通的超链接了用于从当前页面跳转到href指向的另一 个页面(非锚点情况)。
a标签默认事件禁掉之后做了什么才实现了跳转?
let domArr document.getElementsByTagName(a)
[...domArr].forEach(item{item.addEventListener(click,function () {location.href this.href})
}) 十九、React-Router如何获取URL的参数和历史对象
1获取URL的参数
get传值
路由配置还是普通的配置如admin传参方式如admin?id1111。通过this.props.location.search获取url获取到一个字符串?id1111
可以用urlqsquerystring浏览器提供的api URLSearchParams对象或者自己封装的方法去解析出id的值。
动态路由传值
路由需要配置成动态路由如path/admin/:id传参方式如admin/111。通过this.props.match.params.id 取得url中的动态路由id部分的值除此之外还可以通过useParamsHooks来获取
通过query或state传值
传参方式如在Link组件的to属性中可以传递对象 {pathname:/admin,query:111,state:111};。通过this.props.location.state或this.props.location.query来获取即可传递的参数可以是对象、数组等但是存在缺点就是只要刷新页面参数就会丢失。
2获取历史对象
如果React 16.8 时可以使用 React Router中提供的Hooks
import { useHistory } from react-router-dom;
let history useHistory();
使用this.props.history获取历史对象
let history this.props.history; 二十、Redux 中间件是什么接受几个参数柯里化函数两端的参数具体是什么
Redux 的中间件提供的是位于 action 被发起之后到达 reducer 之前的扩展点换而言之原本 view -→ action - reducer - store 的数据流加上中间件后变成了 view - action - middleware - reducer - store 在这一环节可以做一些副作用的操作如异步请求、打印日志等。
applyMiddleware源码
export default function applyMiddleware(...middlewares) {return createStore (...args) {// 利用传入的createStore和reducer和创建一个storeconst store createStore(...args)let dispatch () {throw new Error()}const middlewareAPI {getState: store.getState,dispatch: (...args) dispatch(...args)}// 让每个 middleware 带着 middlewareAPI 这个参数分别执行一遍const chain middlewares.map(middleware middleware(middlewareAPI))// 接着 compose 将 chain 中的所有匿名函数组装成一个新的函数即新的 dispatchdispatch compose(...chain)(store.dispatch)return {...store,dispatch}}
}
从applyMiddleware中可以看出
redux中间件接受一个对象作为参数对象的参数上有两个字段 dispatch 和 getState分别代表着 Redux Store 上的两个同名函数。柯里化函数两端一个是 middewares一个是store.dispatch。 二十一、Redux 状态管理器和变量挂载到 window 中有什么区别
两者都是存储数据以供后期使用。但是Redux状态更改可回溯——Time travel数据多了的时候可以很清晰的知道改动在哪里发生完整的提供了一套状态管理模式。
随着 JavaScript 单页应用开发日趋复杂JavaScript 需要管理比任何时候都要多的 state 状态。 这些 state 可能包括服务器响应、缓存数据、本地生成尚未持久化到服务器的数据也包括 UI状态如激活的路由被选中的标签是否显示加载动效或者分页器等等。
管理不断变化的 state 非常困难。如果一个 model 的变化会引起另一个 model 变化那么当 view 变化时就可能引起对应 model 以及另一个model 的变化依次地可能会引起另一个 view 的变化。直至你搞不清楚到底发生了什么。state 在什么时候由于什么原因如何变化已然不受控制。 当系统变得错综复杂的时候想重现问题或者添加新功能就会变得举步维艰。
如果这还不够糟糕考虑一些来自前端开发领域的新需求如更新调优、服务端渲染、路由跳转前请求数据等等。前端开发者正在经受前所未有的复杂性难道就这么放弃了吗?当然不是。
这里的复杂性很大程度上来自于我们总是将两个难以理清的概念混淆在一起变化和异步。 可以称它们为曼妥思和可乐。如果把二者分开能做的很好但混到一起就变得一团糟。一些库如 React 视图在视图层禁止异步和直接操作 DOM来解决这个问题。美中不足的是React 依旧把处理 state 中数据的问题留给了你。Redux就是为了帮你解决这个问题。 二十二、mobox 和 redux 有什么区别
1共同点
为了解决状态管理混乱无法有效同步的问题统一维护管理应用状态;某一状态只有一个可信数据来源通常命名为store指状态容器;操作更新状态方式统一并且可控通常以action方式提供更新状态的途径;支持将store与React组件连接如react-reduxmobx- react;
2区别
Redux更多的是遵循Flux模式的一种实现是一个 JavaScript库它关注点主要是以下几方面
Action∶ 一个JavaScript对象描述动作相关信息主要包含type属性和payload属性∶
type∶ action 类型;payload∶ 负载数据;
Reducer∶ 定义应用状态如何响应不同动作action如何更新状态;Store∶ 管理action和reducer及其关系的对象主要提供以下功能∶
维护应用状态并支持访问状态(getState());支持监听action的分发更新状态(dispatch(action));支持订阅store的变更(subscribe(listener));
异步流∶ 由于Redux所有对store状态的变更都应该通过action触发异步任务通常都是业务或获取数据任务也不例外而为了不将业务或数据相关的任务混入React组件中就需要使用其他框架配合管理异步任务流程如redux-thunkredux-saga等;
Mobx是一个透明函数响应式编程的状态管理库它使得状态管理简单可伸缩
Action∶定义改变状态的动作函数包括如何变更状态;Store∶ 集中管理模块状态State和动作(action)Derivation衍生∶ 从应用状态中派生而出且没有任何其他影响的数据
对比总结
redux将数据保存在单一的store中mobx将数据保存在分散的多个store中redux使用plain object保存数据需要手动处理变化后的操作;mobx适用observable保存数据数据变化后自动处理响应的操作redux使用不可变状态这意味着状态是只读的不能直接去修改它而是应该返回一个新的状态同时使用纯函数;mobx中的状态是可变的可以直接对其进行修改mobx相对来说比较简单在其中有很多的抽象mobx更多的使用面向对象的编程思维;redux会比较复杂因为其中的函数式编程思想掌握起来不是那么容易同时需要借助一系列的中间件来处理异步和副作用mobx中有更多的抽象和封装调试会比较困难同时结果也难以预测;而redux提供能够进行时间回溯的开发工具同时其纯函数以及更少的抽象让调试变得更加的容易 二十三、React Hook 的使用限制有哪些
React Hooks 的限制主要有两条
不要在循环、条件或嵌套函数中调用 Hook。在 React 的函数组件中调用 Hook。
那为什么会有这样的限制呢Hooks 的设计初衷是为了改进 React 组件的开发模式。在旧有的开发模式下遇到了三个问题。
组件之间难以复用状态逻辑。过去常见的解决方案是高阶组件、render props 及状态管理框架。复杂的组件变得难以理解。生命周期函数与业务逻辑耦合太深导致关联部分难以拆分。人和机器都很容易混淆类。常见的有 this 的问题但在 React 团队中还有类难以优化的问题希望在编译优化层面做出一些改进。
这三个问题在一定程度上阻碍了 React 的后续发展所以为了解决这三个问题Hooks 基于函数组件 开始设计。然而第三个问题决定了 Hooks 只支持函数组件。
那为什么不要在循环、条件或嵌套函数中调用 Hook 呢因为 Hooks 的设计是基于数组实现。在调用时按顺序加入数组中如果使用循环、条件或嵌套函数很有可能导致数组取值错位执行错误的 Hook。当然实质上 React 的源码里不是数组是链表。
这些限制会在编码上造成一定程度的心智负担新手可能会写错为了避免这样的情况可以引入 ESLint 的 Hooks 检查插件进行预防。 二十四、React Hooks在平时开发中需要注意的问题和原因
1不要在循环条件或嵌套函数中调用Hook必须始终在 React函数的顶层使用Hook
这是因为React需要利用调用顺序来正确更新相应的状态以及调用相应的钩子函数。一旦在循环或条件分支语句中调用Hook就容易导致调用顺序的不一致性从而产生难以预料到的后果。
2使用useState时候使用pushpopsplice等直接更改数组对象的坑
使用push直接更改数组无法获取到新值应该采用析构方式但是在class里面不会有这个问题。代码示例
function Indicatorfilter() {let [num,setNums] useState([0,1,2,3])const test () {// 这里坑是直接采用push去更新num// setNums(num)是无法更新num的// 必须使用num [...num ,1]num.push(1)// num [...num ,1]setNums(num)}
return (div classNamefilterdiv onClick{test}测试/divdiv{num.map((item,index) (div key{index}{item}/div))}/div/div)
}class Indicatorfilter extends React.Componentany,any{constructor(props:any){super(props)this.state {nums:[1,2,3]}this.test this.test.bind(this)}test(){// class采用同样的方式是没有问题的this.state.nums.push(1)this.setState({nums: this.state.nums})}render(){let {nums} this.statereturn(divdiv onClick{this.test}测试/divdiv{nums.map((item:any,index:number) (div key{index}{item}/div))}/div/div)}
}
3useState设置状态的时候只有第一次生效后期需要更新状态必须通过useEffect
TableDeail是一个公共组件在调用它的父组件里面我们通过set改变columns的值以为传递给TableDeail 的 columns是最新的值所以tabColumn每次也是最新的值但是实际tabColumn是最开始的值不会随着columns的更新而更新
const TableDeail ({columns,
}:TableData) {const [tabColumn, setTabColumn] useState(columns)
}// 正确的做法是通过useEffect改变这个值
const TableDeail ({columns,
}:TableData) {const [tabColumn, setTabColumn] useState(columns)useEffect(() {setTabColumn(columns)},[columns])
}
4善用useCallback
父组件传递给子组件事件句柄时如果我们没有任何参数变动可能会选用useMemo。但是每一次父组件渲染子组件即使没变化也会跟着渲染一次。
5不要滥用useContext
可以使用基于 useContext 封装的状态管理工具。 二十五、React Hooks 和生命周期的关系
函数组件的本质是函数没有 state 的概念的因此不存在生命周期一说仅仅是一个render 函数而已。但是引入 Hooks 之后就变得不同了它能让组件在不使用 class 的情况下拥有 state所以就有了生命周期的概念所谓的生命周期其实就是 useState、 useEffect() 和 useLayoutEffect() 。
即Hooks 组件使用了Hooks的函数组件有生命周期而函数组件未使用Hooks的函数组件是没有生命周期的。
下面是具体的 class 与 Hooks 的生命周期对应关系
constructor函数组件不需要构造函数可以通过调用 useState来初始化 state。如果计算的代价比较昂贵也可以传一个函数给 useState。
const [num, UpdateNum] useState(0)
getDerivedStateFromProps一般情况下我们不需要使用它可以在渲染过程中更新 state以达到实现 getDerivedStateFromProps 的目的。
function ScrollView({row}) {let [isScrollingDown, setIsScrollingDown] useState(false);let [prevRow, setPrevRow] useState(null);if (row ! prevRow) {// Row 自上次渲染以来发生过改变。更新 isScrollingDown。setIsScrollingDown(prevRow ! null row prevRow);setPrevRow(row);}return Scrolling down: ${isScrollingDown};
}
React 会立即退出第一次渲染并用更新后的 state 重新运行组件以避免耗费太多性能。
shouldComponentUpdate可以用 React.memo 包裹一个组件来对它的 props 进行浅比较
const Button React.memo((props) {// 具体的组件
});
注意React.memo等效于PureComponent它只浅比较 props。这里也可以使用 useMemo 优化每一个节点。
render这是函数组件体本身。componentDidMount, componentDidUpdate useLayoutEffect 与它们两的调用阶段是一样的。但是我们推荐你一开始先用 useEffect只有当它出问题的时候再尝试使用 useLayoutEffect。useEffect 可以表达所有这些的组合。
// componentDidMount
useEffect((){// 需要在 componentDidMount 执行的内容
}, [])
useEffect(() {// 在 componentDidMount以及 count 更改时 componentDidUpdate 执行的内容document.title You clicked ${count} times;return () {// 需要在 count 更改时 componentDidUpdate先于 document.title ... 执行遵守先清理后更新// 以及 componentWillUnmount 执行的内容 } // 当函数中 Cleanup 函数会按照在代码中定义的顺序先后执行与函数本身的特性无关
}, [count]); // 仅在 count 更改时更新
请记得 React 会等待浏览器完成画面渲染之后才会延迟调用 因此会使得额外操作很方便
componentWillUnmount相当于 useEffect 里面返回的 cleanup 函数
// componentDidMount/componentWillUnmount
useEffect((){// 需要在 componentDidMount 执行的内容return function cleanup() {// 需要在 componentWillUnmount 执行的内容 }
}, [])
componentDidCatch and getDerivedStateFromError目前还没有这些方法的 Hook 等价写法。
class 组件Hooks 组件constructoruseStategetDerivedStateFromPropsuseState 里面 update 函数shouldComponentUpdateuseMemorender函数本身 componentDidMountuseEffectcomponentDidUpdateuseEffectcomponentWillUnmountuseEffect 里面返回的函数componentDidCatch无getDerivedStateFromError无 二十六、为什么React并不推荐优先考虑使用Context
Context目前还处于实验阶段可能会在后面的发行版本中有很大的变化事实上这种情况已经发生了所以为了避免给今后升级带来大的影响和麻烦不建议在app中使用context。尽管不建议在app中使用context但是独有组件而言由于影响范围小于app如果可以做到高内聚不破坏组件树之间的依赖关系可以考虑使用context对于组件之间的数据通信或者状态管理有效使用props或者state解决然后再考虑使用第三方的成熟库进行解决以上的方法都不是最佳的方案的时候在考虑context。context的更新需要通过setState()触发但是这并不是很可靠的Context支持跨组件的访问但是如果中间的子组件通过一些方法不影响更新比如 shouldComponentUpdate() 返回false 那么不能保证Context的更新一定可以使用Context的子组件因此Context的可靠性需要关注 二十七、React中refs的作用是什么有哪些应用场景
Refs 提供了一种方式用于访问在 render 方法中创建的 React 元素或 DOM 节点。Refs 应该谨慎使用如下场景使用 Refs 比较适合
处理焦点、文本选择或者媒体的控制触发必要的动画集成第三方 DOM 库
Refs 是使用 React.createRef() 方法创建的他通过 ref 属性附加到 React 元素上。要在整个组件中使用 Refs需要将 ref 在构造函数中分配给其实例属性
class MyComponent extends React.Component {constructor(props) {super(props)this.myRef React.createRef()}render() {return div ref{this.myRef} /}
}
由于函数组件没有实例因此不能在函数组件上直接使用 ref
function MyFunctionalComponent() {return input /;
}
class Parent extends React.Component {constructor(props) {super(props);this.textInput React.createRef();}render() {// 这将不会工作return (MyFunctionalComponent ref{this.textInput} /);}
}
但可以通过闭合的帮助在函数组件内部进行使用 Refs
function CustomTextInput(props) {// 这里必须声明 textInput这样 ref 回调才可以引用它let textInput null;function handleClick() {textInput.focus();}return (divinputtypetextref{(input) { textInput input; }} /inputtypebuttonvalueFocus the text inputonClick{handleClick}//div);
}
注意
不应该过度的使用 Refs。ref 的返回值取决于节点的类型。
当 ref 属性被用于一个普通的 HTML 元素时React.createRef() 将接收底层 DOM 元素作为他的 current 属性以创建 ref。当 ref 属性被用于一个自定义的类组件时ref 对象将接收该组件已挂载的实例作为他的 current。当在父组件中需要访问子组件中的 ref 时可使用传递 Refs 或回调 Refs。 二十八、Redux中的connect有什么作用
connect负责连接React和Redux
1获取state
connect 通过 context获取 Provider 中的 store通过 store.getState() 获取整个store tree 上所有state
2包装原组件
将state和action通过props的方式传入到原组件内部 wrapWithConnect 返回—个 ReactComponent 对 象 ConnectConnect 重 新 render 外部传入的原组件 WrappedComponent 并把 connect 中传入的 mapStateToPropsmapDispatchToProps与组件上原有的 props合并后通过属性的方式传给WrappedComponent
3监听store tree变化
connect缓存了store tree中state的状态通过当前state状态 和变更前 state 状态进行比较从而确定是否调用 this.setState() 方法触发Connect及其子组件的重新渲染
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/913127.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!