建站快车是什么,wordpress博客增加音乐页面,河北网站开发联系电话,现在装宽带多少钱文章目录 组件通信组件通信的意义父传子实现props说明子传父实现兄弟组件通信跨组件通信Context通信案例 React组件进阶children属性props校验组件生命周期 组件通信
组件通信的意义
组件是独立且封闭的单元#xff0c;默认情况下组件只能使用自己的数据#xff08;state默认情况下组件只能使用自己的数据state 组件化开发的过程中完整的功能会拆分多个组件在这个过程中不可避免的需要互相传递一些数据 为了能让各组件之间可以进行互相沟通数据传递这个过程就是组件通信
父子关系 - 最重要的兄弟关系 - 自定义事件模式产生技术方法 eventBus / 通过共同的父组件通信其它关系 - mobx / redux / zustand
父传子实现
实现步骤
父组件提供要传递的数据 - state 可以不满足例如传递方法的时候给子组件标签添加属性值为 state中的数据子组件中通过 props 接收父组件中传过来的数据 类组件使用this.props获取props对象函数式组件直接通过参数获取props对象 在子组件标签上添加的属性都会成为该子组件props属性对象上的值 接下来我们来实现一下如下的场景 实现代码
import React from react// 函数式子组件
function FSon(props) {console.log(props)return (div子组件1{props.msg}/div)
}// 类子组件
class CSon extends React.Component {render() {return (div子组件2{this.props.msg}/div)}
}
// 父组件
class App extends React.Component {state {message: this is message}render() {return (divdiv父组件/divFSon msg{this.state.message} /CSon msg{this.state.message} //div)}
}export default Appprops说明 props是只读对象readonly 根据单项数据流的要求子组件只能读取props中的数据不能进行修改 props可以传递任意数据 数字、字符串、布尔值、数组、对象、函数、JSX
class App extends React.Component {state {message: this is message}render() {return (divdiv父组件/divFSon msg{this.state.message} age{20} isMan{true} cb{() { console.log(1) }} child{spanthis is child/span}/CSon msg{this.state.message} //div)}
}同时我们在使用props的时候通常会使用解构赋值例如 当然我们也可以直接在函数的参数位置进行解构赋值 子传父实现
本质 父组件给子组件传递回调函数子组件调用
实现步骤
父组件提供一个回调函数 - 用于接收数据将函数作为属性的值传给子组件子组件通过props调用 回调函数将子组件中的数据作为参数传递给回调函数
接下来我们来实现一下如下的场景
代码实现
import React from react// 子组件
function Son(props) {function handleClick() {// 调用父组件传递过来的回调函数 并注入参数props.changeMsg(this is newMessage)}return (div{props.msg}button onClick{handleClick}change/button/div)
}class App extends React.Component {state {message: this is message}// 提供回调函数changeMessage (newMsg) {console.log(子组件传过来的数据:,newMsg)this.setState({message: newMsg})}render() {return (divdiv父组件/divSonmsg{this.state.message}// 传递给子组件changeMsg{this.changeMessage}//div)}
}export default App兄弟组件通信
核心思路 通过状态提升机制利用共同的父组件实现兄弟通信。也就是说通过子传父父传子来实现。 实现步骤
将共享状态提升到最近的公共父组件中由公共父组件管理这个状态 提供共享状态提供操作共享状态的方法 要接收数据状态的子组件通过 props 接收数据要传递数据状态的子组件通过props接收方法调用方法传递数据
代码实现
import React from react// 子组件A
function SonA(props) {return (divSonA{props.msg}/div)
}
// 子组件B
function SonB(props) {return (divSonBbutton onClick{() props.changeMsg(new message)}changeMsg/button/div)
}// 父组件
class App extends React.Component {// 父组件提供状态数据state {message: this is message}// 父组件提供修改数据的方法changeMsg (newMsg) {this.setState({message: newMsg})}render() {return ({/* 接收数据的组件 */}SonA msg{this.state.message} /{/* 修改数据的组件 */}SonB changeMsg{this.changeMsg} //)}
}export default App跨组件通信Context 上图是一个react形成的嵌套组件树如果我们想从App组件向任意一个下层组件传递数据该怎么办呢目前我们能采取的方式就是一层一层的props往下传显然很繁琐
那么Context 提供了一个无需为每层组件手动添加 props就能在组件树间进行数据传递的方法
实现步骤 创建Context对象 导出 Provider 和 Consumer对象 const { Provider, Consumer } createContext()使用Provider包裹上层组件提供数据 注意Provider组件不一定必须包裹根组件但是必须包裹需要共享数据的子组件的最近公共祖先组件。这样Provider组件可以将数据通过Context对象传递给所有的子孙组件而不需要通过props逐层传递 Provider value{this.state.message}{/* 根组件 */}
/Provider需要用到数据的组件使用Consumer包裹获取数据 注意这里括号里面是一个箭头函数的方法声明形式不能改变 Consumer {value /* 基于 context 值进行渲染*/}
/Consumer代码实现
import React, { createContext } from react// 1. 创建Context对象
const { Provider, Consumer } createContext()// 3. 消费数据
function ComC() {return (Consumer {value div{value}/div}/Consumer)
}function ComA() {return (ComC/)
}// 2. 提供数据
class App extends React.Component {state {message: this is message}render() {return (Provider value{this.state.message}div classNameappComA //div/Provider)}
}export default App通信案例
要求App为父组件用来提供列表数据 ListItem为子组件用来渲染列表数据 // 列表数据
[{ id: 1, name: 超级好吃的棒棒糖, price: 18.8, info: 开业大酬宾全场8折 },{ id: 2, name: 超级好吃的大鸡腿, price: 34.2, info: 开业大酬宾全场8折 },{ id: 3, name: 超级无敌的冰激凌, price: 14.2, info: 开业大酬宾全场8折 }
]代码实现
import React from react// 子组件
function ListItem(props) {const { name, price, info, id, delHandler } propsreturn (divh3{name}/h3p{price}/pp{info}/pbutton onClick{() delHandler(id)}删除/button/div)
}// 父组件
class App extends React.Component {state {list: [{ id: 1, name: 超级好吃的棒棒糖, price: 18.8, info: 开业大酬宾全场8折 },{ id: 2, name: 超级好吃的大鸡腿, price: 34.2, info: 开业大酬宾全场8折 },{ id: 3, name: 超级无敌的冰激凌, price: 14.2, info: 开业大酬宾全场8折 }]}delHandler (id) {this.setState({list: this.state.list.filter(item item.id ! id)})}render() {return ({this.state.list.map(item ListItemkey{item.id}{...item}delHandler{this.delHandler} /)}/)}
}export default AppReact组件进阶
children属性
children属性表示该组件的子节点只要组件内部有子节点props中就有该属性
children可以是
普通文本普通标签元素函数 / 对象JSX
props校验 在ts中这个问题得到了很好的解决不需要第三方依赖的支持。 对于组件来说props是由外部传入的我们其实无法保证组件使用者传入了什么格式的数据如果传入的数据格式不对就有可能会导致组件内部错误有一个点很关键 - 组件的使用者可能报错了也不知道为什么看下面的例子
面对这样的问题如何解决 props校验
实现步骤
安装属性校验包yarn add prop-types导入prop-types 包使用 组件名.propTypes {} 给组件添加校验规则
核心代码
import PropTypes from prop-typesconst List props {const arr props.colorsconst lis arr.map((item, index) li key{index}{item.name}/li)return ul{lis}/ul
}List.propTypes {colors: PropTypes.array
}规则说明
四种常见结构
常见类型array、bool、func、number、object、stringReact元素类型element必填项isRequired特定的结构对象shape({})
// 常见类型
optionalFunc: PropTypes.func,
// 必填 只需要在类型后面串联一个isRequired
requiredFunc: PropTypes.func.isRequired,
// 特定结构的对象
optionalObjectWithShape: PropTypes.shape({color: PropTypes.string,fontSize: PropTypes.number
})官网文档更多阅读https://reactjs.org/docs/typechecking-with-proptypes.html 如何给组件的props提供默认值
对于函数组件来说
直接使用函数参数默认值
function List({pageSize 10}) {return (div此处展示props的默认值{ pageSize }/div)
}// 不传入pageSize属性
List /对于类组件来说
使用类静态属性声明默认值static defaultProps {}
class List extends Component {static defaultProps {pageSize: 10}render() {return (div此处展示props的默认值{this.props.pageSize}/div)}
}
List /通过我们上面使用的第三包工具包里的 defaultProps 也可以给组件的props设置默认值在未传入props的时候生效 组件生命周期
组件的生命周期是指组件从被创建到挂载到页面中运行起来再到组件不用时卸载的过程注意只有类组件才有生命周期类组件 实例化 函数组件 不需要实例化 那么我们自然就有一个疑问既然函数组件没有生命周期那么他怎么实现类似钩子函数的功能 React函数组件本身是没有生命周期函数的因为它们只是接收props并返回JSX的纯函数没有自己的状态和实例。React函数组件可以通过使用Effect Hook来模拟一些生命周期的功能例如在组件挂载、更新或卸载时执行一些副作用操作¹。React函数组件还可以通过使用其他的Hook来实现一些类组件的特性例如useState Hook可以让函数组件拥有自己的状态useRef Hook可以让函数组件访问DOM元素或保存一些可变的值useCallback Hook和useMemo Hook可以让函数组件优化性能等。 如何理解render阶段纯净且不包含副作用
React的render阶段是指React组件根据props和state生成虚拟DOM的过程。在这个过程中React会调用组件的render方法或函数组件本身返回一个React元素或null。React的render阶段是纯净的也就是说它不会对组件的props和state进行任何修改也不会对外部环境产生任何影响。这样可以保证render阶段的可预测性和可测试性以及避免不必要的渲染和性能损耗。React的render阶段是不包含副作用的也就是说它不会对真实的DOM进行任何操作也不会触发任何生命周期方法或钩子函数。这样可以保证render阶段的纯粹性和高效性以及避免不必要的副作用和错误。 例如发送一个请求是一种副作用操作因为它会对外部环境产生影响例如改变服务器的状态或者获取数据。因此发送一个请求不应该在render阶段进行而应该在其他阶段进行例如在生命周期方法或钩子函数中。 React的render阶段只负责生成虚拟DOM而不负责渲染到真实的DOM上。这一步由React的commit阶段完成它会根据虚拟DOM和真实DOM的差异进行最小化的更新。在commit阶段React会执行一些副作用操作例如调用生命周期方法或钩子函数插入或删除DOM节点添加或移除事件监听器等。
接下来我们来看一下类组件的三个重要的生命阶段
挂载更新卸载
挂载阶段 注意
constructer现在用的不是很多了例如初始化state直接写就行不用写在constructer里面。在render里面不要使用setState因为setState会导致重新渲染从而再次执行render函数相当于进入了无限循环。
例如
更新阶段
也就是每次更新都会执行的钩子函数 注意
componentDidUpdate不能直接调用setState因为setState会造成更新从而再次调用render函数这样就会造成无限循环。
卸载阶段 注意react类组件中的生命周期函数可以使用箭头函数表示例如
// 导入React和Component
import React, { Component } from react;// 定义一个计数器组件继承自Component
class Counter extends Component {// 定义一个构造函数用来初始化state和propsconstructor(props) {// 调用super方法传入propssuper(props);// 初始化state设置count为0this.state {count: 0,};}// 定义一个生命周期函数用来在组件挂载时执行一些操作// 使用箭头函数不需要使用bind方法或者闭包来绑定thiscomponentDidMount () {// 在控制台输出一条信息表示组件已经挂载console.log(The component is mounted.);};// 定义一个生命周期函数用来在组件更新时执行一些操作// 使用箭头函数不需要使用bind方法或者闭包来绑定thiscomponentDidUpdate () {// 在控制台输出一条信息表示组件已经更新以及当前的count值console.log(The component is updated. The count is this.state.count);};// 定义一个生命周期函数用来在组件卸载时执行一些操作// 使用箭头函数不需要使用bind方法或者闭包来绑定thiscomponentWillUnmount () {// 在控制台输出一条信息表示组件已经卸载console.log(The component is unmounted.);};// 定义一个方法用来增加count的值// 使用箭头函数不需要使用bind方法或者闭包来绑定thisincrement () {// 使用setState方法将count的值加一this.setState((prevState) ({count: prevState.count 1,}));};// 定义一个方法用来减少count的值// 使用箭头函数不需要使用bind方法或者闭包来绑定thisdecrement () {// 使用setState方法将count的值减一this.setState((prevState) ({count: prevState.count - 1,}));};// 定义一个渲染方法用来返回组件的视图render() {// 返回一个div元素包含一个h1元素和一个button元素return (divh1Counter: {this.state.count}/h1button onClick{this.increment}/buttonbutton onClick{this.decrement}-/button/div);}
}// 导出Counter组件
export default Counter;
还要注意生命周期函数是同步的只有在执行完了之后才会进入下一个生命周期执行下一个生命周期钩子。