React是前端三大框架之一,在开发中也是一项技能;这里从实际开发中总结了React开发的一些技巧,适合React初学或者有一定项目经验的同学。
1、组件通讯
1.1道具
子组件
import React from "react";import PropTypes from "prop-types";import { Button } from "antd";export default class EightteenChildOne extends React.Component { static propTypes = { //propTypes校验传入类型,详情在技巧11 name: PropTypes.string }; click = () => { // 通过触发方法子传父 this.props.eightteenChildOneToFather("这是 props 改变父元素的值"); }; render() { return (
这是通过 props 传入的值{this.props.name}
点击改变父元素值
); }}
父组件
this.eightteenChildOneToFather(mode)}> // 或者
传传值时:
传统写法
const {dataOne,dataTwo,dataThree} = this.state
升级写法
1.2道具升级版
原理:子组件里面利用props获取父组件方法直接调用,从而改变父组件的值
注意:此方法和props大同小异,都是props的应用,所以在源码中没有模仿
调用父组件方法改变该值
// 父组件state = { count: {}}changeParentState = obj => { this.setState(obj);}// 子组件onClick = () => { this.props.changeParentState({ count: 2 });}
1.3提供者,消费者和上下文
1.Context在16.x之前是定义一个类别的对象,类似vue的eventBus,如果组件要使用到该值直接通过this.context获取
//根组件class MessageList extends React.Component { getChildContext() { return {color: "purple",text: "item text"}; } render() { const {messages} = this.props || {} const children = messages && messages.map((message) => ); return
{children}
; }}MessageList.childContextTypes = { color: React.PropTypes.string text: React.PropTypes.string};//中间组件class Message extends React.Component { render() { return (
Delete
); }}//孙组件(接收组件)class MessageItem extends React.Component { render() { return (
{this.context.text}
); }}MessageItem.contextTypes = { text: React.PropTypes.string //React.PropTypes在 15.5 版本被废弃,看项目实际的 React 版本};class Button extends React.Component { render() { return ( {this.props.children} ); }}Button.contextTypes = { color: React.PropTypes.string};
2.16.x之后的上下文使用了提供商和客户模式,在某些提供商的初始值,在子孙级的消费者中获取该值,并且能够传递函数,修改了上下文声明了一个上下文的定义,上下文.js
import React from 'react'let { Consumer, Provider } = React.createContext();//创建 context 并暴露Consumer和Provider模式export { Consumer, Provider}
父组件导入
// 导入 Providerimport {Provider} from "../../utils/context"
父组件定义的值:{name}
子组件
// 导入Consumerimport { Consumer } from "../../utils/context"function Son(props) { return ( //Consumer容器,可以拿到上文传递下来的name属性,并可以展示对应的值 {name => (
// 在 Consumer 中可以直接通过 name 获取父组件的值
子组件。获取父组件的值:{name}
)} );}export default Son;
1.4 EventEmitter
EventEmiter传送门 使用事件插件定义一个类别的事件机制
1.5路由传参
1.参数
xxxthis.props.history.push({pathname:"/path/" + name});读取参数用:this.props.match.params.name
2.查询
this.props.history.push({pathname:"/query",query: { name : 'sunny' }});读取参数用: this.props.location.query.name
3.状态
this.props.history.push({pathname:"/sort ",state : { name : 'sunny' }});读取参数用: this.props.location.query.state
4.搜索
xxxthis.props.history.push({pathname:`/web/search?id ${row.id}`});读取参数用: this.props.location.search
这个在react-router-dom:v4.2.2有bug,传参替换页面会空白,刷新才会加载出来
5.优缺点
1.params在HashRouter和BrowserRouter路由中刷新页面参数都不会丢失2.state在BrowserRouter中刷新页面参数不会丢失,在HashRouter路由中刷新页面会丢失3.query:在HashRouter和BrowserRouter路由中刷新页面参数都会丢失4.query和 state 可以传对象
1.6 onRef
原理:onRef通讯原理就是通过props的事件机制将组件的this(组件实例)当做参数传到父组件,父组件就可以操作子组件的状态和方法
jsx
export default class EightteenChildFour extends React.Component { state={ name:'这是组件EightteenChildFour的name 值' } componentDidMount(){ this.props.onRef(this) console.log(this) // ->将EightteenChildFour传递给父组件this.props.onRef()方法 } click = () => { this.setState({name:'这是组件click 方法改变EightteenChildFour改变的name 值'}) }; render() { return (
{this.state.name}
点击改变组件EightteenChildFour的name 值
); }}
seven.jsx
eightteenChildFourRef = (ref)=>{ console.log('eightteenChildFour的Ref值为') // 获取的 ref 里面包括整个组件实例 console.log(ref) // 调用子组件方法 ref.click()}
1.7参考
原理:就是通过React的ref属性获取到整个子组件实例,再进行操作
.jsx
// 常用的组件定义方法export default class EightteenChildFive extends React.Component { state={ name:'这是组件EightteenChildFive的name 值' } click = () => { this.setState({name:'这是组件click 方法改变EightteenChildFive改变的name 值'}) }; render() { return (
{this.state.name}
点击改变组件EightteenChildFive的name 值
); }}
seven.jsx
// 钩子获取实例componentDidMount(){ console.log('eightteenChildFive的Ref值为') // 获取的 ref 里面包括整个组件实例,同样可以拿到子组件的实例 console.log(this.refs["eightteenChildFiveRef"]) }// 组件定义 ref 属性
1.8 Redux
redux是一个独立的事件通讯插件,这里就不做过多的叙述
1.9 MobX
MobX也是一个独立的事件通讯插件,这里就不做过多的叙述
1.10通量
flux也是一个独立的事件通讯插件,这里就不做过多的叙述
1.11挂钩
1.hooks是利用userReducer和context实现通讯,下面模拟实现一个简单的redux
2.核心文件分为action,reducer,键入
action.js
import * as Types from './types';export const onChangeCount = count => ({ type: Types.EXAMPLE_TEST, count: count + 1})
reducer.js
import * as Types from "./types";export const defaultState = { count: 0};export default (state, action) => { switch (action.type) { case Types.EXAMPLE_TEST: return { ...state, count: action.count }; default: { return state; } }};
types.js
export const EXAMPLE_TEST = 'EXAMPLE_TEST';
18.jsx
export const ExampleContext = React.createContext(null);//创建createContext上下文// 定义组件function ReducerCom() { const [exampleState, exampleDispatch] = useReducer(example, defaultState); return ( );}
EightteenChildThree.jsx //组件
import React, { useEffect, useContext } from 'react';import {Button} from 'antd'import {onChangeCount} from '../../pages/TwoTen/store/action';import { ExampleContext } from '../../pages/TwoTen/eighteen';const Example = () => { const exampleContext = useContext(ExampleContext); useEffect(() => { // 监听变化 console.log('变化执行啦') }, [exampleContext.exampleState.count]); return (
值为{exampleContext.exampleState.count}
exampleContext.dispatch(onChangeCount(exampleContext.exampleState.count))}>点击加 1
)}export default Example;
3.hooks实际上就是对常设React的API进行了封装,暴露比较方便使用的钩子;
4.钩子有:
5,使用即时方法
function FancyInput(props, ref) { const inputRef = useRef(); useImperativeMethods(ref, () => ({ focus: () => { inputRef.current.focus(); } })); return ;}FancyInput = forwardRef(FancyInput);
1.12插槽
slot就是将父组件的标签传给子组件,类似vue的v-slot
场景:一些组件只是共享部分dom逻辑,里面有部分逻辑是独立的
// 父组件文件import SlotChild from 'SlotChild'这是父组件的 slot
}>// 子组件子组件直接获取 this.props.slot 就可获取到内容