React使用

系列文章目录


目录

系列文章目录

前言

一、JSX

二、事件

三、表单

四、setState

五、组件声明周期

六、React 高级特性

七、Redux

八、React-router

总结


前言

react使用


一、JSX

// 原生html
const rawHtml = '<span>富文本内容<i>斜体</i><b>加粗</b></span>';
const rawHtmlData = {
    __html: rawHtml,    // 注意,必须是这种格式
}
const rawHtmlElem = <div>
    <p dangerouslySetInnerHTML={rawHtmlData}></p>
    <p>{rawHtml}</p>
</div>
return rawHtmlElem;

二、事件

  • event 是 SyntheticEvent,模拟出来 DOM 事件所有能力
  • event.nativeEvent 是原生事件对象
  • 所有的事件,都被挂载到(16版本 document 上)(17版本 root 组件上)
  • 和 DOM 事件不一样,和 Vue事件也不一样
// 获取 event
clickHandle = (event) => {
    event.preventDefault() // 阻止默认行为
    event.stopPropagation() // 阻止冒泡
    console.log('target', event.target) // 指向当前元素,即当前元素触发
    console.log('current target', event.currentTarget) // 指向当前元素,假象!

    // 注意,event 其实是 React 封装的。可以看 __proto__.constructor 是syntheticEvent 组合事件
    console.log('event', event) // 不是原生的Event,原生的 MouseEvent
    console.log('event.__proto__.constructor',event.__proto__.constructor)

    // 原生 event 如下。其 __proto__.constructor 是 MouseEvent
    console.log('nativeEvent',event.nativeEvent)
    console.log('nativeEvent target',event.nativeEvent.target) // 指向当前元素,即当前元素触发
    console.log('nativeEvent current target',event.nativeEvent.current.target) // 指向document

}

// 传递参数
clickHandlerEvent(id, title, event) {
    console.log('event', event); // 最后追加一个参数,即可接收event
}

三、表单

受控组件:表单里的值受state控制

四、setState

不可变值(返回新值,不影响原值)

可能是异步更新 可能会被合并(react <=17)

异步更新+合并state(react18,自动批处理)

// 不要直接修改state,使用不可变值(不修改原值)
// this.state.count++ // 错误
this.setState({
    count: this.state.count + 1 //正确
    // count: this.state.count
})

// 不可变值(函数式编程,纯函数)-数组
const list5Copy = this.state.list5.slice();
list5Copy.splice(2,0,'a'); // 中间插入、删除
this.setState({
    list1: this.state.list1.concat(100), //追加
    list2: [...this.state.list2, 100], //追加
    list3: this.state.list3.slice(0,3), // 截取
    list4: this.state.list4.filter(item => item>100),//筛选
    list5: list5Copy //其他
})
// 注意:不能直接对 this.state.list 进行push/pop/splice 等,这样违反不可变值

// 不可变值-对象
this.setState({
    obj1: Object.assign({}, this.state.obj1, {a: 100}),
    obj2: {...this.state.obj2, a: 100}
})
// 注意:不能直接对 this.state.obj 进行属性设置,这样违反不可变值
// 可能是异步的
this.setState({
    count: this.state.count + 1
}, ()=>{
    // 回调拿最新值
    console.log(this.state.count)
})
console.log(this.state.count); // 异步的,拿不到最新值

// setTimeout中setState是同步的
setTimeout(() => {
    this.setState({
        count: this.state.count +1
    })
    console.log(this.state.count)
},0)

// 自己定义的DOM事件,setState是同步的
bodyClickHandler = () => {
    this.setState({
        count: this.state.count+1
    })
}

componentDidMount() {
    document.body.addEventListener('click',this.bodyClickHandler)
}

componentWillUnmount() {
    // 及时销毁自定义DOM事件
    document.body.removeEventListener('click',this.bodyClickHandler)
    // clearTimeout
}
// 可能会合并
// 传入对象,会被合并(类似Object.assign),执行结果只一次 +1
this.setState({
    count: this.state.count +1
})
this.setState({
    count: this.state.count +1
})
this.setState({
    count: this.state.count +1
})

