React组件间通信
父子组件传值(props)
父组件 -> 子组件
父组件<son value={this.state.value} />
子组件{this.props.value}
子组件 -> 父组件
子组件this.props.onClick()
父组件<son onClick={this.onClick.bind(this)} />
onClick(e){
this.setState({
value: e
})
}
跨层级组件通信(React.createContext())
//文件context.js
import React from 'react'
let { Consumer, Provider } = React.createContext(null) //创建context并暴露Consumer和Provider
export { Consumer, Provider }
//父组件
import { Provider } from './context.js'
<Provider value={value}>
<son />
</Provider>
//子组件
import { Consumer } from './context.js'
<Context>
{(value) => {
<div>父组件的值:{value}</div>
<grandson />
}}
</Context>
//孙组件
import { Consumer } from './context.js'
<Consumer>
{(value) => {
<div>父组件的值:{value}</div>
}}
</Consumer>
父组件获取子组件实例从而调用子组件方法(OnRef、props)
//父组件
child: any
sonRef = (ref) => {
this.child = ref
}
this.child.onClickSon()
<Son onRef={this.sonRef}></Son>
//子组件
componentDidMount() {
this.props.onRef(this) // 在这将子组件的实例传递给父组件this.props.onRef()方法
}
onClickSon = ()=>{
this.setState({
value: ''
})
}
访问DOM节点或者组件(ref)
//父组件
son: any
this.son = React.createRef() // 在此处创建ref
onClick = () => {
const { current } = this.son // 注意,这里必须通过 this.son.current拿到子组件的实例
current.onClickSon()
}
<son ref={this.son}></son>
//子组件
onClickSon = () => {
alert()
}
React生命周期
react的生命周期主要分为四个阶段:组件初始化、组件挂载、组件更新、组件卸载。
组件初始化阶段:
constructor()完成了React数据的初始化。
组件挂载阶段:
componentWillMount(){}主要用于服务端渲染,修改state的值。
render()页面渲染执行逻辑,render()函数把jsx编译为函数并生成虚拟Dom,只能通过this.props和this.state访问数据,且不能执行this.setState更改组件状态。
componentDidMount(){}组件挂载到Dom后调用,且只调用一次。可以在函数中通过this.refs获取真实Dom。
组件更新阶段:
componentWillReceiveProps(nextProps){}接收父组件新的props时通过this.setState更新state重新渲染组件执行的逻辑。
shouldComponentUpdate(nextProps,nextState){}在setState以后,state发生变化,组件会重新渲染执行逻辑,如果在这个生命周期中return false可以阻止组件的更新,主要用于性能优化。return true则会更新组件。
componentWillUpdate(nextProps,nextState){}在shouldComponentUpdate方法返回true后,组件将重新渲染调用该函数(此时不更新props和state,否则会陷入死循环),之后调用render()方法进行重新渲染。
componentDidUpdate(prevProps,prevState){}可以获取操作更新后的Dom节点。
组件卸载阶段:
componentWillUnmount(){}组价卸载前被调用,可执行清除定时器和组件的监听器等操作。
//父组件
class Father extends React.Component {
static defaultProps = {
//1.加载默认静态属性
};
constructor() {
super()
//2.加载默认状态,接收父组件传下来的props和定义this.state初始内容
this.state = {num: 1}
}
componentWillMount() {
//3.父组件挂载前
}
componentDidMount() {
//5.父组件挂载完成
}
shouldComponentUpdate() {
//6.父组件是否需要更新
return true 更新组件
return false 不更新组件
}
componentWillUpdate() {
//7.组件将要更新
}
componentDidUpdate() {
//8.父组件更新完成
}
onClick = ()=>{
this.setState({
num: this.state.num + 1
})
};
render() {
4.render父组件挂载
return (
<div>
<a onClick={this.onClick}></a>
<Child />
</div>
)
}
}
ReactDOM.render(<Father/>,document.getElementId('root'));
//子组件
class Child extends React.Component {
componentWillReceiveProps() {
//9.子组件将要接收到新属性
}
shouldComponentUpdate(newProps,newState) {
//10.子组件是否需要更新
return true 更新组件
return false 不更新组件
}
componentWillUpdate() {
//11.子组件将要更新
}
componentDidUpdate() {
//13.子组件完成更新
}
componentWillUnmount() {
//14.子组件将卸载
}
render() {
//13.子组件挂载中
return (
<p>this.props.num</p>
)
}
}
React路由
React提供了react-router来管理路由,react-router包含3个分别为react-router是router的核心代码、react-router-dom是用于浏览器和react-router-native是用于原生应用。
react-router有3种基础组件
路由组件:BrowserRouter 和 HashRouter
这两个路由都会创建一个专门的history对象,两者之间的主要区别是他们存储URL和与Web服务器通信的方式,是否带#。BrowserRouter使用了history模式,HashRouter使用了hash模式。
路由匹配组件:Switch 和 Route
Route组件是用于路径的匹配,path属性:用户设置匹配到的路径。component属性:设置匹配到的路径后,渲染的组件。exact属性:精准匹配,只有精准匹配到完全一致的路径,才会渲染对应的组件。
Switch组件匹配path对应组件,Route组件匹配到都会渲染,而Switch组件包裹只匹配第一个。
导航组件:Link, NavLink, 和 Redirect
Link组件用于路径跳转,渲染成a元素,属性to用于跳转的路径。
NavLink组件匹配路径选中,修改a元素的样式。属性activeStyle:匹配到的样式,属性activeClassName匹配到的类名class,属性exact:是否精准匹配。
Redirect组件是用于路由的重定向,当这个组件出现后,就会执行跳转对应的to路径中。
Redux
Redux是一个可预测的JavaScript应用状态管理容器,将整个应用的状态存储到store里。组件改变state的方法是通过调用store的dispatch方法,触发action,这个action被对应的reducer处理。组件派发(dispatch)行为(action)给store,而不是直接通知其他组件。其他组件可以通过订阅store中的状态(state)来刷新自己的视图。
Redux使用教程(Provider、reateStore、connect)
//安装redux、react-redux
npm install redux --save
npm install react-redux
初始代码模版index.js文件
//引入createStore、Provider
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import App from "./App.js"
//创建Store的初始化数据
const initialState = {value: ""}
//接收state和action然后return state数据
const rootReducer = (state,action) => {
return state
}
//传入rootReducer和initialState生成store
const store = createStore(rootReducer,initialState)
//store作为参数是Redux在React使用的绑定库,搭建Redux和React交流的桥梁。
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
document.getElementById("root")
)
App.js组件使用
//导入connect函数
import React from "react"
import {connect} from "react-redux"
import todo from "./todo.js"
class App extends React.Component {
//接收store数据
constructor(props) {
super(props)
}
render() {
return (
<div>{this.props.value}</div>
<todo></todo>
);
}
}
const mapStateToProps = (state,props) => ({
value: state.value
})
export default connect(mapStateToProps)(App)
connect是一个高阶函数,高阶函数是指可以接受参数调用并返回另一个函数的函数。connect通过调用函数返回新函数再接收App组件作为参数,通过mapStateToProps注入value属性,再注入App组件。
将Redux和React之间的数据互通,并删除了this.state,使store的状态替代了this.state,因此this.state.value会报错。
Action Creators
Action是更新Store的状态的一种方式:通过调用dispatch函数,传递一个action给这个函数。
Action是一个简单的JavaScript对象,它包含type类型(必有属性),以及更新的状态需要的数据。{type:'do',value: '内容'}
//actions.js文件
export const doChange = value => ({
type: 'do',
value,
})
//todo.js
import React from "react"
import {connect} from "react-redux";
import {doChange} from "./actions"
const todo = ({dispatch}) => {
return (
<button onClick={e => {
e.preventDefault();
dispatch(doChange('改变数据'));
}}>todo</button>
)
}
export default connect()(todo)
Reducer:响应Action的指令
Reducer响应从组件中dispatch出来Action,并更新Store中的状态。
Reducer是一个普通的JavaScript函数,它接收两个参数: state是Store中的对象状态树和action是组件中dispatch的那个Action。
reducer(state,action){
//对state进行操作
//state = {a:1,b:2}修改方式 newState = {...state,a: 3}得出 {a:3,b:2}
return newState
}
//在初始代码模版index.js文件
const rootReducer = (state,action) => {
switch (action.type) {
case "do": {
return {
...state,
value: action.value
}
}
default: return state
}
}
reducer的约定是一个纯函数,不能直接修改state,而是使用{...}对对象解构对的方式返回一个被修改后的state。
JSX
JSX是JavaScript XML的缩写,也是JavaScript的语法扩展是React使用的一种文件,它利用JavaScript的表现力和类似HTML的模版语法,这使得HTML文件非常容易理解,此文件能使应用非常可靠,并能够提高其性能。浏览器不能读取常规JavaScript对象中的JSX,需要使用Babel转换器将JSX文件转换成JavaScript对象,然后才能让浏览器解析。
Virtual Dom虚拟Dom
Virtual DOM是一个轻量级的JavaScript对象,它最初只是real DOM的副本。它是一个节点树,他将元素、他们的属性和内容作为对象及其属性。React的渲染函数从React组件中创建一个节点树。然后它响应数据模型中的变化来更新该树,该变化是由用户或系统完成的各种动作引起的。
Virtual DOM工作流程
- 每当低层数据发生变化时,整个UI都将在Virtual DOM描述中重新渲染。
- 然后计算之前DOM表示与新表示的之间的差异
- 完成计算后,将只用实际更改的内容更新real DOM。
React知识总结
副文本HTMl解析dangerouslySetInnerHTML
<div dangerouslySetInnerHTMl={value}></div>
两种事件绑定
<button onClick={this.click.bind(this,value1,value2)}></button>
<button onClick={() => this.click(value1,value2)}></button>
click(data1,data2){
console.log(data1,data2)
}
setState()不可变值、可能是异步更新、可能会被合并
数组不能使用push()、pop()、splice()等方法,这样违反了不可变值,会影响 shouldCompententUpdate判断
this.setState(() => ({
list: this.state.list.concat(array), //追加
list: [...this.state.list,array], //追加
list: this.state.list.slice(0,3), //截取
list: this.state.list.filter(item => item < 100) //筛选
}))
对象修改值
this.setState(() => {
obj: Object.assign({},this.state.obj,object),
obj: {...this.state.obj,...obj}
})
通常情况下为异步更新值
const count = this.state.count + 1
this.setState({
count
},() => {
//这个函数没有默认参数
//类比Vue的$nextTick()
console.log(this.state.count)//打印更新后的值
})
【重点】传入对象会被合并,结果只执行一次
初始值this.state.count = 0
this.setState({count:this.state.count+1})
this.setState({count:this.state.count+1})
this.setState({count:this.state.count+1})
输出值:this.state.count = 1
【重点】传入函数不会合并,函数无法合并
初始值this.state.count = 0
this.setState((prevState, props) => {
return {
count: prevState.count + 1
}
})
this.setState((prevState, props) => {
return {
count: prevState.count + 1
}
})
this.setState((prevState, props) => {
return {
count: prevState.count + 1
}
})
输出值:this.state.count = 3