1、非受控组件
ref
defaultValue defaultChecked
手动操作DOM元素
2、Portals
类似于css的hack解决兼容的技巧
Portals使用场景
overflow:hidden
父组件z-index值太小
fixed需要放在body第一层级
使用方法
//父组件调用
<PortalsDemo>Modal 内容</PortalsDemo>
//子组件内容
render() {
// // 正常渲染
// return <div className="modal">
// {this.props.children} {/* vue slot */}
// </div>
// 使用 Portals 渲染到 body 上。
// fixed 元素要放在 body 上,有更好的浏览器兼容性。
return ReactDOM.createPortal(
<div className="modal">{this.props.children}</div>,
document.body // DOM 节点
)
}
效果如下
3、Context
公共信息(语言、主题)如何传递给每个组件?
用props太繁琐
用redux小题大做
三部:产生->修改->消费
1、上下文的应用三部:产生->修改->消费
1、 创建 Context 填入默认值(任何一个 js 变量)
//全局创建
const ThemeContext = React.createContext('light')
//父组件中的light变量
constructor(props) {
super(props)
this.state = {
theme: 'light'
}
}
把light这个变量放到父组件的上下文中,能功其他后代组件使用
render() {
return <ThemeContext.Provider value={this.state.theme}>
<Toolbar />
<hr/>
<button onClick={this.changeTheme}>change theme</button>
</ThemeContext.Provider>
}
2、怎么消费这个
class组件使用方法
1、可以在组件外部指定,通过**[组件名.contextType]**指定一下对应的上下文
ThemedButton.contextType = ThemeContext
//指定 contextType 读取当前的 theme context。
2、也可以在组件内部用ES6语法 static静态属性的方式指定,
static contextType = ThemeContext
使用
const theme = this.context
// React 会往上找到最近的 theme Provider,然后使用它的值。
函数组件怎么消费
// 底层组件 - 函数是组件
function ThemeLink (props) {
// const theme = this.context // 会报错。函数式组件没有实例,即没有 this
// 函数式组件可以使用 Consumer
return <ThemeContext.Consumer>
{ value => <p>link's theme is {value}</p> }
</ThemeContext.Consumer>
2、函数式组件的HOOKS函数的应用
useState:在函数组件中应用状态
useEffect:在函数组件中应用周期函数
useRef:在函数组件中操作DOM 连接地址 blog.csdn.net/weixin_4167…
3、函数组件应用上下文
连接地址
4、异步组件
import()
React.lazy
React.Suspense
const ContextDemo = React.lazy(() => import('./ContextDemo'))
class App extends React.Component {
constructor(props) {
super(props)
}
render() {
return <div>
<p>引入一个动态组件</p>
<hr />
<React.Suspense fallback={<div>Loading...</div>}>
<ContextDemo/>
</React.Suspense>
</div>
// 1. 强制刷新,可看到 loading (看不到就限制一下 chrome 网速)
// 2. 看 network 的 js 加载
}
}
5、性能优化
1、性能优化在React更加重要,相对于vue更加重要
shouldComponentUpdata(简称SCU)
shouldComponentUpdata是可选的生命周期,默认返回值是true,即在React中默认:父组件有更新,子组件则无条件也更新
SCU一定要配合不可变值
React为什么要弄这么一个可选 可定制的生命周期,为什么不直接封装好呢
class Footer extends React.Component {
constructor(props) {
super(props)
}
render() {
return <p>
{this.props.text}
{this.props.length}
</p>
}
componentDidUpdate() {
console.log('footer did update')
}
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.text !== this.props.text
|| nextProps.length !== this.props.length) {
return true // 可以渲染
}
return false // 不重复渲染
}
// React 默认:父组件有更新,子组件则无条件也更新!!!
// 性能优化对于 React 更加重要!
// SCU 一定要每次都用吗?—— 需要的时候才优化
}
父组件调用
render() {
return <div>
<Input submitTitle={this.onSubmitTitle}/>
<List list={this.state.list}/>
<Footer text={this.state.footerInfo} length={this.state.list.length}/>
</div>
}
SCU使用新的state和旧的state做比较,返回值true和false又决定了是否重新渲染
默认返回ture,即默认不相等,都重新渲染
相同则返回false,不渲染
2、PureComponent和React.memo
PureComponent用在class组件
React.memo用在函数组件
PureComponent,SCU中实现了浅比较
如果不相同,则返回true,重新渲染
也是配合不可变值来使用
浅比较已经使用大部分情况,尽量不要做深度比较,深度比较,一次性递归到底,性能不好
深度比较用
import _ from 'lodash'
//子组件
class List extends React.Component {
constructor(props) {
super(props)
}
render() {
const { list } = this.props
return <ul>{list.map((item, index) => {
return <li key={item.id}>
<span>{item.title}</span>
</li>
})}</ul>
}
// 增加 shouldComponentUpdate
shouldComponentUpdate(nextProps, nextState) {
// _.isEqual 做对象或者数组的深度比较(一次性递归到底)
if (_.isEqual(nextProps.list, this.props.list)) {
// 相等,则不重复渲染
return false
}
return true // 不相等,则渲染
}
}
父组件
render() {
return <div>
<Input submitTitle={this.onSubmitTitle}/>
<List list={this.state.list}/>
</div>
}
onSubmitTitle = (title) => {
// 正确的用法
this.setState({
list: this.state.list.concat({
id: `id-${Date.now()}`,
title
})
})
// // 为了演示 SCU ,故意写的错误用法
// this.state.list.push({
// id: `id-${Date.now()}`,
// title
// })
// this.setState({
// list: this.state.list
// })
}