// 传入函数,不会被合并,执行结果是+3
this.setState((prevState, props) => {
    return {
        count: prevState.count + 1
    }
})
this.setState((prevState, props) => {
    return {
        count: prevState.count + 1
    }
})
this.setState((prevState, props) => {
    return {
        count: prevState.count + 1
    }
})

React <= 17 setState

1. React组件事件:异步更新 + 合并 state

2.DOM事件,setTimeout:同步更新,不合并state

3.只有React组件事件才批处理

React18 setState

1. React组件事件:异步更新 + 合并 state

2.DOM事件,setTimeout:异步更新 + 合并 state

3.Automatic Batching 自动批处理

五、组件声明周期

单组件生命周期

六、React 高级特性

  • 函数组件
    纯函数,输入props,输出JSX
    没有实例,没有生命周期,没有state
    不能扩展其他方法
  • 非受控组件-优先使用受控组件 必须操作DOM时,再使用非受控组件​​​​​​​
    // 使用场景
    // 1. 必须手动操作DOM元素,setState实现不了
    // 2. 文件上传 <input type='file'>
    // 3. 某些富文本编辑器,需要传入DOM元素
    
    // 优先使用受控组件 必须操作DOM时,再使用非受控组件
    
    // ref
    // defaultValue defaultChecked
    // 手动操作 DOM 元素
    class App extends React.Component {
        constructor(props) {
            super(props)
            this.state = {
                name: '豆',
                flag: true
            }
            this.nameInputRef = React.createRef() // 创建ref
            this.fileInputRef = React.createRef()
        }
        render() {
            return <div>
                // 使用defaultValue 而不是 value,使用ref
                <input defaultValue={this.state.name} ref={this.nameInputRef}>
                // state并不会随着改变
                <span>{this.state.name}</span>
    
                <button onClick={this.alertName}>click</button>
            </div>
    
            // checkout defaultChecked
            // return <div>
            //    <input type='checkbox' defaultChecked={this.state.flag}>
            // </div>
    
            // file
            // return <div>
            //    <input type='file' ref={this.fileInputRef} />
            //    <button onClick={this.alertFile}>file</button>
            // </div>
    
        }
        alertName = () => {
            const elem = this.nameInputRef.current; // 通过ref获取DOM节点
            console.log(elem.value) // 不是 this.state.name
        }
        alertFile = () => {
            const elem = this.fileInputRef.current; // 通过ref获取DOM节点
            console.log(elem.file[0].name)
        }
    
    }
  • Portals(传送门)(组件默认会按照既定层级嵌套渲染,组件渲染到父组件以外使用Portals)
    // 使用场景
    overflow: hidden
    父组件 z-index 值太小
    fixed需要放在body第一层
    
    class AdvancedUse extends React.Component {
        constructor(props) {
            super(props)
        }
        render() {
            return <>
                <PortalsDemo>children内容</PortalsDemo>
            </>
        }
    }
    
    // PortalsDemo组件
    import ReactDOM from 'react-dom';
    
    class PortalsDemo extends React.Component {
        constructor(props) {
            super(props)
            this.state = {}
        }
        render() {
            // 正常 
            // this.props.children类似vue slot
            // return <div className='modal'>{this.props.children}</div>
    
            // 使用 Protals 渲染到 body 上
            // fixed 元素要放在 body 上,有更好的浏览器兼容性
            return ReactDom.createProtal(
                <div className='modal'>{this.props.children}</div>,
                document.body    // DOM节点(渲染到的地方)
            )
        }
    }
  • context(上下文-公共信息,传递给每个组件)
    // 创建Context 填入默认值
    const ThemeContext = React.createContext('light')
    
    class App extends React.Component{
        constractor(props) {
            super(props);
            this.state = {
                theme: 'light'
            }
        }
    
        render() {
            return <ThemeContext.Provider value={this.state.theme}>
                <Toolbar />
            </ThemeContext.Provider>
        }
    }
    
    // Toolbar组件
    function Toolbar(props) {
        return (
            <ThemedButton />
        )
    }
    
    // 子组件使用class
    class ThemedButton extends React.Component {
        // 指定 contextType 读取当前的 theme context
        // static contextType = ThemeContext // 也可以ThemedButton.contextType = ThemeContext
        render() {
            const theme = this.context    // React 会往上找到最近的 theme Provider
            return <p>{theme}</p>
        }
    }
    ThemedButton.contextType = ThemeContext // 制定 contextType 读取当前 theme context
    
    // 函数组件
    function ThemeLick (props) {
        // const theme = this.context // 会报错,函数式组件没有实例
        
        // 函数式组件可以使用 Consumer
        return <ThemeContext.Consumer>
            {value => <p>{value}</p>}
        </ThemeContext.Consumer>
    }
  • 异步组件
    const ContextDemo = React.lazy(() => import('./ContextDemo'))
    
    class App extends React.Component {
        constructor(props) {
            super(props)
        }
        render() {
            return <div>
                <React.Suspense fallback={<div>loading...</div>}>
                    <ContextDemo />
                </React.Suspense>
            </div>
        }
    }
  • 性能优化
    // 1.shouldComponentUpdate(SCU)
    // 2.PureComponent 和 React.memo
    // 3.不可变值 immutable.js
    
    // react 默认:父组件有更新,子组件则无条件也更新
    shouldComponentUpdate(nextProps, nextState) {
        if(nextState.count !== this.state.count) {
            return true; //可以渲染
        }
        return false; // 不重复渲染
    }
    // SCU 默认返回true, 即react 默认重新渲染所有子组件
    // 必须配合“不可变值”一起使用
    // 可先不用 SCU,有性能问题时再考虑使用
    
    // PureComponent ,SCU 中实现浅比较(配合不可变值)
    class List extends React.PureComponent {
        constructor(props){
            super(props)
        }
        render() {}
    }
    
    // memo 函数组件中的 PureComponent
    function MyComponent(props) {
        // 使用 props 渲染
    }
    function areaEqual(prevProps, nextPoprs) {
        // 如果把nextPoprs 传入 render 方法的返回结果与
        将 prevProps 传入render方法的返回结果一致则返回true
        否则返回 false
    }
    export default React.memo(MyComponent, areaEqual)
    
    // immutable.js
    // 基于共享数据(不是深拷贝),速度好
    const map1 = Immutable.Map({a:1,b:2,c:3});
    const map2 = map1.set('b',50);
    map1.get('b'); // 2
    map2.get('b'); // 50
  • 高阶组件HOC
    // 高阶组件不是一种功能,而是一种模式
    // 接收一个组件,返回一个新组件
    const HOCFactory = (Component) => {
        class HOC  extends React.Componet {
            // 在此定义多个组件的公共逻辑
            render (){
                return <Component {...this.props} />    // 返回拼装的结果
            }
        }
        return HOC
    }
    const EnhancedComponent1 = HOCFactory(WrappedComponent1);
    const EnhancedComponent2 = HOCFactory(WrappedComponent2);
  • Render Props
    // Render Props 的核心思想
    // 通过一个函数将class 组件的 state 作为 porps 传递给纯函数组件
    class Factory extends React.Component {
        constructor() {
            this.state = {
                // state 即多个组件的公共逻辑的数据
            }
        }
        // 修改 state
        render() {
            return <div>{this.props.render(this.state)}</div>
        }
    }
    // 属性校验
    Factory.propTypes = {
        render: PropTypes.func.isRequired    // 必须接收一个render属性,而且是函数
    }
    
    const App = (props) => {
        <Factory render={
            // render 是一个函数组件
            (props) => <p> {props.a} {props.b} ... </p>
        }/>
    }

    HOC vs Render Props

    HOC:模式简单,但会增加组件层级(透传可能有属性覆盖)

    Render Props:代码简洁,学习成本较高

