Ref 用法
import React, { useRef, useEffect } from 'react'
import Counter from './components/Counter'
import SnapShort from './components/SnapShort'
import MPropTypes from './components/MPropTypes'
import SetContext from './components/SetContext'
import FaComponent from './components/PureComponent'
import RenderProp from './components/RenderProp'
import ModelPar from './components/Model'
import CatchError from './components/ErrorBoundary'
export interface SendProps {
name: string,
age: number,
gender: string,
isMarried?: boolean,
hobby: Array<string>,
position: { x: number, y: number }
}
// const App: React.FC = () => {
// const dv = useRef(null)
// useEffect(() => {
// console.log(dv.current)
// })
// return (
// <div ref={dv}></div>
// )
// }
const Input2 = React.forwardRef((props, ref) => {
return (<input type="text" ref={ref as { current: null }} />)
})
class App extends React.Component {
state: { list: string }
constructor(props: any) {
super(props)
this.button = React.createRef()
this.input2 = React.createRef()
this.state = {
list: '1'
}
}
input = {}
input2 = {
current: null
}
button = {
current: null
}
componentDidMount() {
console.log(this.refs.div)
console.log(this.input)
console.log(this.button.current)
console.log(this.input2.current)
}
render() {
let prop: SendProps = {
name: '张三',
age: 18,
gender: '男',
hobby: ['sm'],
position: { x: 100, y: 100 }
}
return (
<div ref='div'>
<input value={this.state.list} onChange={() => { }} readOnly ref={(ref) => { this.input = ref! }} type="text" />
<Input2 ref={this.input2} />
<button ref={this.button} onClick={() => { this.setState({ list: '456' }) }}>点击</button>
<div dangerouslySetInnerHTML={{ __html: '<span>gg</span>' }}></div>
<Counter />
<SnapShort />
<MPropTypes {...prop} />
<SetContext />
<FaComponent />
<RenderProp />
<ModelPar />
<CatchError />
</div>
)
}
}
export default App
context 用法
import React from 'react'
type IProps = {
color: string
}
const ThemeContext = React.createContext({ color: '', setColor: (color: string) => { } })
class GrandSon extends React.Component {
static contextType = ThemeContext
render() {
return (
<div style={{ color: this.context.color }}>GrandSon
<br />
<button onClick={() => this.context.setColor('red')}>变色</button>
</div>
)
}
}
class Another extends React.Component<IProps> {
constructor(props: IProps) {
super(props)
}
render() {
return (
<div style={{color: this.props.color}}>
Son
</div>
)
}
}
class Son extends React.Component {
render() {
return (
<GrandSon />
)
}
}
class SetContext extends React.Component<any, { color: string }> {
constructor(props: any) {
super(props)
this.state = {
color: 'pink'
}
}
setColor = (color: string) => {
this.setState(() => {
return {
color
}
})
}
render() {
let ctx = {
color: this.state.color,
setColor: this.setColor
}
return (
<ThemeContext.Provider value={ctx}>
<Son />
<ThemeContext.Consumer>
{
(value) => <Another {...value} />
}
</ThemeContext.Consumer>
</ThemeContext.Provider>
)
}
}
export default SetContext
context 简单实现
function createContext() {
class Provider extends React.Component {
static value = null
constructor(props) {
super(props)
Provider.value = props.value
this.state = { value: props.value }
}
static getDerivedStateFromProps(props, state) {
Provider.value = props.value
return { value: props.value }
}
render() {
return this.props.children
}
}
class Consumer extends React.Component {
render() {
return this.props.children(Provider.value)
}
}
return { Provider, Consumer }
}
const ThemeContexts = createContext()
class Son extends React.Component {
static contextType = ThemeContexts
render() {
this.context = Son.contextType.Provider.value
return (
<div>{this.context.color}</div>
)
}
}
class Parent extends React.Component {
render() {
return (
<ThemeContexts.Provider>
<Son />
<ThemeContext.Consumer>
{(value) => (<div>value.color</div>)}
</ThemeContext.Consumer>
</ThemeContexts.Provider>
)
}
}
getDerivedStateFromProps
import React from 'react'
interface IState {
num: number
}
class Counter extends React.Component<any, IState> {
constructor(props: any) {
super(props)
this.state = {
num: 0
}
}
add = () => {
this.setState(() => {
return { num: this.state.num + 1 }
})
}
render() {
return (
<div>
{this.state.num}
<button onClick={this.add}>点击+1</button>
<SubCounter num={this.state.num}></SubCounter>
</div>
)
}
}
class SubCounter extends React.Component<IState, IState> {
constructor(props: IState) {
super(props)
this.state = {
num: 11
}
}
static getDerivedStateFromProps(nextProps: IState, prevState: IState) {
// prevState { num: 11 }
if (nextProps.num % 2 === 0) {
return {
num: nextProps.num * 3
}
}
return null
}
render() {
return (
<div>
<br />
props: {this.props.num}
<br/>
{/* 替换了 state 中的 num */}
state: {this.state.num}
</div>
)
}
}
export default Counter
getSnapshotBeforeUpdate
import React from 'react'
interface IState {
message: number[]
}
class SnapShort extends React.Component<any, IState> {
wrapper: { current: null | HTMLDivElement }
constructor(props: any) {
super(props)
this.state = {
message: []
}
this.wrapper = React.createRef()
}
getSnapshotBeforeUpdate() {
return this.wrapper.current!.scrollHeight
}
componentDidUpdate(preProps: any, PreState: any, preScrollHeight: number) {
this.wrapper.current!.scrollTop = this.wrapper.current!.scrollTop + this.wrapper.current!.scrollHeight - preScrollHeight
}
componentDidMount() {
setInterval(() => {
this.setState(() => {
return {
message: [this.state.message.length, ...this.state.message]
}
})
}, 1000)
}
render() {
return (
<div style={{ width: '100px', height: '200px', border: '1px solid red', overflow: 'auto' }} ref={this.wrapper}>
<ul>
{this.state.message.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
)
}
}
export default SnapShort
PropTypes
import React from 'react'
import { SendProps } from '../App'
import PropTypes from 'prop-types'
class MPropTypes extends React.Component<SendProps> {
static defaultProps = {
isMaried: false
}
static propTypes = {
name: PropTypes.string.isRequired,
// age: PropTypes.number.isRequired,
gender: PropTypes.oneOf(['男', '女']),
isMarried: PropTypes.bool,
hobby: PropTypes.arrayOf(PropTypes.string),
position: PropTypes.shape({
x: PropTypes.number,
y: PropTypes.number
}),
age(props: any, propName: any, componentName: any) {
if(props[propName] < 18) {
return new Error(`Invalid Prop ${propName}`)
}
}
}
constructor(props: SendProps) {
super(props)
}
render() {
return (
<div>{JSON.stringify(this.props)}</div>
)
}
}
export default MPropTypes
PureComponent 实现和 meno 实现
import React from 'react'
interface IState {
number: { count: number, title: string }
}
function shallowEqual(a: any, b: any) {
if (a === b) {
return true
}
if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) {
return false
}
const arr = Object.keys(a)
const brr = Object.keys(b)
if (arr.length === brr.length && arr.every(item => a[item] === b[item])) {
return true
} else {
return false
}
}
class PureComponent extends React.Component<any, IState> {
shouldComponentUpdate(nextProps: any, nextState: any) {
return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState)
}
}
class WriteComponent extends PureComponent {
render() {
return (
<div>{this.props.count}</div>
)
}
}
class Title extends PureComponent {
render() {
console.log('渲染')
return (
<div>{this.props.title}</div>
)
}
}
function memo(FuncComponent: any) {
return class extends PureComponent {
render() {
return <FuncComponent {...this.props}/>
}
}
}
function FuncComonent(props: any) {
console.log('函数组件渲染')
return <div>{props.title}</div>
}
const MemoComponent = memo(FuncComonent)
class FaComponent extends React.Component<any, IState> {
constructor(props: any) {
super(props)
this.state = {
number: {
count: 1,
title: '第一次'
}
}
}
add = () => {
this.setState(() => {
return {
number: {
count: this.state.number.count + 1,
title: this.state.number.title
},
}
})
}
changeTitle = () => {
this.setState(() => {
return {
number: {
count: this.state.number.count,
title: this.state.number.title + '---'
},
}
})
}
render() {
return (
<div>
<Title title={this.state.number.title}/>
<MemoComponent title={this.state.number.title} />
<WriteComponent count={this.state.number.count} />
<button onClick={this.add}>点击</button>
<button onClick={this.changeTitle}>点击</button>
</div>
)
}
}
export default FaComponent
render prop
import React from 'react'
interface IState {
x: number,
y: number
}
interface IProp {
render: (props: IState) => JSX.Element
}
class RenderProp extends React.Component<IProp, IState> {
constructor(props: IProp) {
super(props)
this.state = {
x: 0,
y: 0
}
}
mouseMove = (event: React.MouseEvent<HTMLDivElement>) => {
this.setState({
x: event.clientX,
y: event.clientY
})
}
render() {
return (
<div onMouseMove={this.mouseMove}>
{this.props.render(this.state)}
</div>
)
}
}
function Child(props: IState) {
return <p>x={props.x}; y={props.y}</p>
}
class Proxy extends React.Component {
render() {
return (
<RenderProp render={(props: IState) => <Child {...props} />} />
)
}
}
export default Proxy
Portal
import React from 'react'
import ReactDom from 'react-dom'
class Model extends React.Component {
render() {
return ReactDom.createPortal(this.props.children, document.getElementById('model-root')!)
}
}
class ModelPar extends React.Component<any, { isShow: boolean }> {
constructor(props: any) {
super(props)
this.state = {
isShow: false
}
}
toggle = () => {
this.setState({ isShow: !this.state.isShow })
}
render() {
return (
<div>
{this.state.isShow && (<Model>
<div onClick={this.toggle} style={{ position: 'absolute', left: 0, top: 0, bottom: 0, right: 0, backgroundColor: 'rgba(0, 0, 0, .8)', color: '#fff' }}>
ModelSon
</div>
</Model>)}
<button onClick={this.toggle}>点击显示模态框</button>
</div>
)
}
}
export default ModelPar
错误边界
import React, { ErrorInfo } from 'react'
interface IState {
hasErrors: boolean
}
class Child extends React.Component {
render() {
let a: null | Array<number> = null
return (
<div>
<ul>
{a!.map(item => (
<li>{item}</li>
))}
</ul>
</div>
)
}
}
class CatchError extends React.Component {
render() {
return (
<div>
<div>
上面元素
</div>
<ErrorBoundary>
{/* 必须是子组件 */}
<Child />
</ErrorBoundary>
<div>
下面元素
</div>
</div>
)
}
}
class ErrorBoundary extends React.Component<any, IState> {
componentDidCatch(err: Error, info: ErrorInfo) {
this.setState({
hasErrors: true
})
}
constructor(props: any) {
super(props)
this.state = {
hasErrors: false
}
}
render() {
if (this.state.hasErrors) {
return <div>子组件发生未知错误</div>
}
return this.props.children
}
}
export default CatchError