react项目详解
- 类组件
- 1、事件绑定
- 2、setState
- 3、组件通讯
- 4、插槽
- 5、样式隔离
- 6、ref 的使用 (相当于函数式组件中的 useRef)
- 函数式组件
- 7、定义变量,修改变量
- 8、useEffect 副作用函数
- 9、useMemo
- 10、useCallback
- 11、useRef (相当于类组件的 createRef)
- 12、高阶组件(相当于vue中的mixins)
类组件
1、事件绑定
javascript">class App extends React.Component {
// 定义事件
handleClick1(val, e) {
// 阻止默认行为
e.preventDefault();
//阻止冒泡
e.stopPropagation();
console.log(val, e);
}
// 定义事件第二种方式
handleClick2(val3, e) {
console.log(val3);
}
render() {
return (
<div className={style.App}>
<button onClick={this.handleClick1.bind(this, "传参数")}>事件绑定</button>
<button onClick={(e) => this.handleClick2(11, e)}>事件绑定第二中方式</button>
</div>
);
}
}
注意:react中阻止冒泡和默认行为和原生是一样的 e.preventDefault(); e.stopPropagation();
2、setState
- setState 是浅合并 (Object.asign), 所以在更新 state 状态时,如果是对象,一定要展开再合并新的数据
javascript">state = {
obj: {
a: 1,
b: 2,
c: 3
}
}
setState({
obj: {
...this.state.obj,
b: 5
}
})
- setState 在修改 list 数组的时候,如果是 React.Component 可是直接在数据的基础上修改,如果是 React.PureComponent ,就只能深拷贝一份数组再进行修改,因为 PureComponent 对比的是地址值
- 多次 setState render只会走一次
- 在类组件中 React.Component 下,setState 走一次, 就会render 一次页面更新,所以尽量使用 React.PureComponent
- setState 是异步的,取值要在第二个参数里面
javascript">setState({
msg: "修改msg的值"
}, () => {
this.state.msg // 取值
})
3、组件通讯
react 中父组件传值给子组件 和 子组件传值给父组件,都是通过 props 来实现的
父组件给子组件传值,父组件给子组件传递属性即可
javascript">// 父组件中
render() {
return <div>
<Son name="父传值给子"></Son>
</div>
}
// 子组件中接值
this.props.name
子组件传值给父组件,父组件给子组件传递方法即可
javascript">// 父组件中
getChildValue(val) {
console.log("父组件拿到子组件的值", val)
}
render() {
return <div>
<Son onGetChildValue={this.getChildValue.bind(this)}></Son>
</div>
}
// 子组件中
handle() {
this.props.onGetChildValue("子组件的值传给父组件") // 子组件拿到父组件传过来的方法直接调用,把值传过去
}
render() {
return <div>
<button onClick={this.handle.bind(this)}>点击给父组件值</button>
</div>
}
子组件接值验证和默认值的定义
javascript">// props 验证值的类型 和 默认值
Son.propTypes = {
name: (props) => {
if(typeof props.name !== "string") {
return new Error("期望一个字符串")
}
return null
}
}
Son.defaultProps = {
name: "默认值"
}
子组件接值验证借助第三方库
javascript">// 1、下载 proptypes
yarn add proptypes
// 2、引用
import propType from "proptypes"
// 子组件接值校验 (使用)
Son.propTypes = {
name: propType.string, // 校验字符串
age: propType.number, // 校验数字
list: propType.array, // 校验数组
obj: propType.object // 校验对象
}
4、插槽
react中的插槽的使用也是 props
javascript">// 1、默认插槽
父组件
render() {
return <div>
<Slot>
<div>before</div> // 需要在子组件展示的内容
</Slot>
</div>
}
子组件
render() {
const { children } = this.props
return <div>
{children}
</div>
}
// 2、具名插槽
父组件
render() {
return <div>
<Slot>
<div data-id="before">before</div> // 父组件传过去两处在子组件需要展示的内容
<div data-id="after">after</div>
</Slot>
</div>
}
子组件占位,展示父组件的内容
render() {
// 子组件从 props 得到 父组件传进来的 自定义属性为 data-id, 然后判断展示在不同的位置
const { children } = this.props
let beforeVNode, afterNode
children.forEach(v => {
if("before" === v.props['data-id']) {
beforeVNode = v
}else{
afterNode = v
}
})
return <div>
{beforeVNode} // 虚拟Dom用插值表达式来解析
Slot
{afterNode}
</div>
}
// 3、作用域插槽 (借助的就是子传值给父组件)
父组件
render() {
return <div>
<Slot customClick={(val) => console.log(val)}></Slot>
</div>
}
子组件
render() {
return <div>
<button onClick={() => this.props.customClick(this.state.msg)}>作用域插槽</button>
</div>
}
5、样式隔离
创建 xxx.module.scss, 在jsx引入
javascript">// 在js中
import style from "./xxx.module.scss"
render() {
return <div className={ style.box }>
111
</div>
}
// css中
.box{
.....
}
6、ref 的使用 (相当于函数式组件中的 useRef)
ref 获取dom
javascript">const div1 = React.createRef() // 定义的 div1 和标签中的 ref 的值是一样的
componentDidMount() {
console.log(div1) // 获取 dom
}
render() {
return <div>
<div ref={div1}>111</div>
</div>
}
ref 获取组价实例
javascript">const divComponent = React.createRef() // 定义的 divComponent 和标签中的 ref 的值是一样的
componentDidMount() {
console.log(divComponent) // 获取组件实例
}
render() {
return <div>
<Son ref={divComponent}></Son>
</div>
}
函数式组件
1、函数式组件没有生命周期
2、函数式组件没有 this
3、函数式组件通过 hook 来完成各种操作
4、函数式组件本身的函数体相当于 render 函数
5、props 在函数的第一个参数接受
7、定义变量,修改变量
javascript">import { useState } from "react"
let [msg, setMsg] = useState('') // 定义msg变量
// 修改msg变量
function changeMsg() {
setMsg('修改msg变量的值')
}
return <div>
{ msg } // 使用变量
<button onClick={() => changeMsg()}>修改变量</button>
</div>
8、useEffect 副作用函数
1、不传第二个参数 = componentDidMount 和 componentDidUpdate
2、第二个参数传空数组 = componentDidMount
3、第二个参数数组里面放某个数据 = watch 监听
4、第一个参数函数体里面返回值是一个方法 = componentDidUnMount
javascript">useEffect(function() {}, [])
9、useMemo
useMemo 类似于 vue 中的计算属性, 第二个参数和 useEffect 的参数用法是一样的
javascript">// 1、定义常量的时候可以用 useMemo 包裹一下,表示只在创建的时候创建一次, 更新的时候就不创建了
const computedObj = useMemo(function() {
return {
name: "张三",
age: 18
}
}, [])
// 2、当做计算属性来使用,依赖写在第二个参数的数组里面
const computedObj = useMemo(() => msg + ":" + text, [msg, text]) // 表示只有 msg 和 text 两个变量改变的时候,useMemo 才会重新运行
10、useCallback
包裹方法使用的hook,第二个参数和 useMemo、useEffect 规则是一样的
11、useRef (相当于类组件的 createRef)
javascript">import { useRef, useEffect } from "react"
const div1 = useRef()
useEffect(() => {
console.log(div1.current) // 获取dom节点
}, [])
return <div>
<div ref={div1}>111</div>
</div>
12、高阶组件(相当于vue中的mixins)
高阶组件就相当于 vue 中的 mixins 混入, 说白了就是往使用高阶组件的组件的 props 里面 放一些公共的属性和方法
在 utils 里面建一个高阶组件 HigherOrderComponent.jsx
javascript">// HigherOrderComponent.jsx
import { useState } from "react"
// 高阶组件的封装
export default function HeightComponent(Component) { // 暴露出去一个方法
return function Heightsa(props) { // 返回一个方法
// 高阶组件封装的属性值(每个使用高阶组件的组件都可以使用)
const [heightVal] = useState("高阶函数封装的属性")
// 高阶组件封装的方法(每个使用高阶组件的组件都可以使用)
const heightMethods = () => {
console.log("高阶函数封装的方法");
}
return <>
// 把传进来的组件加上高阶组件包装之后的 props 返回出去,使用这个高阶组件的组件就有了上面的属性和方法
<Component {...props} heightVal={heightVal} heightMethods={heightMethods}></Component>
</>
}
}
组件的使用者
javascript">import HeightComponent from "../utils/HigherOrderComponent.jsx"
const App = (props) => {
const { heightVal, heightMethods } = props // 拿到了高阶组件的属性和方法了
return <div>
App
</div>
}
export default HeightComponent(App) // 只用高阶组件包装一下再暴露出去就可以了