React-组件进阶
组件进阶
组件通讯
多个组件之间需要共享某些数据,为了实现这个功能,打破组件的独立封闭性,让其与外界沟通,这个过程就是组件通讯组件的props
作用:接收传递给组件的数据 传递数据:给组件标签添加属性 接收数据:函数组件通过参数props接收数据,类组件通过this.props接收数据//props使用 //函数组件接收数据 const Hello = (props) => { //props是一个对象 console.log(props) return () } ReactDOM.render(props:{props.name}
, document.getElementByI
//类组件接收数据 class Hello extends React.Component { render() { console.log(this.props); return (特点 可以传递任意类型的数据 props是只读对象,只能读取属性的值,无法修改对象 使用类组件是,如果写了构造函数,应该将props传递给super(),否则无法在构造函数中获取到props) } } ReactDOM.render(props:{this.props.name}
, document.getElementById('root'));
class Hello extends React.Component { render() { console.log(this.props); this.props.fn() return () } } ReactDOM.render(props:{this.props.name}
{this.props.tag}} colors={['red', 'blue']} fn={() => { console.log(123); }} tag={ 123
} />, document.getElementById('root'));
组件通讯的三种方式
1.父组件传递数据给子组件//父组件 class Parent extends React.Component { state = { lastName: '王' } render() { return (父组件:) } } //子组件 const Child = (props) => { return (this.state.lastName} /> ) } ReactDOM.render(子组件,接收到父组件的数据{props.name}
, document.getElementById('root'))
2.子组件传递数据给父组件
//父组件 class Parent extends React.Component { state = { parentMsg: '' } //回掉函数 用来接收数据 getChildMsg = data => { console.log('接收到子组件传输的数据', data); this.setState({ parentMsg: data }) } render() { return (父组件:{this.state.parentMsg}) } } //子组件 class Child extends React.Component { state = { Msg: 'dddddddddddd' } handleClick = () => { //子组件调用父组件的回掉函数 this.props.getMsg(this.state.Msg) } render() { return (this.getChildMsg} /> 子组件:) } } ReactDOM.render(, document.getElementById('root'))
3.兄弟组件
//父组件 class Counter extends React.Component { //共享状态 state = { count: 0 } //提供修改状态的方法 onIncrement = () => { this.setState({ count: this.state.count + 1 }) } render() { return () } } const Child1 = (props) => { return (this.state.count} /> this.onIncrement} /> 计数器:{props.count}
) } const Child2 = (props) => { return } ReactDOM.render(, document.getElementById('root'))
Context
作用:跨组件传递数据(比如:主题、语言) 调用React.createContext()创建Provider(提供数据)和Consumer(消费数据)两个组件。//创建context组件 const { Provider, Consumer } = React.createContext() class App extends React.Component { render() { return () } } const Node = (props) => { return ( ) } const SubNode = (props) => { return ( ) } const Child = (props) => { return ( ) } ReactDOM.render({ (data) => 我是子节点---{data} } , document.getElementById('root'))
props深入
1.children属性 组件标签的子节点,当组件标签有子节点时,props就会有该属性 children属性与普通的props一样,值可以为任意值(文本、React元素,组件,函数等)const App = props => { console.log(props); return () } ReactDOM.render(组件标签的子节点:
{props.children}我是子节点 , document.getElementById('root'))
const Test = () => const App = props => { console.log(props); return () } ReactDOM.render(组件标签的子节点:
{props.children}{/* , document.getElementById('root'))我是p标签
*/}
const Test = () => { return () } const App = props => { console.log(props); props.children() return (2.props校验 安装prop-types包:npm i prop-types) } ReactDOM.render(组件标签的子节点:
{/* {props.children} */}{() => console.log('这是一个函数子节点')} , document.getElementById('root'))
const App = props => { const arr = props.colors const lis = arr.map((item, index) =>
- {lis}
//添加props校验 //属性a的类型:数值(number) //属性fn的类型:函数(func)并且为必填项 //属性tag的类型:React元素(element) //属性filter的类型:对象({area:'上海',price:1999}) App.propTypes = { colors: PropTypes.array, a: PropTypes.number, fn: PropTypes.func.isRequired, tag: PropTypes.element, filter: PropTypes.shape({ area: PropTypes.string, price: PropTypes.number }) } ReactDOM.render(props的默认值 场景:分页组件-》每页显示的条数{ }} />, document.getElementById('root'))
const App = props => { console.log(props) return () } //添加props默认值 App.defaultProps = { //当不传入pagesize是,则显示默认的10 pagesize: 10 } ReactDOM.render(此处展示props的默认值:{props.pagesize}
, document.getElementById('root'))
组件的生命周期
组件从被创建到挂载到页面中运行,再到组件不用时写在的过程 生命周期的每个阶段总伴随着一些方法调用,这些方法就是生命周期的钩子函数 钩子函数的作用,为开发人员在不同阶段操作组件提供了时机。 只有类组件才有生命周期生命周期的三个阶段
1.创建时(挂载阶段)class App extends React.Component { constructor(props) { super(props) //初始化state this.state = { count: 0 } //处理this指向问题 console.warn('生命周期钩子函数:constructor'); //获取不到,因为没有渲染结束,结果为null // const title = document.getElementById('title'); // console.log(title); } //进行dom操作 //能够发送ajax请求,获取远程的数据 componentDidMount() { console.warn('生命周期钩子函数:componentDidMount'); //渲染结束之后,可以获取title // const title = document.getElementById('title'); // console.log(title); } render() { // //错误提示 不要在render中调用setState // this.setState({ // count: 1 // }) console.warn('生命周期钩子函数:render'); return (2.更新时 执行时机:1.setState() 2forceUpdate 3.组件接收到新的props) } } ReactDOM.render(统计打豆豆的次数
, document.getElementById('root'))
class App extends React.Component { constructor(props) { super(props) //初始化state this.state = { count: 0 } } handleClick = () => { // this.setState({ // count: this.state.count + 1 // }) this.forceUpdate() } componentDidUpdate() { console.warn('---子组件生命周期钩子函数 componentDidUpdate'); //获取dom const title = document.getElementById('title') console.log(title); } render() { console.warn('生命周期钩子函数 render'); return () } } class Counter extends React.Component { render() { console.warn('---子组件生命周期钩子函数 render'); returnthis.state.count} /> 统计豆豆被打的次数{this.props.count}
} } ReactDOM.render(, document.getElementById('root'))
3.卸载时
class App extends React.Component { constructor(props) { super(props) //初始化state this.state = { count: 0 } } handleClick = () => { this.setState({ count: this.state.count + 1 }) } render() { return ({this.state.count > 3 ? () } } class Counter extends React.Component { componentDidMount() { //开启定时器 this.timeid = setInterval(() => { console.log('定时器正在执行'); }, 500) } render() { return豆豆被打死了
) : () } 统计豆豆被打的次数{this.props.count}
} componentWillUnmount() { console.warn('生命周期钩子函数 componentWillUnmount'); //清理定时器 clearInterval(this.timeid) } } ReactDOM.render(, document.getElementById('root'))
render-props和高阶组件
React组件复用概述 思考:如果两个组件中的部分功能相似或相同,该如何处理 处理方式:复用相似的功能 复用什么:1.state 2.操作state的方法 两种方式:1.render props模式 2.高阶组件(HOC) 注意:这两种方式不是新的API,而是利用React自身特点的编码技巧,演化成的固定模式。 Render props模式 问题1:如何难道该组件中复用的state? 在使用组件时,添加一个值为函数的prop,通过函数参数来获取(需要组件内部实现) 问题2:如何渲染任意的UI 使用该函数的返回值作为要渲染的UI内容(需要组件你饿不实现)/render props模式 //mouse组件创建 class Mouse extends React.Component { propsTypes = { } state = { x: 0, y: 0 } handleMouseMove = e => { this.setState({ x: e.clientX, y: e.clientY }) } //监听鼠标移动事件 componentDidMount() { window.addEventListener('mousemove', this.handleMouseMove) } //在组件卸载时移除事件绑定 componentWillUnmount() { window.addEventListener('mousemove', this.handleMouseMove) } render() { return this.props.children(this.state) } } //添加props校验 Mouse.propTypes = { children: PropTypes.func.isRequired } class App extends React.Component { render() { return () } } ReactDOM.render(render props 模式
{/*{ return */}鼠标当前位置{mouser.x},{mouser.y}
}} />{ mouser => { return ( {/*鼠标当前位置{mouser.x},{mouser.y}
) } }{ return */}}}>
{ mouser => ( {{ position: 'absolute', top: mouser.y, left: mouser.x } } /> )}
, document.getElementById('root'))
高阶组件
目的:实现状态逻辑复用 采用包装模式,比如说手机壳 思路分析:- 高阶组件(HOC)是一个函数,接收要包装的组件,返回增强后的组件
- 高阶组件内部创建一个类组件,在这个类组件中提供复用的状态逻辑,通过prop将复用的状态传递给被包装组件
//高阶组件 //创建高阶组件 function withMouse(WrappedComponent) { //该组件提供复用的状态逻辑 class Mouse extends React.Component { state = { x: 0, y: 0 } hanldemouseMove = e => { this.setState({ x: e.clientX, y: e.clientY }) } componentDidMount() { window.addEventListener('mousemove', this.hanldemouseMove) } componentWillUnmount() { window.removeEventListener('mousemove', this.hanldemouseMove) } render() { return设置displayname 使用高阶组件存在的问题:得到两个组件名称相同 原因:默认情况下,React使用组件名称作为displayName 解决方式:为高阶组件设置displayName便于调试时区分不同的组件 display Name的作用:用于设置调试信息this.state} /> } } return Mouse } const Position = props => ( 鼠标当前位置:(x:{props.x},y:{props.y})
) //加入图片 const Girl = props => { return ({{ position: 'absolute', top: props.y, left: props.x } } />) } //获取增强后的组件 const Mouseposition = withMouse(Position) //调用图片迁移的高阶组件 const MouseGirl = withMouse(Girl) class App extends React.Component { render() { return (
) } } ReactDOM.render(高阶组件
, document.getElementById('root'))
//设置display Mouse.displayName = 'WithMouse' + getDisplayName(WrappedComponent) function getDisplayName(WrappedComponent) { return WrappedComponent.displayName || WrappedComponent.name || 'Component' }