react学习笔记,ReactDOM,react-router-dom
react 学习
1. 下载与安装
下载
npm install -g create-react-app
安装
npx create-react-app xxx 推荐
npm init react-app xxx
yarn create react-app xxx
2. 创建 react 元素 indexjs 文件
import React from "react";
import ReactDOM from "react-dom";
// 1创建react元素const title = React.createElement("h1", null, "1111111hello word");
ReactDOM.render(title, document.getElementById("root"));// 2创建react元素
import React from "react";
import ReactDOM from "react-dom";const title = <h1>helloword</h1>;ReactDOM.render(title, document.getElementById("root"));
3. 类名
class => className
const title =
helloword 1111111111
4. 组件
import React from "react";
import ReactDOM from "react-dom";function Hello() {return <div>111111111111111zujian1</div>;
}const Hello = () => <div>188888888811zujian1</div>;ReactDOM.render(<Hello></Hello>, document.getElementById("root"));
### 组件拆分
index.js
import Hello from './components/Hello'ReactDOM.render(<Hello></Hello>, document.getElementById('root'))
Hello.js
import React from "react";class Hello extends React.Component {render() {return (<div>hello组件</div>)}
}export default Hello
5. 继承
// 继承import React from "react";import ReactDOM from "react-dom";class Hello extends React.Component {render() {return (<div>111111111class继承</div>)}}ReactDOM.render(<Hello></Hello>, document.getElementById('root'))//组件式
//1 Hello.jsimport React from "react";class Hello extends React.Component {render() {return (<div>hello组件</div>)}
}export default Hello//2 index.js
import Hello from './components/Hello'ReactDOM.render(<Hello></Hello>, document.getElementById('root'))
6. 点击事件
class Hello extends React.Component {handOlick() {console.log("点击触发");}render() {return <button onClick={this.handOlick}>点击</button>;}
}ReactDOM.render(<Hello></Hello>, document.getElementById("root"));
function Hello() {function handclick() {console.log("点击触发;1");}return <button onClick={handclick}>点击</button>;
}ReactDOM.render(<Hello></Hello>, document.getElementById("root"));
7. 阻止默认事件
class Hello extends React.Component {handclick(e) {e.preventDefault(); // 阻止默认跳转 阻止a标签的默认跳转console.log("点击触发了");}render() {return (<a href="http://www.baidu.com" onClick={this.handclick}>跳转</a>);}
}ReactDOM.render(<Hello></Hello>, document.getElementById("root"));
8. state
class Hello extends React.Component {// constructor() {// super()// this.state = {// num: 0// }// }// 简化state = {num: 1,};render() {return <div>state使用 {this.state.num}</div>;}
}ReactDOM.render(<Hello></Hello>, document.getElementById("root"));
9. setState
class Hello extends React.Component {// 简化state = {num: 1,};render() {return (<div><div>state使用 {this.state.num}</div><buttononClick={() => {this.setState({ num: this.state.num + 1 });}}>+1</button></div>);}
}ReactDOM.render(<Hello></Hello>, document.getElementById("root"));
箭头函数;
class Hello extends React.Component {// 简化state = {num: 1,};onIncrement = () => {this.setState({ num: this.state.num + 1 });};render() {return (<div><div>state使用222 {this.state.num}</div>{/* 2强制改变this */}<button onClick={this.onIncrement}>+1</button></div>);}
}
10 改变 this 指向
// p31 强制改变this 用consturctorclass Hello extends React.Component {// 简化// state = {// num: 1// }constructor() {super();this.state = {num: 1,};this.onIncrement = this.onIncrement.bind(this);}onIncrement() {this.setState({ num: this.state.num + 1 });}render() {return (<div><div>state使用222 {this.state.num}</div>{/* 2强制改变this */}<button onClick={this.onIncrement}>+1</button></div>);}
}ReactDOM.render(<Hello></Hello>, document.getElementById("root"));
11. 表单处理
受控组件
// 受控组件class Hello extends React.Component {state = {txt: "",content: "",};handlerChange = (e) => {this.setState({txt: e.target.value,});console.log(e.target.value);console.log(this.state.txt);};handlarComtent = (e) => {this.setState({context: e.target.value,});};render() {return (<div><inputtype="text"value={this.state.txt}onChange={this.handlerChange}></input><textareavalue={this.state.content}onChange={this.handlarComtent}></textarea></div>);}
}
多表单元素优化
// 多表单元素的优化class Hello extends React.Component {state = {txt: "",content: "",};handlerForm = (e) => {const target = e.target;const value = target.type === "checkbox" ? target.cheched : target.value;const name = target.name;this.setState({[name]: value,});console.log(value);console.log(name);console.log(e.target.value);console.log(this.state.txt);};render() {return (<div><inputtype="text"value={this.state.txt}onChange={this.handlerForm}></input><textareavalue={this.state.content}onChange={this.handlerForm}></textarea></div>);}
}ReactDOM.render(<Hello></Hello>, document.getElementById("root"));
非受控组件
非受控组件;class Hello extends React.Component {constructor() {super();this.txtRef = React.createRef();}getTxt = () => {console.log("文本框" + this.txtRef.current.value);};render() {return (<div><input type="text" ref={this.txtRef}></input><button onClick={this.getTxt}>获取文本框值</button></div>);}
}ReactDOM.render(<Hello></Hello>, document.getElementById("root"));
12. 组件基础案例
class Hello extends React.Component {state = {txt1: "",txt2: "",list: [{id: "1",name: "jack",content: "111111",},{id: "2",name: "jack666666",content: "222222",},{id: "3",name: "jack999999965666",content: "333333",},],};btn = () => {console.log(111);};//优化renderList() {return this.state.list.length === 0 ? (<div>暂无评论</div>) : (<ul>{this.state.list.map((item) => {return (<li key={item.id}>评论人{item.name} <br></br> 内容{item.content}</li>);})}</ul>);}handleForm = (e) => {const { name, value } = e.target;this.setState({[name]: value,});};add = () => {const { list, txt1, txt2 } = this.state;// 去除首尾空格 如果空 就提示 下面代码不执行if (txt1.trim() === "" || txt2.trim() === "") {console.log("请输入name或者content");return;}const newList = [{id: new Date().getTime(),name: txt1,content: txt2,},...list,];console.log(newList);// 更新state状态this.setState({list: newList,});// 清空文本框this.setState({txt1: "",txt2: "",});};render() {const { txt1, txt2 } = this.state;return (<div><inputtype="text"onChange={this.handleForm}value={txt1}placeholder="请输入评论人"name="txt1"></input><textareaonChange={this.handleForm}value={txt2}name="txt2"></textarea><button onClick={this.add}>发表评论</button>{/* 没优化版本 */}{/* 如果list没有数据 就是暂无评论 否则 就循环渲染 */}{/* {this.state.list.length === 0 ? (<div>暂无评论</div>) : (<ul>{this.state.list.map(item => {return <li key={item.id}>评论人{item.name} <br></br> 内容{item.content}</li>})}</ul>)} */}{/* 优化版本 */}{this.renderList()}</div>);}
}ReactDOM.render(<Hello></Hello>, document.getElementById("root"));
13. 组件通讯
props
// 2 props 接收
const Hello = (props) => {console.log(props);return (<div><span>{props.name} {props.age}</span></div>);
};// 1 传递
ReactDOM.render(<Hello name="jsck" age={22}></Hello>,document.getElementById("root")
);
// 2 props 接收
class Hello extends React.Component {render() {console.log(this.props);return (<div>{this.props.name}{this.props.age}</div>);}
}// // 1 传递
ReactDOM.render(<Hello name="jsck" age={22}></Hello>,document.getElementById("root")
);
// 推荐使用 props作为consttuctor参数
// 2 props 接收
class Hello extends React.Component {constructor(props) {super(props);console.log(props);}render() {console.log(this.props);return (<div>{this.props.name}{this.props.age}</div>);}
}// 1 传递
ReactDOM.render(<Hello name="jsck" age={22}></Hello>,document.getElementById("root")
);
父传子
// 父传子
// 父组件
class Parent extends React.Component {state = {name: "wang1",};render() {return (<div>我是父组件<Child name={this.state.name}></Child></div>);}
}// 子组件
const Child = (props) => {return (<div><p>我是子组件 接收父组件数据为:{props.name}</p></div>);
};ReactDOM.render(<Parent name="jsck" age={22}></Parent>,document.getElementById("root")
);
子传父
// 父组件
class Parent extends React.Component {state = {parentMsg: "",};getChildMsg = (data) => {console.log("接收子组件数据wei:" + data);this.setState({parentMsg: data,});};render() {return (<div>父组件 aaa {this.state.parentMsg}<hr></hr>子组件:<Child getMsg={this.getChildMsg}></Child></div>);}
}class Child extends React.Component {state = {msg: "子组件数据 ",};handClick = () => {this.props.getMsg(this.state.msg);};render() {return (<div>我是子组件<button onClick={this.handClick}>点我给父组件传递数据</button></div>);}
}ReactDOM.render(<Parent name="jsck" age={22}></Parent>,document.getElementById("root")
);
兄弟相传
//思路: 将共享状态提升到最近的公共父组件中 由公共父组件管理这个状态//子组件通过props接收
class Parent extends React.Component {// 提供共享状态state = {num: 0}// 提供修改状态方法changeNum = () => {this.setState({num: this.state.num + 1})}render() {return (<div><Child1 num={this.state.num}></Child1><Child2 changeNum={this.changeNum}></Child2></div>)}
}const Child1 = (props) => {return <h1>计数器:{props.num}</h1>
}const Child2 = (props) => {return <button onClick={props.changeNum}>+1</button>
}ReactDOM.render(<Parent name="jsck" age={22}></Parent>, document.getElementById('root'))
拆分组件式
index.js :
import Child1 from "./components/Child1";
import Child2 from "./components/Child2";class Parent extends React.Component {// 提供共享状态state = {num: 0}// 提供修改状态方法changeNum = () => {this.setState({num: this.state.num + 1})}render() {return (<div><Child1 num={this.state.num}></Child1><Child2 changeNum={this.changeNum}></Child2></div>)}
}ReactDOM.render(<Parent></Parent>, document.getElementById('root'))
Child1.js :
import React from "react";class Child1 extends React.Component {constructor(props) {super(props);console.log(props);}render() {return (<div>计数器:{this.props.num}</div>)}
}export default Child1
Child2.js :
import React from "react";class Child2 extends React.Component {constructor(props) {super(props);console.log(props);}render() {return (<button onClick={this.props.changeNum}>+1</button>)}
}export default Child2
14 Context 跨组件传递数据
类似于vue的 vuex
步骤
// 组件 app node subNode Child 从app组件传到Child组件
// 用props 一层一层传 繁琐用React.createContext() 简便
1. 调用 React.createContext() 创建 Provider(提供数据) 和 Consumer(消费数据) 两个组件2. 使用Provider组件作为父节点<Provide><div className="App"><Child1></Child1></div></Provide>3. 设置value属性 表示要传递的值<Provider value="pink"></Provider>4. 调用Consumer组件接收数据<Consumer Consumer >通过回调函数的参数拿到传递过来的value的值{data => <span >data参数表示接收到的数据----{data}</span >}</Consumer
代码
const { Provider, Consumer } = React.createContext()
class App extends React.Component {render() {// 用Provide包裹内容return (<Provider value="pink"><div className="App"><Node></Node></div></Provider>)}
}const Node = props => {return (<div><SubNode></SubNode></div>)
}
const SubNode = props => {return (<div><Child1></Child1></div>)
}
const Child1 = props => {return (<div><div> 我是Child1组件</div><Consumer>{data => <span>我是接收过来的值---{data}</span>}</Consumer></div>)
}
ReactDOM.render(<App></App>, document.getElementById('root'))
15. props深入
children属性 props.children
const Parent = props => {console.log(props)// 输出为 : children : "我是子组件子节点"props.children()return (<div>{props.children}</div>)
}// const Text = () => {
// return <h1>我是组件</h1>
// }//<Parent></Parent>里面可以是文字 标签 组件 react元素 等
// ReactDOM.render(<Parent>我是子组件子节点</Parent>, document.getElementById('root'))
// ReactDOM.render(<Parent><h1>h1标签</h1></Parent>, document.getElementById('root'))
// ReactDOM.render(<Parent><Text></Text></Parent>, document.getElementById('root'))
ReactDOM.render(<Parent>{() => console.log('这是一个函数子节点')}</Parent>, document.getElementById('root'))
props校验 约束规则 默认值
//校验
// 允许在创建组件的时候 指定props的类型 格式等
// 安装包 yarn add prop-types npm i props-types
// 导入包 import PropTypes from "prop-types";
// 使用组件名.propTypes{}
// 检验规则通过PropTypes对象来指定import PropTypes from "prop-types";const App = props => {const arr = props.colorsconst lis = arr.map((item, index) => <li key={index}>{item.name}</li>)return <ul>{lis}</ul>
}App.propTypes = {colors: PropTypes.array
}ReactDOM.render(<App colors={[{ name: "123" }]}></App>, document.getElementById('root'))// 约束规则
//常见类型 array bool func number object string
//React元素 element
//必填项 isRequired
//特定结构的对象 .shape({})import PropTypes from "prop-types";const App = props => {return (<div><h1>props检验</h1></div>)
}App.propTypes = {num: PropTypes.number,//数字fn: PropTypes.func.isRequired, // 函数 isRequired必填项tag: PropTypes.element, //react元素filter: PropTypes.shape({ //filter类型 对象({})area: PropTypes.string,price: PropTypes.number})
}ReactDOM.render(<App fn={() => { }}></App>, document.getElementById('root'))// props 默认值 未传入时生效// 场景 分页组件const App = props => {console.log(props)return (<div>prpos默认值</div>)
}// 以传入的值为主
App.defaultProps = {pagSize: 10
}// pagSize 10
ReactDOM.render(<App></App>, document.getElementById('root'))// pagSize 20
ReactDOM.render(<App pagSize={20}></App>, document.getElementById('root'))
16. 生命周期
创建 | ||
钩子函数 | 触发时机 | 作用 |
constructor | 创建组件时 | 1.初始化state 2.为事件处理绑定this |
render | 每次组件渲染时都会触发 | 渲染UI 注意不能调用setState() |
componentDidMount | 组件挂载后(完成DOM渲染) | 1.发送网络请求 2.DOM操作 |
更新 | ||
钩子函数 | 触发时机 | 作用 |
render | 每次组件渲染都会触发 | 渲染UI 挂载阶段是同一个render |
componentDidUpdate | 组件更新后 | 1. 发送网络请求 1. DOM操作 注意 如果要setState() 必须放在一个 if 条件中 |
卸载 | ||
钩子函数 | 触发时机 | 作用 |
componentWillUnmount | 组件卸载(从页面中消失) | 执行清理工作 (清除定时器) |
创建 | ||
钩子函数 | 触发时机 | 作用 |
constructor | 创建组件时 | 1.初始化state 2.为事件处理绑定this |
render | 每次组件渲染时都会触发 | 渲染UI 注意不能调用setState() |
componentDidMount | 组件挂载后(完成DOM渲染) | 1.发送网络请求 2.DOM操作 |
// 创建时
// 执行顺序 constructor() => render() => componentDidMountclass App extends React.Component {constructor(props) {super(props)// 初始化statethis.state = {num: 0}console.log('1生命周期函数---constructor')}componentDidMount() {// 获取DOM操作// console.log(document.getElementById('header'))// 发送ajax请求 axios.get("")console.log('2生命周期函数---componentDidMount')}render() {// 错误演示 不要再render中调用setState()// this.setState({// num: 1// })console.log('3生命周期函数---render')return (<div><h1 id="header">header</h1></div>)}
}ReactDOM.render(<App ></App>, document.getElementById('root'))
```js
//更新时
//执行时机 1.setState() 2. forceUpdate() 3. 组件接收的props//执行顺序 render() => componentDidUpdate()
更新 | ||
钩子函数 | 触发时机 | 作用 |
render | 每次组件渲染都会触发 | 渲染UI 挂载阶段是同一个render |
componentDidUpdate | 组件更新后 | 1. 发送网络请求 1. DOM操作 注意 如果要setState() 必须放在一个 if 条件中 |
class App extends React.Component {
constructor(props) {
console.log(‘constructor’)
super(props)
this.state = {
num: 0
}
}
handerNum = () => {
this.setState({
num: this.state.num + 1
})
// 演示强制更新
// this.forceUpdate()
}
render() {
console.log(‘render’)
return (
+1
)
}
}
class Numb extends React.Component {
render() {
console.log(‘子组件------render’)
return (
统计次数:{this.props.num}
)
}
// 如果调用setState 必须放在if中
componentDidUpdate(prevProps) {
console.log(‘子组件生命周期 componentDidUpdate’)
// 获取DOM
console.log(document.getElementById(‘title’))
// this.setState({}) 报错 会递归更新
console.log('上次props', prevProps)console.log('当前props', this.props)// // 正确写法 比较更新前后的props是否相同 来决定重新渲染条件// if (prevProps.num !== this.props.num) {// this.setState({})// // 发送ajax// }
}
}
ReactDOM.render(, document.getElementById(‘root’))
```
卸载 | ||
钩子函数 | 触发时机 | 作用 |
componentWillUnmount | 组件卸载(从页面中消失) | 执行清理工作 (清除定时器) |
// 卸载时
// 执行时机 组件从页面中消失class App extends React.Component {constructor(props) {console.log('constructor')super(props)this.state = {num: 0}}handerNum = () => {this.setState({num: this.state.num + 1})// 演示强制更新// this.forceUpdate()}render() {console.log('render')return (<div>{this.state.num > 3 ? <p>豆豆打死了</p> : <Numb num={this.state.num}></Numb>}<button onClick={this.handerNum}>+1</button></div>)}
}class Numb extends React.Component {render() {console.log('子组件------render')return (<div><h1 id="title">统计次数:{this.props.num}</h1></div>)}componentDidMount() {// 开启定时器this.inter = setInterval(() => {console.log('定时器正在执行')}, 500);}componentWillUnmount() {console.log('生命周期 componentWillUnmount')// 清除定时器clearInterval(this.inter)}}ReactDOM.render(<App ></App>, document.getElementById('root'))
17. render props 和 高阶组件
react组件复用
复用相似功能
复用什么? 1. state 2. 操作state的方法
两种方式 1. render props模式 2.高阶组件(HOC)
思路:将要复用的state和操作state的方法封装到一个组件中
// 创建Mouse组件
// class Mouse extends React.Component {
// state = {
// x: 0,
// y: 0
// }// // 鼠标移动事件
// handleMouseMove = e => {
// this.setState({
// x: e.clientX,
// y: e.clientY,
// })
// }
// // 监听鼠标移动事件
// componentDidMount() {
// window.addEventListener('mousemove', this.handleMouseMove)
// }// render() {
// // return null;
// // console.log(this.props.render)
// return this.props.renderss(this.state)
// }
// }// class App extends React.Component {
// render() {
// return (
// <div>
// 111
// <Mouse renderss={(mouse) => {
// return <p>鼠标位置:x{mouse.x},y:{mouse.y}</p>
// }}></Mouse>// <Mouse renderss={mouse => {
// return <p style={{ position: 'absolute', top: mouse.y, left: mouse.x }}>看我!!!</p>
// }}></Mouse>
// </div>
// )
// }
// }// ReactDOM.render(<App ></App>, document.getElementById('root'))
render props模式 children代替render属性
// render props模式 childern代替render属性
class Mouse extends React.Component {state = {x: 0,y: 0}// 鼠标移动事件handleMouseMove = e => {this.setState({x: e.clientX,y: e.clientY,})}// 监听鼠标移动事件componentDidMount() {window.addEventListener('mousemove', this.handleMouseMove)}render() {// return null;// console.log(this.props.render)return this.props.children(this.state)}
}class App extends React.Component {render() {return (<div>111<Mouse>{mouse => {return <p>鼠标位置:x{mouse.x},y:{mouse.y}</p>}}</Mouse><Mouse>{mouse => {return <p style={{ position: 'absolute', top: mouse.y, left: mouse.x }}>看我!!!</p>}}</Mouse></div>)}
}ReactDOM.render(<App ></App>, document.getElementById('root'))
render props 代码优化 添加props校验
import PropTypes from 'prop-types'
class Mouse extends React.Component {state = {x: 0,y: 0}handleMove = (e) => {this.setState({x: e.clientX,y: e.clientY})}componentDidMount() {window.addEventListener("mousemove", this.handleMove)}// 删除监听// 在组建卸载时移除事件绑定componentWillUnmount() {window.removeEventListener("mousemove", this.handleMove())}render() {return this.props.children(this.state)}
}// 添加props校验
Mouse.propTypes = {children: PropTypes.func.isRequired
}class App extends React.Component {render() {return (<div>1111<Mouse>{mouse => {return <p>鼠标位置:x{mouse.x},y:{mouse.y}</p>}}</Mouse><Mouse>{mouse => {return <p style={{ position: 'absolute', top: mouse.y, left: mouse.x }}>看我!!!</p>}}</Mouse></div>)}
}ReactDOM.render(<App ></App>, document.getElementById('root'))
高阶组件
使用步骤
1.创建一个函数 名称以with开头
2.指定函数参数 参数以大写字母开头 (作为要渲染的组件)
3.在函数内部创建一个类组件,提供复用的状态逻辑代码 并返回
4.在该组件中 渲染参数组件 同时将状态通过prop传递给参数组件
\5. 调用该高阶组件 传入要增强的组件 通过返回值拿到增强后的组件 渲染页面
function withMouse (WrapperComponent){
class Mouse extends React.Component{}
return Mouse
}
Mouse组件的render方法中
return <WrapperComponent {…this.state}>
// // 创建高阶组件
// function withMouse(WrappenComponent) {
// // 该组件提供付永德状态逻辑
// class Mouse extends React.Component {
// // 鼠标状态
// state = {
// x: 0,
// y: 0
// }
// handleMove = e => {
// this.setState({
// x: e.clientX,
// y: e.clientY
// })
// }// // 控制鼠标状态的逻辑
// componentDidMount() {
// window.addEventListener("mousemove", this.handleMove)
// }// componentWillUnmount() {
// window.removeEventListener("mousemove", this.handleMove)
// }// render() {
// return <WrappenComponent {...this.state}></WrappenComponent>
// }// }// return Mouse// }// const Position = props => {// return <p>
// 鼠标位置:x{props.x} y: {props.y}
// </p>
// }// const ThisPosition = (props) => {
// return <p style={{ position: 'absolute', top: props.y, left: props.x }}>看我!!!</p>
// }// // 获取增强后的组件
// const MousePosition = withMouse(Position)
// const ThisMousePosition = withMouse(ThisPosition)// class App extends React.Component {
// render() {
// return (
// <div>
// 高阶组件// {/* 渲染增强后逇组件 */}// <MousePosition></MousePosition>
// <ThisMousePosition />
// </div>
// )
// }
// }// ReactDOM.render(<App />, document.getElementById('root'))
displayName属性
// 设置displayName
// 不设置 React调试器 都显示Mouse
// 便于调试区分// 创建高阶组件
// function withMouse(WrappenComponent) {
// // 该组件提供付永德状态逻辑
// class Mouse extends React.Component {
// // 鼠标状态
// state = {
// x: 0,
// y: 0
// }
// handleMove = e => {
// this.setState({
// x: e.clientX,
// y: e.clientY
// })
// }// // 控制鼠标状态的逻辑
// componentDidMount() {
// window.addEventListener("mousemove", this.handleMove)
// }// componentWillUnmount() {
// window.removeEventListener("mousemove", this.handleMove)
// }// render() {
// return <WrappenComponent {...this.state}></WrappenComponent>
// }// }// // // 设置displayName
// Mouse.displayName = `WithMouse${getDisplayName(WrappenComponent)}`// function getDisplayName(WrappenComponent) {
// return WrappenComponent.displayName || WrappenComponent.name || 'Component'
// }// return Mouse// }// const Position = props => {// return <p>
// 鼠标位置:x{props.x} y: {props.y}
// </p>
// }// const ThisPosition = (props) => {
// return <p style={{ position: 'absolute', top: props.y, left: props.x }}>看我!!!</p>
// }// // 获取增强后的组件
// const MousePosition = withMouse(Position)
// const ThisMousePosition = withMouse(ThisPosition)// class App extends React.Component {
// render() {
// return (
// <div>
// 高阶组件// {/* 渲染增强后逇组件 */}// <MousePosition></MousePosition>
// <ThisMousePosition />
// </div>
// )
// }
// }// ReactDOM.render(<App />, document.getElementById('root'))
高阶组件传递props
// 问题 props丢失
// 原因 高阶组件没有往下传递props
// 解决方法 渲染WrappedComponen时 讲state和this.props一起传递给组件function withMouse(WrappenComponent) {// 该组件提供付永德状态逻辑class Mouse extends React.Component {// 鼠标状态state = {x: 0,y: 0}handleMove = e => {this.setState({x: e.clientX,y: e.clientY})}// 控制鼠标状态的逻辑componentDidMount() {window.addEventListener("mousemove", this.handleMove)}componentWillUnmount() {window.removeEventListener("mousemove", this.handleMove)}render() {console.log(this.props, 'Mouse组件')// return <WrappenComponent {...this.state}></WrappenComponent>return <WrappenComponent {...this.state} {...this.props}></WrappenComponent>}}// // 设置displayNameMouse.displayName = `WithMouse${getDisplayName(WrappenComponent)}`function getDisplayName(WrappenComponent) {return WrappenComponent.displayName || WrappenComponent.name || 'Component'}return Mouse}const Position = props => {console.log(props, 'Position组件')// 拿不到传过来的a// 问题 props丢失// 原因 高阶组件没有往下传递props// 解决方法 渲染WrappedComponen时 将state和this.props一起传递给组件// return <WrappenComponent {...this.state} {...this.props}></WrappenComponent>return <p>鼠标位置:x{props.x} y: {props.y}</p>
}// 获取增强后的组件
const MousePosition = withMouse(Position)class App extends React.Component {render() {return (<div>高阶组件{/* 渲染增强后逇组件 */}<MousePosition a="1"></MousePosition></div>)}
}ReactDOM.render(<App />, document.getElementById('root'))
18. react原理揭秘
setState的说明
// setState的说明
// setstate更新数据是异步的// 推荐使用setState((state,props)=>{}) 参数state:表示最新的state props表示最新的propsclass App extends React.Component {state = {num: 0}handleClick = () => {// 更新state 是异步的// this.setState({// num: this.state.num + 1// })// console.log(this.state.num) //0// this.setState({// num: this.state.num + 1// })// 后面的setState不要依赖于前面的setState// this.setState调用两次 render只调用一次// 推荐语法: 也是异步的// 调用一次 参数state还是原来的statethis.setState((state, props) => {return {num: state.num + 1}})console.log(this.state.num) //0// 调用第二次 这次的state参数 是上次调用之后的statethis.setState((state, props) => {return {num: state.num + 1}})console.log(this.state.num)//2}render() {console.log('render')return (<div>计数器 {this.state.num}<button onClick={this.handleClick}>+1</button></div>)}
}ReactDOM.render(<App />, document.getElementById('root'))
setState 第二个参数
// setState第二个参数 propsclass App extends React.Component {state = {num: 0}handleClick = () => {this.setState((state, props) => {return {num: state.num + 1}}, () => { //状态更新后并且更新渲染后,立即执行 可以操作DOMconsole.log('状态更新', this.state.num) //1console.log(document.getElementById('title'))document.title = '更新标题'})console.log(this.state.num) //0}render() {console.log('render')return (<div>计数器 {this.state.num}<h1 id="title">111111111111</h1><button onClick={this.handleClick}>+1</button></div>)}
}ReactDOM.render(<App />, document.getElementById('root'))
19. JSX 语法转化过程
// // jsx是createElement的语法糖 jsx被@bable/preset-react编译为createElement// // jsx
// // const element = <h1 className="eleme">helo jsx</h1>
// // console.log(element)// // createElement
// const element = React.createElement(
// 'h1',
// {
// className: "elem"
// },
// "hello jsx"
// )// console.log(element)// ReactDOM.render(element, document.getElementById('root'))
2. 组件性能优化
// 1) 减轻state
// 只存储跟组件渲染相关的数据 比如列表数据 loading等
// 注意 不用做渲染的数据不要放在state中 比如定时器 直接放在this当中 直接this.setinter=setInterval(() => { }, 500);// 2)避免不必要的重新渲染
// 组件更新机制 父组件更新 会引起子组件也会更新
// 问题 子组件没有任何变化时也会重新渲染
// 解决方法:使用钩子函数 shouldComponentUpdate(nextProps,nextState)
// 作用 通过返回值决定该组件是否重新渲染 返回true表示重新渲染 false表示不能
// 触发时机 更新阶段的钩子函数 组件重新渲染 前 执行 (shouldComponentUpdate => render)
// class App extends React.Component {// // true 渲染页面 false 不渲染
// shouldComponentUpdate(nextProps, nextState) {
// console.log(nextProps) //最新的props
// console.log(nextState) //最新的state
// console.log(this.state) // 更新前的状态
// return true
// }
// state = {
// num: 0
// }// handleClick = () => {
// this.setState((state) => {
// return {
// num: state.num + 1
// }
// })
// }// render() {
// return (<div>{this.state.num}// <button onClick={this.handleClick}>+1</button>
// </div>)
// }
// }// ReactDOM.render(<App />, document.getElementById('root'))
案例
// 案例
class App extends React.Component {state = {num: 0}handleClick = () => {this.setState((state) => {return {num: Math.floor(Math.random() * 3) // 0-3 随机数}})}// 因为两次生成的随机数相同 就不必要重新渲染shouldComponentUpdate(nextProps, nextState) {// if (nextState.num === this.state.num) {// return false// } else {// return true// }return nextState.num !== this.state.num}render() {console.log('render')return (<div>{this.state.num}<button onClick={this.handleClick}>随机数</button></div>)}
}ReactDOM.render(<App />, document.getElementById('root'))
通过props避免不必要的重新渲染
//利用props 避免不必要的重新渲染class App extends React.Component {state = {num: 0}handleClick = () => {this.setState((state) => {return {num: Math.floor(Math.random() * 3) // 0-3 随机数}})}render() {return (<div><NumberBox number={this.state.num}></NumberBox><button onClick={this.handleClick}>随机数</button></div>)}
}class NumberBox extends React.Component {shouldComponentUpdate(nextProps) {console.log(nextProps)console.log(this.props)return nextProps.number !== this.props.number}render() {console.log('render')return (<div>随机数{this.props.number}</div>)}
}ReactDOM.render(<App />, document.getElementById('root'))
纯组件 (内部自动实现shouldComponentUpdate)
// 纯组件
// 纯组件 PureComponent 与React.Component功能相似
// 区别 PureComponent内部自动实现了 shouldComponentUpdate 不需要手动比较
// 原理 纯组件内部通过分别对比前后两次props和state的值 来决定是否重新渲染组件// import { PureComponent } from "react";
// class App extends PureComponent { // 可以写为class App extends React.PureComponent { 就不需要引入
// state = {
// num: 0
// }// handleClick = () => {
// this.setState((state, props) => {
// return {
// num: Math.floor(Math.random() * 3)
// }
// })
// }// render() {
// console.log('render')
// return (
// <div>
// 随机数 {this.state.num}
// <button onClick={this.handleClick}>随机</button>
// </div>
// )
// }
// }// ReactDOM.render(<App />, document.getElementById('root'))
import { PureComponent } from "react";class App extends React.Component {state = {num: 0}handleClick = () => {this.setState((state, props) => {return {num: Math.floor(Math.random() * 3)}})}render() {return (<div><NumberBox number={this.state.num}></NumberBox><button onClick={this.handleClick}>随机</button></div>)}
}class NumberBox extends PureComponent {render() {console.log('render')return <div>suijiahsu1 {this.props.number}</div>}
}ReactDOM.render(<App />, document.getElementById('root'))
21 好客租房 项目
### 1. 项目搭建
npx create-react-app hkzf
yarn start
2. 组件库 antd-mobile
1 打开文档 http://ant-design-mobile.antgroup.com/zh/components/button
3. 配置基础路由
yarn add react-router-dom
import { BrowserRouter as Router, Route, Routes, Link } from ‘react-router-dom’
4. 外观和样式调整
index.css
html,
body {height: 100%;font-family: "Microsoft YaHei";color: #333;background-color: #fff;
}
* {box-sizing: border-box;
}
5. 两种布局页面
- 有tabber页面
- 无tabbar页面
6. 嵌套路由
v6版本
render() {return <div className="home">shouye<Button size='large' color='primary'>xxxxxxxxxxxxx</Button>{/* <Route path="/news" element={<News />}></Route> */}<Link to="/home/news">news</Link><Routes>{/* <Route path="/*" ></Route> */}<Route path="/" element={<News />}></Route><Route path="/a" element={<A />}></Route></Routes></div>}
v6之前版本:
render() {return (<div style={{ background: "red", padding: "10px" }} className="home">shouye<Button size='large' color='primary'>xxxxxxxxxxxxx</Button><Route path="/home/news" component={News}></Route></div>)}
}
附加
1.关闭 eslint
package.json 中修改
"eslintConfig": {"extends": ["react-app","react-app/jest"],"rules": {"no-undef": "off","no-restricted-globals": "off","no-unused-vars": "off"}},