七、Redux

Redux是一个数据状态管理插件

单项数据流

redux 更新过程:dispatch 一个 action,reducer接收,判断action,返回新的state,触发订阅更新,绑定到视图层

dispatch(action)

reducer -> newState(不可变值)

subscribe 触发通知

// redux 单独使用
import { createStore } from 'redux'

export default function() {
    // 第一步:定义计算规则,即reducer
    function counter(state = 0, action){
        switch (action.type) {
            case 'INCREMENT':
                return state + 1
            case 'decrement':
                return state - 1
            default:
                return state
        }
    }

    // 第二步:根据计算规则生成store
    let store = createStore(counter)

    // 第三步:定义数据(即 state)变化之后的派发规则
    store.subscribe(() => {
        console.log('fn1 -> current state', store.getState())
    })
    store.subscribe(() => {
        console.log('fn2 -> current state', store.getState())
    })
    store.subscribe(() => {
        console.log('fn2 -> current state', store.getState())
    })

    // 第四步:触发数据变化(dispatch一个action)数据变化后触发更新订阅
    store.dispatch({type: 'INCREMENT'})
    store.dispatch({type: 'INCREMENT'})
    store.dispatch({type: 'DECREMENT'})
}

react-redux

// <Provider>
// connect
// mapStateToProps mapDispatchToProps

