<div id="app"></div>
<!-- 引入react核心库 -->
<script type="text/javascript" src="../js/react.development.js"></script>
<!-- 引入react-dom,用于支持react操作DOM -->
<script type="text/javascript" src="../js/react-dom.development.js"></script>
<!-- 引入babel,用于将jsx转为js -->
<script type="text/javascript" src="../js/babel.min.js"></script>
<script type="text/babel">
const demo = <span>Hello Word</span>
ReactDOM.render(demo, document.querySelector('#app'))
</script>
{ }
const name = 'Hello Word'
<span style={{color:'red',fontSize:'55px'}}>{name}</span>
className
。<div className="className" id={ID}>
<span style={{color:'red',fontSize:'55px'}}>{name}</span>
</div>
style={{key:value}}
的形式去写。 <span style={{color:'red',fontSize:'55px'}}>{name}</span>
undefined
/null
/Boolean
类型 <h2>{String(aaa)}</h2>
<h2>{bbb + ""}</h2>
<h2>{ccc.toString()}</h2>
表达式:一个表达式会产生一个值,可以放在任何一个需要值的地方
下面这些都是表达式:
(1). a
(2). a+b
(3). demo(1)
(4). arr.map()
(5). function test () {}
语句(代码):
(1).if(){}
(2).for(){}
(3).switch(){case:xxxx}
混入map
表达式
const data = ['Vue', 'React', 'Animation']
// 只能渲染数组
const VDOM = (
<div>
<h1>HEllo REACT</h1>
<h2>React遍历对象与数组</h2>
<ul>
{
data.map((v, index) => {
return <li key={index}>{v}</li>
})
}
</ul>
</div>
)
ReactDOM.render(VDOM, document.querySelector('#test'))
注意点
大写
render
渲染时必须使用标签function MyComponent(){
console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
}
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
注意点
class MyComponent extends React.Component {
render(){
//render是放在哪里的?—— MyComponent的原型对象上,供实例使用。
//this指向?—— MyComponent的实例对象 <=>MyComponent组件实例对象。
console.log('render中的this:',this);
return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
}
}
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
注意点
React默认开启严格模式
this
的指向undefined
可以在构造函数中利用bind
,apply
,call
改变this的指向注意点,setState是异步的
,可以传递对象或者函数,且每次调用 render函数
都会重新渲染)// state使用
class Wether extends React.Component {
// 1. 继承React组件实例上添加的属性
// 2. 构造器的this指向构造函数的实例对象
// 3. render() 函数中的this也指向构造函数的实例对象
constructor(props) {
// super继承父组件属性
super(props)
this.state = { isHost: false, wind: '炎热' }
// 改变this的指向
this.demo = this.demo.bind(this)
}
render() {
const { isHost } = this.state
// this.function 是直接调用this指向window
return (
<div onClick={this.demo} >{isHost ? '雨天' : '晴天'}</div>
)
}
demo() {
// this.state.isHost = !this.state.isHost // 取反 状态不能直接更改(React响应捕捉不到)
let isHost = this.state.isHost
// 修改状态需要用setState
this.setState({ isHost: !isHost })
}
}
ReactDOM.render(<Wether />, document.querySelector('#test'))
class Wether extends React.Component {
// constructor(props) {
// super(props)
// // 改变this指向
// this.speck = this.speck.bind(this)
// }
// 简写: 直接原型上追加属性
state = {
name: '张三',
isName: true
}
render() {
let { isName, name } = this.state
return (
<div onClick={this.speck} style={{ fontSize: '24px', color: 'red' }}> {isName ? '显示名字' : `不显示${name}`}</div>
)
}
// 直接追加到原型上这里的this就是指原型
speck = () => {
let isName = this.state.isName
// setState里面必须包含一个对象
this.setState({ isName: !isName })
}
}
ReactDOM.render(<Wether />, document.querySelector('#test'))
props
直接在实例上的 key=value 会追加到React实例props上
class Person extends React.Component{
render(){
// console.log(this);
const {name,age,sex} = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age+1}</li>
</ul>
)
}
}
//渲染组件到页面
ReactDOM.render(<Person name="jerry" age={19} sex="男"/>,document.getElementById('test1'))
ReactDOM.render(<Person name="tom" age={18} sex="女"/>,document.getElementById('test2'))
const p = {name:'老刘',age:18,sex:'女'}
// 对象解构的方式使用
ReactDOM.render(<Person {...p}/>,document.getElementById('test3'))
实例.propTypes={ }
对象里面包含要限制的数据类型实例.defaultProps={ }
对象里面包含的是默认的属性值class DataLimit extends React.Component {
speck=()=>{
console.log(this.props)
}
render() {
const { name, age, sex } = this.props
// 注意点为props为只读属性不能修改
return (
<div>
<h2>{name}</h2>
<h2>{age+1}</h2>
<h2>{sex}</h2>
<h2 onClick={this.speck}> 点击事件</h2>
</div>
)
}
}
// propType 限制类型 (是否必传等)
// 1.PropTypes.string 限制为字符串
// 2.PropTypes.string.isRequired 限制为必传
// 3. 限制方法为func
DataLimit.propTypes = {
name: PropTypes.string.isRequired,
sex: PropTypes.string,
speak: PropTypes.func
}
// prop传值 默认值
DataLimit.defaultProps = {
sex: "女"
}
const data = { name: '张珊珊', age: 18, sex: "男" }
ReactDOM.render(<DataLimit {...data} />, document.querySelector('#test1'))
注意点
static
关键字给类添加属性 //类中可以直接写赋值语句,如下代码的含义是:给Car的实例对象添加一个属性,名为a,值为1
class Car {
constructor(name,price){
this.name = name
this.price = price
// this.wheel = 4
}
a = 1
wheel = 4
static demo = 100
}
const c1 = new Car('奔驰c63',199)
console.log(c1);
console.log(Car.demo); // 100
DataLimit
添加 propTypes
与defaultProps
两个属性做限制// static 给类添加属性
class DataLimit extends React.Component {
speck = () => {
console.log(this)
}
render() {
const { name, age, sex } = this.props
// 注意点为props为只读属性不能修改
return (
<div>
<h2>{name}</h2>
<h2>{age + 1}</h2>
<h2>{sex}</h2>
<h2 onClick={this.speck}> 点击事件</h2>
</div>
)
}
static propTypes = {
name: PropTypes.string.isRequired,
sex: PropTypes.string,
speak: PropTypes.func
}
// prop传值 默认值
static defaultProps = {
sex: "女"
}
}
// (DataLimit.对象) 与直接在类里面用(static 对象) 一样
console.log(DataLimit.propTypes);
const data = { name: '张珊珊', age: 18, sex: "男" }
ReactDOM.render(<DataLimit {...data} />,
document.querySelector('#test1'))
注意点
function Person(prop) {
const { name, age, sex } = prop
return (
<div>
<li><a href="">{name}</a></li>
<li><a href="">{age}</a></li>
<li><a href="">{sex}</a></li>
</div>
)
}
// 函数式组件限制
Person.propTypes = {
name: PropTypes.string.isRequired,
sex: PropTypes.string,
speak: PropTypes.func
}
// prop传值 默认值
Person.defaultProps = {
sex: "女"
}
const data = { name: '张珊珊', age: 18, sex: "男" }
ReactDOM.render(<Person {...data} />, document.querySelector('#test1'))
refs
操作dom
class PersonRefs extends React.Component {
clickRef = () => {
console.log(this); // {Input:dom节点 }
console.log(this.refs.Input);
}
render() {
// 字符串形式的ref
return (
<div>
<input type="text" ref="Input"/>
<button ref="button" onClick={this.clickRef}>点击Refs </button>
<input ref="input02" type="text" />
</div>
)
}
}
ReactDOM.render(<PersonRefs />, document.querySelector('#test'))
class RefsFunc extends React.Component {
addInput = () => {
alert(this.input.value)
// const { input1 } = this
// alert(input1.value)
}
state = {
isShow: true
}
isShowEvent = () => {
const { isShow } = this.state
console.log(isShow);
this.setState({ isShow: !isShow })
}
// ref 中写成这个只会回调一次
CurrentEvent = (vnode) => {
this.input02 = vnode
console.log('xxxxxx');
}
render() {
// 默认回调一次
//更新时,调用两次
// Vnode => this.input1 = Vnode 回调函数 ref 回调形式
return (
<div>
<input type="text" ref={CurrentNode => { this.input = CurrentNode; console.log('更新调用两次'); }} defaultValue="默认值" />
<input type="text" ref={this.CurrentEvent} />
<input type="text" ref={Vnode => this.input = Vnode} defaultValue="默认值" />
<button onClick={this.addInput}> 函数形式的Input使用 </button>
<p>{this.state.isShow ? "更新false" : "更新true"}</p>
<button onClick={this.isShowEvent}>切换内联函数调用</button>
</div>
)
}
}
ReactDOM.render(< RefsFunc />, document.querySelector('#test'))
React.createRef
调用后可以返回一个容器,该容器可以存储被ref所标识的节点,返回一个要ref绑定的dom节点, 且key唯一
class RefsFunc extends React.Component {
// 实例上添加一个myInput
myInput = React.createRef()
componentDidMount = () => {
console.log(this);
console.log(this.myInput.current.value);
// this.currentRefs.current.focusTextInput();
}
render() {
return (
<div>
<input type="text" ref={this.myInput} />
<button onClick={this.componentDidMount}>createRef生成容器标识refDOM节点</button>
</div>
)
}
}
ReactDOM.render(< RefsFunc />, document.querySelector('#test'))
参看官网:createRef官网链接
class Demo extends React.Component{
//展示左侧输入框的数据 refs
showData = ()=>{
alert(this.refs.myRefs.current.value);
}
// 操作的事件与要操作的组件数据在同一个dom节点时,利用事件委托的方式
//展示右侧输入框的数据(target 处理数据)
showData2 = (event)=>{
alert(event.target.value);
}
render(){
return(
<div>
<input ref={e=>this.myrefs=e} type="text" placeholder="点击按钮提示数据"/>
<button onClick={this.showData}>点我提示左侧的数据</button>
<input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}
//渲染组件到页面
ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))
class Login extends React.Component{
handleSubmit = (event)=>{
event.preventDefault() //阻止表单提交
const {username,password} = this
alert(`你输入的用户名是:${username.value},你输入的密码是:${password.value}`)
}
render(){
return(
<form onSubmit={this.handleSubmit}>
用户名:<input ref={c => this.username = c} type="text" name="username"/>
密码:<input ref={c => this.password = c} type="password" name="password"/>
<button>登录</button>
</form>
)
}
}
//渲染组件
ReactDOM.render(<Login/>,document.getElementById('test'))
//受控组件 , 事件触发Input中 传在数据的值
class Login extends React.Component{
//初始化状态
state = {
username:'', //用户名
password:'' //密码
}
//保存用户名到状态中
saveUsername = (event)=>{
this.setState({username:event.target.value})
}
//保存密码到状态中
savePassword = (event)=>{
this.setState({password:event.target.value})
}
//表单提交的回调
handleSubmit = (event)=>{
event.preventDefault() //阻止表单提交
const {username,password} = this.state
alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
}
render(){
return(
<form onSubmit={this.handleSubmit}>
用户名:<input onChange={this.saveUsername} type="text" name="username"/>
密码:<input onChange={this.savePassword} type="password" name="password"/>
<button>登录</button>
</form>
)
}
}
//渲染组件
ReactDOM.render(<Login/>,document.getElementById('test'))
Promise、setTimeout、arr.map()
等等注意点
onChange={this.InputName('username')('xxxx')}
共调用三次,react调用,默认返回的值为target
/创建组件
class Login extends React.Component {
// 初始化状态
state = {
username: "默认值",
password: ""
}
// 实时更新状态, 数据维护在state中为受控组件(相当于vue里面的v-model)
InputName = (dataType) => {
// onChange默认的返回一个函数
//回调的是一个函数 (既函数的柯里化)
return (Type) => {
console.log([Type]);
return (e) => {
this.setState({ [dataType]: e.target.value })
}
}
}
InputPassWord = (e) => {
this.setState({ password: e.target.value })
}
handlySubmit = (e) => {
e.preventDefault();
alert(`userName: ${this.state.username} passwrod : ${this.state.password}`,)
}
render() {
return (
<div>
<form onSubmit={this.handlySubmit} >
{ /* value 绑定默认值*/}
<input type="text" value={this.state.username} onChange={this.InputName('username')('xxxx')} name="username" />
<input type="text" onChange={this.InputPassWord} name="password" />
<button>提交</button>
</form>
</div>
)
}
}
ReactDOM.render(<Login />, document.querySelector('#test'))
class Login extends React.Component {
// 初始化状态
state = {
username: "默认值",
password: ""
}
// 实时更新状态, 数据维护在state中为受控组件(相当于vue里面的v-model)
InputName = (dataType, event) => {
// onChange默认的返回一个函数
// [datatype]使用变量作为属性名
this.setState({ [dataType]: event.target.value })
}
InputPassWord = (e) => {
this.setState({ password: e.target.value })
}
handlySubmit = (e) => {
e.preventDefault();
alert(`userName: ${this.state.username} passwrod : ${this.state.password}`,)
}
render() {
return (
<div>
<form onSubmit={this.handlySubmit} >
{ /* 不用可里化的方式实现
1. onChange 先调用一个event函数在event 函数中又调用了this.InputName这个函数
*/}
<input type="text" value={this.state.username} onChange={(event) => {
this.InputName('username', event)
}
} name="username" />
<input type="text" onChange={this.InputPassWord} name="password" />
<button>提交</button>
</form>
</div>
)
}
}
ReactDOM.render(<Login />, document.querySelector('#test'))
1. constructor()
2. componentWillMount()
3. render()
4. componentDidMount() =====> 常用一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
1. shouldComponentUpdate()
2. componentWillUpdate()
3. render() =====> 必须使用的一个
4. componentDidUpdate()
1. 由ReactDOM.unmountComponentAtNode()触发
2. componentWillUnmount() =====> 常用一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
4.生命周期执行顺序代码参考
class Count extends React.Component {
constructor(props) {
super(props)
console.log('构造器,constructor');
}
state = {
count: 1
}
handlyAdd = () => {
let { count } = this.state
count++;
this.setState({ count })
}
// 钩子
// 1.组件将要挂载的钩子
componentWillMount() {
console.log('组件将要挂载的钩子 componentWillMount');
}
// 2. 挂载中
render() {
console.log('挂载中 render');
const { count } = this.state
return (
<div>
<p>当前的数字 {count}</p>
<button onClick={this.handlyAdd}>点我加一</button>
<button onClick={this.UnMountEvent}> 卸载组件</button>
<button onClick={this.mandatoryUpdate}> 强制更新,不改状态</button>
</div>
)
}
//3 挂载完毕
componentDidMount() {
console.log('挂载完毕 componentDidMount');
}
// 更新的组件
// 1. 组件是否可以更新 返回值ture 或false
shouldComponentUpdate() {
console.log('组件是否可以更新 shouldComponentUpdate');
// 注意点1. 这个方法不写默认可以更新 为true
// 2. 方法写入了 ,没有return 默认为false
return true
}
// 2. 组件将要更新
// 3. render(){}
componentWillUpdate() {
console.log('组件将要更新 componentWillUpdate');
}
// 4. 组件更新
componentDidUpdate() {
console.log('组件更新完毕 componentWillUpdate');
}
//999 卸载组件
UnMountEvent = () => {
console.log('卸载DOM节点 unmountComponentAtNode');
ReactDOM.unmountComponentAtNode(document.querySelector('#test'))
}
// 强制更新 不走shouldComponentUpdate()函数
mandatoryUpdate = () => {
this.forceUpdate()
}
}
// 父子组件生命周期
class Myfalter extends React.Component {
state = {
name: "父组件信息"
}
fatherEmitSon = () => {
const { name } = this.state
this.setState({ name: '修改父组件的信息' })
}
render() {
const { name } = this.state
return (
<div>
<h2>我是父组件</h2>
<h3>-----------------------------------</h3>
<button onClick={this.fatherEmitSon}>修改父组件值传递给子组件 </button>
< Myson name={name} />
</div>
)
}
}
class Myson extends React.Component {
render() {
return (
<div>
<h2>我是子组件</h2>
<p>我将要展示父组件的内容: <span style={{ color: 'red' }}>{this.props.name}</span></p>
</div>
)
}
componentDidMount() {
console.log('子组件挂载时调用 componentDidMount');
}
componentWillReceiveProps(props) {
// 1. (第一次接受值默认没有调用)子组件更新触发的生命周期 可以传递值
console.log('xxxxxxxxx', props);
}
shouldComponentUpdate() {
console.log('组件是否可以更新 shouldComponentUpdate');
// 注意点1. 这个方法不写默认可以更新 为true
// 2. 方法写入了 ,没有return 默认为false
return true
}
// 2. 组件将要更新
// 3. render(){}
componentWillUpdate() {
console.log('组件将要更新 componentWillUpdate');
}
// 4. 组件更新
componentDidUpdate() {
console.log('组件更新完毕 componentWillUpdate');
}
}
// ReactDOM.render( , document.querySelector('#test'))
ReactDOM.render(<Myfalter />, document.querySelector('#test'))
1. constructor()
2. getDerivedStateFromProps()
3. render()
4. componentDidMount() =====> 常用一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
1. getDerivedStateFromProps()
2. shouldComponentUpdate()
3. render()
4. getSnapshotBeforeUpdate()
5. componentDidUpdate()
1. componentWillUnmount() =====> 常用一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息
注意点 UNSAFE_componentWillMount() , UNSAFE_componentWillUpdate()
等生命周期废弃 具体参考官网
生命周期代码参考
class Count extends React.Component {
constructor(props) {
super(props)
console.log('构造器,constructor');
}
state = {
count: 1
}
handlyAdd = () => {
let { count } = this.state
count++;
this.setState({ count })
}
// 2. 挂载中
render() {
console.log('挂载中 render');
const { count } = this.state
return (
<div>
<p>当前的数字 {count}</p>
<button onClick={this.handlyAdd}>点我加一</button>
<button onClick={this.UnMountEvent}> 卸载组件</button>
<button onClick={this.mandatoryUpdate}> 强制更新,不改状态</button>
</div>
)
}
// 新增加的钩子(相当于将要挂载 或将要更新的钩子)
// 用处: state值完全取决于props
// 注意点 写入必须返回值
static getDerivedStateFromProps(props, state) {
console.log("新增加的钩子 getDerivedStateFormProps");
console.log('state', state);
// 返回一个对象
return props
}
// 新增加的钩子 (在更新之前获取快照)
// 注意点 必须返回一个快照 或null
getSnapshotBeforeUpdate() {
return '更新之前的值'
}
//3 挂载完毕
componentDidMount() {
console.log('挂载完毕 componentDidMount');
}
// 更新的组件
// 1. 组件是否可以更新 返回值ture 或false
shouldComponentUpdate() {
console.log('组件是否可以更新 shouldComponentUpdate');
// 注意点1. 这个方法不写默认可以更新 为true
// 2. 方法写入了 ,没有return 默认为false
return true
}
// 4. 组件更新完成(拿到之前的值,可以获取getSnapshotBeforeUpdate这个钩子return的值)
componentDidUpdate(preProps, preState, preValue) {
console.log('组件更新完毕 componentWillUpdate');
console.log('组件更新完成', preProps, preState, preValue);
}
//999 卸载组件
UnMountEvent = () => {
console.log('卸载DOM节点 unmountComponentAtNode');
ReactDOM.unmountComponentAtNode(document.querySelector('#test'))
}
// 强制更新 不走shouldComponentUpdate()函数
mandatoryUpdate = () => {
this.forceUpdate()
}
}
// 父子组件生命周期
class Myfalter extends React.Component {
state = {
name: "父组件信息"
}
fatherEmitSon = () => {
const { name } = this.state
this.setState({ name: '修改父组件的信息' })
}
render() {
const { name } = this.state
return (
<div>
<h2>我是父组件</h2>
<h3>-----------------------------------</h3>
<button onClick={this.fatherEmitSon}>修改父组件值传递给子组件 </button>
< Myson name={name} />
</div>
)
}
}
class Myson extends React.Component {
render() {
return (
<div>
<h2>我是子组件</h2>
<p>我将要展示父组件的内容: <span style={{ color: 'red' }}>{this.props.name}</span></p>
</div>
)
}
componentDidMount() {
console.log('子组件挂载时调用 componentDidMount');
}
componentWillReceiveProps(props) {
// 1. (第一次接受值默认没有调用)子组件更新触发的生命周期 可以传递值
console.log('xxxxxxxxx', props);
}
shouldComponentUpdate() {
console.log('组件是否可以更新 shouldComponentUpdate');
// 注意点1. 这个方法不写默认可以更新 为true
// 2. 方法写入了 ,没有return 默认为false
return true
}
// 2. 组件将要更新
// 3. render(){}
componentWillUpdate() {
console.log('组件将要更新 componentWillUpdate');
}
// 4. 组件更新
componentDidUpdate() {
console.log('组件更新完毕 componentWillUpdate');
}
}
ReactDOM.render(<Count />, document.querySelector('#test'))
// ReactDOM.render( , document.querySelector('#test'))
// scrollTop 滚动的高度
// scrollHeight 内容区域的高度
class NewsList extends React.Component {
state = {
newList: []
}
componentDidMount() {
setInterval(() => {
const { newList } = this.state
let list = `新闻${newList.length + 1}`
this.setState({ newList: [list, ...newList] })
}, 1000)
}
getSnapshotBeforeUpdate() {
return this.refs.list.scrollHeight
}
componentDidUpdate(preProps, preState, height) {
// console.log(preProps, preState, height);
// height 是 getSnapshotBeforeUpdate()传递的值
this.refs
}
render() {
return (
<div className="list" ref="list">
{
this.state.newList.map((n, index) => {
return <div key={index} className='news'> {n}</div>
})
}
</div>
)
}
}
ReactDOM.render(<NewsList />, document.getElementById('test'))
npm i -g create-react-app
create-react-app hello-react
<meta charset="utf-8" />
<!-- %PUBLIC_URL%代表public文件夹的路径 -->
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<!-- 开启理想视口,用于做移动端网页的适配 -->
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- 用于配置浏览器页签+地址栏的颜色(仅支持安卓手机浏览器) -->
<meta name="theme-color" content="red" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<!-- 用于指定网页添加到手机主屏幕后的图标 -->
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!-- 应用加壳时的配置文件 -->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>
<body>
<!-- 若llq不支持js则展示标签中的内容 -->
<noscript>You need to enable JavaScript to run this app.</noscript>
<!-- 根ID -->
<div id="root"></div>
第一步:创建代理配置文件
在src下创建配置文件:src/setupProxy.js
编写setupProxy.js配置具体代理规则:
const proxy = require('http-proxy-middleware')
module.exports = function(app) {
app.use(
proxy('/api1', { //api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000)
target: 'http://localhost:5000', //配置转发目标地址(能返回数据的服务器地址)
changeOrigin: true, //控制服务器接收到的请求头中host字段的值
/*
changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
changeOrigin默认值为false,但我们一般将changeOrigin值设为true
*/
pathRewrite: {'^/api1': ''} //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
}),
proxy('/api2', {
target: 'http://localhost:5001',
changeOrigin: true,
pathRewrite: {'^/api2': ''}
})
)
}
注意以上是react 17 以前版本 ,现在代理配置
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = function (app) {
app.use(
"/students", // 匹配
createProxyMiddleware({
target: "http://localhost:5000",
changeOrigin: true,
// pathRewrite:{
// '^/api':"" // 忽略
// }
})
);
};
注意点
fetch 发送请求,请求成功数据在 res.json()
中
fetch(`https://api.github.com/search/users?q=${value}`)
.then((res) => {
console.log("服务器连接成功");
// 1.调用res.JSON() 获取连接成功的数据并返回
return res.json();
})
.then((res) => {
console.log("获取数据成功", res);
})
.catch((err) => {
console.log("失败的回调");
});
fetch(`https://api.github.com/search/users?q=${value}`)
.then((data) => console.log(data))
.catch((error) => console.error(error));
const res = await fetch(`https://api.github.com/search/users?q=${value}`);
const data = await res.json();
console.log(data);
父组件在展示子组件时,会传递一些数据给子组件:采用如下方法
父组件通过
属性=值
的形式来传递给子组件数据,或采用解构的形式传参
子组件通过this.props
获取父组件传递过来的数据
export class App extends Component {
constructor() {
super()
this.state = {
books: [
{name: "算法导论", price: 79},
{name: "数据结构", price: 69},
{name: "漫画算法", price: 59},
]
}
}
render() {
const { books } = this.state
return (
<div>
{/* 将数据传递给子组件 */}
<Header books={books}/>
</div>
)
}
}
export class Header extends Component {
render() {
// 接受父组件传递过来的参数
const { books } = this.props
return (
<div>
<ul>
{
books.map(item => {
return (
<li key={item.name}>
名称: {item.name} 价格: {item.price}
</li>
)
})
}
</ul>
</div>
)
}
}
回调函数,子组件向父组件传递消息:
在React中同样是通过props传递消息,只是让父组件给子组件传递一个回调函数,在子组件中调用这个函数即可;
import React, { Component } from 'react'
import ConterButton from './c-cpn/ConterButton'
export class App extends Component {
state = {conter: 100}
changeConter() {
let {conter}= this.state
conter++
this.setState({ conter })
}
render() {
const { conter } = this.state
return (
<div>
<h2>{conter}</h2>
{/* 向子组件中传入一个事件 */}
<ConterButton getConter={this.changeConter()}/>
</div>
)
}
}
export default App
import React, { Component } from 'react'
export class ConterButton extends Component {
btnClick() {
// 当按钮发生点击事件时, 对父组件传递过来的函数进行回调
this.props.getConter()
}
render() {
return (
<div>
<button onClick={this.btnClick}>+1</button>
</div>
)
}
}
export default ConterButton
React.createRef()
创建Ref,保存在实例属性myRef上。父组件中,渲染子组件时,定义一个Ref属性,值为刚创建的myRef。import React, { Component, Fragment } from 'react';
class Son extends Component {
myFunc(name) {
console.log(name);
}
render() {
return <div></div>;
}
}
// 父组件
export default class Father extends Component {
this.myRef = React.createRef();
componentDidMount() {
// 调用子组件的函数,传递一个参数
this.myRef.current.myFunc('Jack');
}
render() {
return (
<div>
<Son ref={this.myRef} />
</div>
);
}
}
1. 类型限制 安装prop-types库
2. 导入 import PropTypes from ‘prop-types’
3. 使用 static propTypes={ 要限制的key:PropTypes.类型.是否必传}
类型限制官网链接参考
下载: npm install pubsub-js --save
使用: import PubSub from ‘pubsub-js’
订阅 PubSub.subscribe(‘delete’, function(data){ });
发布 PubSub.publish(‘delete’, data)
// A组件内的状态
state = {
users:[],
isFirst:true,
isLoading:false,
err:''
}
// 订阅了消息名为updateState的消息事件
componentDidMount(){
// 方法返回两个值(第一个值是订阅与发布共有的属性,第二个是接受发布的信息)
this.token = PubSub.subscribe('updateState',(_,data)=>{
this.setState(data) // 将收到的状态data更新
})
}
// 页面销毁前删除消息订阅 以防消息泄露
componentWillUnmount(){
PubSub.unsubscribe(this.token)
}
// 发布消息名为updateState的消息事件
PubSub.publish('updateState',{isFirst:false,isLoading:true})
axios.get(`https://api.github.com/search/users?q=${keyWord}`).then(res=>{
PubSub.publish('updateState',{users:res.data.items,isLoading:false})
}).catch(err=>{
PubSub.publish('updateState',{err:err.message,isLoading:false})
})
- 安装 npm i hy-event-store
- 创建实例
import { HYEventBus } from "hy-event-store"
const eventBus = new HYEventBus()
export default eventBus
nextClick() {
eventBus.emit("bannerNext", {nickname: "kobe", level: 99})
}
componentDidMount() {
eventBus.on("bannerNext", this.bannerNextClick, this)
}
componentWillUnmount() {
eventBus.off("bannerNext", this.bannerNextClick)
}
- 下载 react-router-dom: npm install --save react-router-dom
<Link to="/xxxxx">Demo</Link>
// component 包裹要切换的组件
<Route path='/xxxx' component={Demo}/>
的最外侧包裹了一个
或
import React, { Component } from "react";
import { Link, BrowserRouter, Route } from "react-router-dom";
import Home from "./components/Home/index";
import About from "./components/About/index";
/* BrowserRouter 只能有一个包裹因此可以调整到index中包裹 */
export default class App extends Component {
render() {
return (
<BrowserRouter>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<div className="page-header">
<h2>React Router Demo</h2>
</div>
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
<Link className="list-group-item" to="/about">About</Link>
<Link className="list-group-item" to="/home">Home</Link>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 注册路由 */}
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
</div>
</div>
</div>
</div>
</BrowserRouter>
);
}
}
{/* Link 与NavLik的区别 activeClassName="选中的类名" */}
<NavLink activeClassName="demo01" className="list-group-item" to="/about">About= </NavLink>
{/* About */}
<NavLink className="list-group-item" to="/home"> Home</NavLink>
navlink的选中样式参考链接
- 封装公共
MyNavlink
组件,this.props
可以接受组件的传值
import React, { Component } from "react";
import { NavLink } from "react-router-dom";
export default class MyNavLink extends Component {
render() {
// const { to } = this.props;
console.log(this.props);
// 1. this.props可以直接接受标签体的属性
// 2. < NavLink children /> 可直接展开显示children对应的value值
return (
<div>
<NavLink className="list-group-item" {...this.props} />
</div>
);
}
}
1.2 路由组件跳转,传值
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
{/* 编写路由链接 */}
{/* Link 与NavLik的区别 activeClassName="" */}
{/* 1.children 对应的value about 可以不写在标签题里面 */}
<MyNavLink to="/about" >About</MyNavLink>
<MyNavLink to="/home" children="Home" />
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 注册路由 */}
<Route path="/about" component={About} />
<Route path="/home" component={Home} />
</div>
</div>
</div>
</div>
单一匹配,它的独特之处在于它专门呈现路由
1.默认使用的是模糊匹配:【输入的路径】必须包含要【匹配的路径】,且顺序要一致)
2.开启严格匹配:
Redirect
参考链接
to="/home"
代表一级路由,to="/home/news"
二级路由
<div>
<ul className="nav nav-tabs">
<li><MyNavLink to="/home/news">News</MyNavLink></li>
<li><MyNavLink to="/home/message">Message</MyNavLink></li>
</ul>
{/* 注册路由 */}
<Switch><Route path="/home/news" component={News}/>
<Route path="/home/message" component={Message}/>
<Redirect to="/home/news"/>
</Switch>
</div>
- search传参,在路由上不需要接受传递的参数
- 路由组件使用传递的参数用
this.props.location.search
- 获取到的search是urlencoded编码字符串,需要借助querystring解析
- state传递参数一般包含path与state,path路径,state传的属性与值
- 路由组件中接受传递的值使用
this.props.location.state
1.params参数
路由链接(携带参数):<Link to='/demo/test/tom/18'}>详情</Link>
注册路由(声明接收):<Route path="/demo/test/:name/:age" component={Test}/>`
接收参数:this.props.match.params
2.search参数
路由链接(携带参数):<Link to='/demo/test?name=tom&age=18'}>详情</Link>
注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>
接收参数:this.props.location.search
备注:获取到的search是urlencoded编码字符串,需要借助querystring解析
3.state参数
路由链接(携带参数):<Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>
注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>
接收参数:this.props.location.state
备注:刷新也可以保留住参数
注意点
区分普通组件与路由组件,在普通组件中不能使用路由组件的API,比如:push,replace
等,要想使用需要借助 withRouter
对普通组件进行包裹
push()
,replace()
,go()
,goback()
等import React, { Component } from 'react'
import {Link,Route} from 'react-router-dom'
import Detail from './Detail'
export default class Message extends Component {
state = {
messageArr:[
{id:'01',title:'消息1'},
{id:'02',title:'消息2'},
{id:'03',title:'消息3'},
]
}
replaceShow = (id,title)=>{
//replace跳转+携带params参数
//this.props.history.replace(`/home/message/detail/${id}/${title}`)
//replace跳转+携带search参数
// this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`)
//replace跳转+携带state参数
this.props.history.replace(`/home/message/detail`,{id,title})
}
pushShow = (id,title)=>{
//push跳转+携带params参数
// this.props.history.push(`/home/message/detail/${id}/${title}`)
//push跳转+携带search参数
// this.props.history.push(`/home/message/detail?id=${id}&title=${title}`)
//push跳转+携带state参数
this.props.history.push(`/home/message/detail`,{id,title})
}
back = ()=>{
this.props.history.goBack()
}
forward = ()=>{
this.props.history.goForward()
}
go = ()=>{
this.props.history.go(-2)
}
render() {
const {messageArr} = this.state
return (
<div>
<ul>
{
messageArr.map((msgObj)=>{
return (
<li key={msgObj.id}>
{/* 向路由组件传递params参数 */}
{/* {msgObj.title} */}
{/* 向路由组件传递search参数 */}
{/* {msgObj.title} */}
{/* 向路由组件传递state参数 */}
<Link to={{pathname:'/home/message/detail',state:{id:msgObj.id,title:msgObj.title}}}>{msgObj.title}</Link>
<button onClick={()=> this.pushShow(msgObj.id,msgObj.title)}>push查看</button>
<button onClick={()=> this.replaceShow(msgObj.id,msgObj.title)}>replace查看</button>
</li>
)
})
}
</ul>
<hr/>
{/* 声明接收params参数 */}
{/* */ }
{/* search参数无需声明接收,正常注册路由即可 */}
{/* */ }
{/* state参数无需声明接收,正常注册路由即可 */}
<Route path="/home/message/detail" component={Detail}/>
<button onClick={this.back}>回退</button>
<button onClick={this.forward}>前进</button>
<button onClick={this.go}>go</button>
</div>
)
}
}
React Router 以三个不同的包发布到 npm 上,它们分别为:
等 。
等。与React Router 5.x 版本相比,改变了什么?
内置组件的变化:移除
,新增
等。
语法的变化:component={About}
变为 element={
等。
新增多个hook:useParams
、useNavigate
、useMatch
等。
,引入了新的替代者:
。
和
要配合使用,且必须要用
包裹
。
相当于一个 if 语句,如果其路径与当前 URL 匹配,则呈现其对应的组件。
属性用于指定:匹配时是否区分大小写(默认为 false)。
都会查看其所有子
元素以找到最佳匹配并呈现组件 。
也可以嵌套使用,且可配合useRoutes()
配置 “路由表” ,但需要通过
组件来渲染其子路由。 与
<>
<Link to="/about">About</Link>
<Link to="/home">Home</Link>
<ul className="nav">
{/* 注册路由 */}
<Routes>
<Route path="/about" element={<About />}></Route>
<Route path="/home" element={<Home />}></Route>
</Routes>
</ul>
</>
与
或
包裹。
组件类似,且可实现导航的“高亮”效果。 <Link to="/路径">Home</Link>
<NavLink className={computedClassName} to="路径">Home</NavLink>
NavLink
高亮效果的实现
// 注意: NavLink默认类名是active,下面是指定自定义的class
//自定义样式
<NavLink
to="login"
className={({ isActive }) => {
console.log('home', isActive)
return isActive ? 'base one' : 'base'
}}
>login</NavLink>
/*
默认情况下,当Home的子组件匹配成功,Home的导航也会高亮,
当NavLink上添加了end属性后,若Home的子组件匹配成功,则Home的导航没有高亮效果。
*/
<NavLink to="home" end >home</NavLink>
组件被渲染,就会修改路径,切换视图。replace
属性用于控制跳转模式(push 或 replace,默认是push)。 <Routes>
<Route path="/about" element={<About />}></Route>
<Route path="/home" element={<Home />}></Route>
{/* 默认为push模式,replace为true时跳转为replace */}
<Route path="/" element={<Navigate to="/home" replace={true} />}></Route>
</Routes>
产生嵌套时,渲染其对应的后续子路由。既指定路由呈现位置const RoutesList = [
{
path: "/about",
element: <About />,
},
{
path: "/home",
element: <Home />,
children: [
{
path: "message",
element: <Message />,
},
{
path: "news",
element: <News />,
},
],
},
];
// 二级路由
<div className="NavContetn">
{/* 指定路由组件呈现的位置Outlet */}
<Outlet />
</div>
// router
{
path: '/detail/:id/:title',
element: <Details />
},
//navigate 传参,注意函数式组件与类组件
navigate(`/detail/${id}/${title}`, { replace: true })
和
。 //路由表配置:src/routes/index.js
import About from '../pages/About'
import Home from '../pages/Home'
import {Navigate} from 'react-router-dom'
export default [
{
path:'/about',
element:<About/>
},
{
path:'/home',
element:<Home/>
},
{
path:'/',
element:<Navigate to="/about"/>
}
]
//App.jsx
import React from 'react'
import {NavLink,useRoutes} from 'react-router-dom'
import routes from './routes'
export default function App() {
//根据路由表生成对应的路由规则
const element = useRoutes(routes)
return (
<div>
......
{/* 注册路由 */}
{element}
......
</div>
)
}
import React from 'react'
import {useNavigate} from 'react-router-dom'
export default function Demo() {
const navigate = useNavigate()
const handle = () => {
//第一种使用方式:指定具体的路径
navigate('/login', {
replace: false,
state: {a:1, b:2}
})
//第二种使用方式:传入数值进行前进或后退,类似于5.x中的 history.go()方法
navigate(-1)
}
return (
<div>
<button onClick={handle}>按钮</button>
</div>
)
}
params
参数,类似于5.x中的match.params
。// 路由表
children: [
{
path: "details/:name",
element: <Details />,
},
],
//导航路由传参
<NavLink to={`Details?name=${UserInfo.name}`}> Details</NavLink>
// useParams接受参数
import { useParams } from 'react-router-dom'
export default function Details() {
const add=useParams()
return (
<div>
{add.name}
</div>
);
}
//导航路由传参
<NavLink to={`Details?name=${UserInfo.name}`}> Details</NavLink>
//接受参数
import { useSearchParams } from "react-router-dom";
export default function Details() {
// setSearch 用于更新search 参数
const [search, setSearch] = useSearchParams();
console.log(search,setSearch);
// setSearch('name="user"');
// 返回的一个函数
const name = search.get("name");
return (
<div> <li>{name}</li></div>
);
}
location
属性。//导航路由传参
<NavLink to="details" state={{ name: UserInfo.name }}> Details</NavLink>
//接受参数
import React from "react";
import { useLocation } from "react-router-dom";
export default function Details() {
const {state:{name}}= useLocation();
return (
<div>
<li>{name}</li>
</div>
);
}
match
属性。 <Route path="/login/:page/:pageSize" element={<Login />}/>
<NavLink to="/login/1/10">登录</NavLink>
export default function Login() {
const match = useMatch('/login/:x/:y')
console.log(match) //输出match对象
//match对象内容如下:
/*
{
params: {x: '1', y: '10'}
pathname: "/LoGin/1/10"
pathnameBase: "/LoGin/1/10"
pattern: {
path: '/login/:x/:y',
caseSensitive: false,
end: false
}
}
*/
return (
<div>
<h1>Login</h1>
</div>
)
}
的上下文中呈现,则 useInRouterContext
钩子返回 true,否则返回 false。POP
、PUSH
、REPLACE
。POP
是指在浏览器中直接打开了这个路由组件(刷新页面)。
// hoc/index.js
import { useLocation, useNavigate, useParams } from 'react-router-dom';
export function withRouter(WapperWithRouter) {
return function (props) {
const navigate = useNavigate()
const params=useParams()
const location = useLocation ()
const router = { navigate, params, location }
return < WapperWithRouter {...props} router={router} />
}
}
import React, { Component } from 'react';
import { Link, Outlet } from 'react-router-dom';
import { withRouter } from '../../hoc';
class About extends Component {
navigateTo(path){
console.log(path);
console.log(this.props.router.navigate(path));
}
render() {
return (
<div>
<Link to="/about/recommend">音乐列表展示</Link>
<Link to="actor"> 音乐艺人展示</Link>
{/* 注意路径的匹配 to 与path保持一致 */}
<button onClick={()=>this.navigateTo('classqa')}> 类组件如何实现跳转</button>
<div>
<Outlet />
</div>
</div>
)
}
}
export default withRouter(About)
至此 类组件的props就有了路由的一些属性可以用navigate
参考
参考2
React.useState()
,React.useEffect()
, React.useRef()
注意点
: setState更新是异步的
setState(stateChange, [callback])------对象式的setState
1. stateChange为状态改变对象(该对象可以体现出状态的更改)
2. callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
this.setState({count},()=>{
console.log(this.state.count);
})
// 或者
this.setState({count:count+1})
setState(updater, [callback])------函数式的setState
1. updater为返回stateChange对象的函数。
2. updater可以接收到state和props。
3. callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。
this.setState((state)=>( { count: state.count+1 }))
语法: const [xxx, setXxx] = React.useState(initValue)
import React from "react";
// 1. 注意点 函数式组件命名 首字母大写
// 2. useState 初始调用后会把值缓存起来,不会因为函数的再次调用把count重新赋值为0
// 3.hooks 必须在最顶层使用 不能在if for 等使用
// 4.useState 如果没有传递参数,那么初始化值为undefined
// 5. 箭头函数写法 const DemoCount= React.memo(()=>{ })
export default function DemoCount(params) {
// 第一个是返回的值, 第二个参数返回一个函数设置第一个返回的值
let [count, setCount] = React.useState(0);
function add() {
// count++;
// setCount(count); //第一种写法
setCount(count=>count+1)
}
return (
<div>
<h1>{count}</h1>
<button onClick={add}>点击+1</button>
<button onClick={()=>setCount(count-1)}>点击-1</button>
</div>
);
}
语法: useEffect(() => {
// 在此可以执行任何带副作用操作
return () => { // 在组件卸载前执行
// 在此做一些收尾工作, 比如清除定时器/取消订阅等
}
}, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行
componentDidMount()
componentDidUpdate()
componentWillUnmount()
// 卸载组件
import React from "react";
function App() {
const [showChild, setShowChild] = React.useState(true);
function unmound() {
setShowChild(false);
}
return (
<>
{showChild && <DemoCount />}
<button onClick={unmound}>卸载组件</button>
</>
);
}
// useEffect 相当于componentDidMount,或者 componentDidUpdate (主要看第二个值传不传)
function DemoCount() {
let [count, setCount] = React.useState(0);
React.useEffect(() => {
let timer = setInterval(() => {
setCount((count) => count + 1);
}, 1000);
return () => {
// 返回值为清除定时器
clearInterval(timer);
};
}, []); // 传一个数组,表示检测谁,(默认不传,检测所有,传空数组谁也不检测)
function add() {
setCount((count) => count + 1);
}
return (
<div className="id">
<h1>{count}</h1>
<button onClick={add}>点击+1</button>
</div>
);
}
export default App;
语法: const refContainer = useRef()
function App() {
const myrefs = React.useRef();
function getRefsValue() {
console.log(myrefs.current.value);
}
return (
<div>
<input ref={myrefs} type='text' />
<button onClick={getRefsValue}>ref</button>
</div>
);
}
Fragment
代替就不会渲染import React, { Fragment } from "react";
// Fragment 忽略标签,|| <>> 区别在于是否需要key
function App() {
const myrefs = React.useRef();
function getRefsValue() {
console.log(myrefs.current.value);
}
return (
<Fragment>
<input ref={myrefs} type="text" />
<button onClick={getRefsValue}>ref</button>
</Fragment>
);
}
- Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过
组件树的逐层传递 props
- Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。
请谨慎使用,因为这会使得组件的复用性变差
React.createContext
使用此API可以创建一个Context对象,组件会从最近的
Provider
中读取对应的值。只有当组件所处的树种不存在Provider时,defaultValue参数才会生效
const MyContext = React.createContext(defaultValue);
Context.Provider
- Provider接受一个value属性,传递给消费组件 当Provider的value属性值更变时,内部的所有消费组件都会重新渲染
- context会根据引用标识进行重新渲染,所以当向value传递一个对象时,需要注意:
当Provider重新渲染时,可能会触发Consumer意外渲染。为了防止这种情况,将value状态提升到父节点的state中
<MyContext.Provider value={某个值}/>
Context.Consumer
- 需要一个函数作为子元素,函数接收
context
值,返回一个React
节点- 传递给函数的value值等价于组件树上方离这个
context
最近的Provider
提供的value值。如果没有对应的Provider
,value
参数等同传递给createContext()的defaultValue
<MyContext.Consumer>
{value => /* 基于 context 值进行渲染*/}
</MyContext.Consumer>
Class.contextType
this.context
来获取最近Context
上的值。你可以在任何生命周期中访问到它,包括render函数中const MyContext = React.createContext()
class MyClass extends React.Component {
render() {
let value = this.context;
/* 基于这个值进行渲染工作 */
}
}
MyClass.contextType = MyContext
public class fields语法中的static类属性初始化contextType
,此外一般都是使用第二种用法const MyContext = React.createContext()
class MyClass extends React.Component {
static contextType = MyContext
render() {
let value = this.context;
/* 基于这个值进行渲染工作 */
}
}
Provider
, Consumer
可以用于类组件和函数式组件// A->B->C , context 组件通信
import React, { Component } from "react";
// 需求C组件展示A组件的Name
// 创建上下文context
const MyContext = React.createContext();
export default class App extends Component {
state = {
name: "我是A组件,需要在C组件中展示",
nameInfo: "A组件的详细信息",
};
render() {
const { name, nameInfo } = this.state;
return (
<div>
<h2>A</h2>
<h5>----------------------</h5>
<MyContext.Provider value={{ name, nameInfo }}>
<B />
</MyContext.Provider>
</div>
);
}
}
class B extends Component {
render() {
return (
<>
<h2> B</h2>
<h5>----------------------</h5>
<C />
</>
);
}
}
// class C extends Component {
// // 声明接受context
// static contextType = MyContext;
// render() {
// console.log(this.context); //
// return (
//
// );
// }
// }
// 函数式组件使用context,Provider只使用于类组件, Consumer 可以用于类组件和函数式组件
function C() {
return (
<div>
<h2>C</h2>
<MyContext.Consumer>
{
value=> {
return `${value.name}`
}
}
</MyContext.Consumer>
<h5>----------------------</h5>
</div>
);
}
import React, { memo, useEffect, useState } from 'react';
// import { ThemeContext } from './context/themContext';
// import { MyUserInfoContext } from './context/userInfo';
import Hooks from './hooks';
// 自定义hooks 需要以use开头
function useSumApp(Name) {
useEffect(() => {
console.log(Name)
return () => {
console.log(Name)
}
}, [Name])
}
const App01 = memo(() => {
// const themeStyleContext = useContext(ThemeContext)
// const userInfoContext = useContext(MyUserInfoContext)
const [themeStyleContext, userInfoContext] = Hooks()
console.log(themeStyleContext);
useSumApp('App01')
return (
<div>App01</div>
)
})
const App02 = memo(() => {
// const themeStyleContext = useContext(ThemeContext)
// const userInfoContext = useContext(MyUserInfoContext)
useSumApp('App02')
return (
<div>App02</div>
)
})
const App = memo(() => {
const [isShow, setIsShow] = useState(true)
return (
<div>
<button onClick={() => setIsShow(!isShow)}>updata Component</button>
{isShow && <App01 />}
{isShow && <App02 />}
</div>
)
})
export default App
hooks的封装
import { ThemeContext } from './context/themContext';
import { MyUserInfoContext } from './context/userInfo';
import { useContext } from 'react';
export default function Hooks() {
const themeStyleContext = useContext(ThemeContext)
const userInfoContext = useContext(MyUserInfoContext)
return [themeStyleContext, userInfoContext]
}
注意点
, 只要调用setState 父子组件中的render函数都会调用
- 重写shouldComponentUpdate()方法 比较新旧state或props数据, 如果有变化才返回true,
- 如果没有返回false
shouldComponentUpdate(nextProps, nextState) {
// 这里可以判断是否更新子组件的render 当nextState与this.state的值相同时,返回false不同返回ture ,简单点说就是阀门是否打开
if (nextState.name === this.state.name) {
return false;
} else {
return true;
}
}
- 使用
PureComponent
PureComponent重写了shouldComponentUpdate()
,- 只有state或props数据有变化才返回true 注意:
只是进行state和props数据的浅比较,
- 如果只是数据对象内部数据变了, 返回false 不要直接修改state数据, 而是要产生新数据
import React, { PureComponent } from "react";
// PureComponent 判断 子组件是否使用父组件的内容,数据更新时是否调用子组件的render
export default class App extends PureComponent {}
this.props.children
渲染1. A组件使用
<B>
<C>xxxx</C>
</B>
2. 在B要使用C组件中调用 {this.props.children}渲染
this.props
渲染// 父组件传递
const btn = <button>按钮2</button>;
<NavBarTwo
leftSlot={btn}
centerSlot={<h2>呵呵呵</h2>}
rightSlot={<i>斜体2</i>}
/>
// 子组件
const { leftSlot, centerSlot, rightSlot } = this.props
return (
<div className='nav-bar'>
<div className="left">{leftSlot}</div>
<div className="center">{centerSlot}</div>
<div className="right">{rightSlot}</div>
</div>
render props
渲染<B render={(data) => <C data={data}></C>}></B>
B组件: {this.props.render(内部state数据)}
C组件: 读取A组件传入的数据显示 {this.props.data}
import React, { PureComponent } from "react";
export default class App extends PureComponent {
render() {
return (
<div style={{ width: "1200px", height: "300px", background: "red" }}>
<h2>A</h2>
<h5>--------App------------</h5>
{/* 相当于Vue里面的插槽, */}
<B render={(name) => <C name={name} />} />
</div>
);
}
}
class B extends PureComponent {
state = { name: "我是B组件需要在C组件展示" };
render() {
console.log("@,render Children");
return (
<div
style={{
width: "300px",
height: "200px",
margin: "0 auto",
background: "#fff",
}}
>
<h2> B 组件</h2>
<h5>----------------------</h5>
{/* 调用C组件的render */}
{/* {this.props.children} */}
{/* 第二种方式:预留插槽 */}
{this.props.render(this.state.name)}
</div>
);
}
}
class C extends PureComponent {
render() {
return (
<div style={{ background: "#0f03d6", color: "#fff", height: "80px" }}>
<h2> C 组件</h2>
<a href="ccc" style={{color:'#ddcc00'}}> {this.props.name}</a>
</div>
);
}
}
后代组件
错误,渲染出备用页面
// 生命周期函数,一旦后台组件报错,就会触发
static getDerivedStateFromError(error) {
console.log(error);
// 在render之前触发
// 返回新的state
return {
hasError: true,
};
}
componentDidCatch(error, info) {
// 统计页面的错误。发送请求发送到后台去
console.log(error, info);
}