import React from 'react'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'

let store = createStore(todoApp)

export default fuunction() {
    return <Provider store={store}>
        <App />
    </Provider>

}

异步action

import {createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';

// 创建store 时,作为中间件传入 redux-thunk
const store = createStore(rootReducer, applyMiddleware(thunk));

// 同步action
export const addTo = text => {
    // 返回action对象
    return { type: 'ADD', id: '123'}
}

// 异步 action
export const addTodoAsync = text => {
    // 返回函数,其中有 dispatch 参数
    return (dispatch) => {
        // ajax 异步获取数据
        fetch(url).then(res => {
            // 执行异步 action
            dispatch(addTo(res.text))
        })
    }    
}

redux 中间件(常用中间件 redux-thunk/redux-promise/redux-saga)

view触发dispatch action,dispatch可能有中间件会再次触发action,reducer根据action返回新的state,新的state触发视图更新

八、React-router

路由模式(hash-to B、H5 history-to C)

路由配置(动态路由、懒加载)

获取url中的参数,可以使用this.props.params.xx

// hash
import { HashRouter as Router, Switch, Route } from 'react-router-dom'

// history
import { BrowserRouter as Router, Switch, Route} from 'react=router-dom'

动态路由

跳转路由(<Link to='/' />  useHistory)

 懒加载

针对大型项目的静态资源懒加载问题,react-router 也给出了解决方案 —— huge-apps,它将 react-router 本身和 webpack 的 require.ensure 结合起来,就解决了这一问题。

九、性能优化

性能检测

安装react性能检测工具 npm i react-addons-perf --save,然后在 ./app/index.js中

// 性能测试
import Perf from 'react-addons-perf'
if(__DEV__){
    window.Perf = Perf
}

运行程序。在操作之前先运行Perf.start()开始检测,然后进行若干操作,运行Perf.stop停止检测,然后再运行Perf.printWasted()即可打印出浪费性能的组件列表。在项目开发过程中,要经常使用检测工具来看看性能是否正常。

PureRenderMixin优化

React 最基本的优化方式是使用PureRenderMixin,安装工具 npm i react-addons-pure-render-mixin --save,然后在组件中引用并使用

import React from 'react'
import PureRenderMixin from 'react-addons-pure-render-mixin'

class List extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
        // 重写组件的shouldComponentUpdate 函数,在每次更新之前判断props和state,如果有变化则返回true,无变化返回false
    }
    
}

Immutable.js 优化

层级很深,考虑用Immutable

var map1 = Immutable.Map({a:1, b:2, c:3});
var map2 = map1.set('b', 50);
map1.get('b'); // 2
map2.get('b'); // 50

总结

react基本使用知识点

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值