React JSX
XML 被设计用来传输和存储数据
HTML 被设计用来显示数据
- JSX 就是 JavaScript,会发现react的写法与传统的HTML不一样,比如class 在react写成className,for在react写成htmlFor ,这些标签语法就是JSX。
const blackBtn =
JSX如何判断条件和渲染列表
react条件判断就和javasctipt一样:用if else,三元表达式,逻辑运算符 && ||
class ConditionDemo extends React.Component {
constructor(props) {
super(props)
this.state = {
theme: 'black'
}
}
render() {
const blackBtn =
const whiteBtn =
// // if else
if (this.state.theme === 'black') {
return blackBtn
} else {
return whiteBtn
}
// // 三元运算符
return
{ this.state.theme === 'black' ? blackBtn : whiteBtn }
// &&
return
{ this.state.theme === 'black' && blackBtn }
}
}
export default ConditionDemo
React渲染
要将React元素渲染到根DOM节点中,把React元素传递给 ReactDOM.render() 的方法来将其渲染到页面上渲染列表
用map、key,react在render函数里面,把map遍历的对象,或者key遍历的对象放在{}表达式里面,把它渲染出来
React事件为何使用bind绑定this
Javascript是一种基于对象(object-based)的语言
class 的方式是 function 方式的语法糖,ES6中 class 的类型还是 function
JSX 回调函数中的 this,类的方法默认是不会绑定 this 的
react组件 ,this指向是指向当前的组件,当组件实例化之后,this指向的是新生成的实例,在新生成的实例使用this.state.xxx是找不到的,所以需要在构造器constructor重新绑定一下this
React 事件和 DOM 事件
event.preventDefault() 方法阻止元素发生默认的行为(如,当点击提交按钮时阻止对表单的提交)。
- JSX 的语法进行事件处理,需要传入一个函数作为事件处理函数,而不是一个字符串(DOM 元素的写法)
HTML 通常写法
React 中写法
React 中的event是一个合成事件,不是原生的Event
- 通过 bind 方式向监听函数传参,在类组件中定义的监听函数,事件对象 e 要排在所传递参数的后面
index {index}; title {item.title}
// 传递参数
clickHandler4(id, title, event) {
console.log(id, title)
console.log('event', event) // 最后追加一个参数,即可接收 event
}
React表单
在React中,可变的状态通常保存在组件的状态属性中,并且只能用 setState() 方法进行更新
- react受控组件:类似双向数据绑定,不过在react中需要开发人员之间编写代码。比如用户在输入框input输入文字会同时更新显示到界面上
设置了输入框 input 值 value = {this.state.value}。在输入框值发生变化时我们可以更新 value。我们可以使用 onChange 事件来监听 input 的变化,并修改 value
class HelloMessage extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'Hello word!'};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
var value = this.state.value;
return
{value}
;
}
}
ReactDOM.render(
,
document.getElementById('example')
);
- 非受控组件
非受控也就意味着我可以不需要设置它的state属性,而通过ref来操作真实的DOM
父组件
调用:let dataref=this.onRef.getData();
console.log('dataref',dataref)
//调用子组件
(this.onRef)=r} />
//子组件
getData=()=>{
console.log('获取数据')
}
React父子组件通讯
通讯是单向的,数据必须是由一方传到另一方
1.父组件与子组件间的通信
在 React 中,父组件可以向子组件通过传 props 的方式,向子组件进行通讯
2.子组件传值父组件
子组件通过调用父组件传递到子组件的方法向父组件传递消息的
//子组件
this.props.listCall(list);
listCall=()=>{
}
<子组件 listCall={listCall}/>
- 父组件管理数据
- 子组件渲染数据
- 表单提交数据
react处理数据,把状态或者数据提到最高的组件上,最高级别的组件管理数据,然后向list子组件下发数据,list子组件渲染数据,input输入框下发一个函数,input组件执行这个事件,最高级别的组件负责把数据拼接好,执行完之后再在知list子组件
import React from 'react'
import PropTypes from 'prop-types'
class Input extends React.Component {
constructor(props) {
super(props)
this.state = {
title: ''
}
}
render() {
return
}
onTitleChange = (e) => {
this.setState({
title: e.target.value
})
}
onSubmit = () => {
const { submitTitle } = this.props
submitTitle(this.state.title) // 'abc'
this.setState({
title: ''
})
}
}
// props 类型检查
Input.propTypes = {
submitTitle: PropTypes.func.isRequired
}
class List extends React.Component {
constructor(props) {
super(props)
}
render() {
const { list } = this.props
return {list.map((item, index) => {
return -
{item.title}
})}
}
}
// props 类型检查
List.propTypes = {
list: PropTypes.arrayOf(PropTypes.object).isRequired
}
class Footer extends React.Component {
constructor(props) {
super(props)
}
render() {
return
{this.props.text}
{this.props.length}
}
componentDidUpdate() {
console.log('footer did update')
}
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.text !== this.props.text
|| nextProps.length !== this.props.length) {
return true // 可以渲染
}
return false // 不重复渲染
}
// React 默认:父组件有更新,子组件则无条件也更新!!!
// 性能优化对于 React 更加重要!
// SCU 一定要每次都用吗?—— 需要的时候才优化
}
class TodoListDemo extends React.Component {
constructor(props) {
super(props)
// 状态(数据)提升
this.state = {
list: [
{
id: 'id-1',
title: '标题1'
},
{
id: 'id-2',
title: '标题2'
},
{
id: 'id-3',
title: '标题3'
}
],
footerInfo: '底部文字'
}
}
render() {
return
}
onSubmitTitle = (title) => {
this.setState({
list: this.state.list.concat({
id: `id-${Date.now()}`,
title
})
})
}
}
export default TodoListDemo
setState为何使用不可变值
- 在修改状态时千万不能改变原来的状态state,修改后的数据不能影响原来的数据,是因为在react中的shouldMountUpdate声明周期数据将要改变的值与之前的数据做个比较,来决定是否更新,以setState不可变值来作为性能优化。
ssetState是同步还是异步
- setState函数中传入的是对象,是异步的,拿不到最新的值
- setState函数传入的是回调函数,是同步的,可以拿到最新的值
this.setState({
count: this.state.count + 1
}, () => {
console.log('count by callback', this.state.count) // 回调函数中可以拿到最新的 state
})
console.log('count', this.state.count) // 异步的,拿不到最新值
- setTimeout函数里面的ssetState是同步的,可以拿到最新的值
// setTimeout 中 setState 是同步的
setTimeout(() => {
this.setState({
count: this.state.count + 1
})
console.log('count in setTimeout', this.state.count)
}, 0)
- 自己定义的 DOM 事件,setState 是同步的
bodyClickHandler = () => {
this.setState({
count: this.state.count + 1
})
console.log('count in body event', this.state.count)
}
componentDidMount() {
// 自己定义的 DOM 事件,setState 是同步的
document.body.addEventListener('click', this.bodyClickHandler)
}
- state 异步更新的话,更新前会被合并,异步更新的话是传入对象,会被合并(类似 Object.assign ),执行结果只一次 +1
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
-state同步更新的话,不会被合并, 传入函数,执行结果是 +3
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
}
})
React组件生命周期
https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
react组件不是一个真正的Dom,react会生成一个虚拟Dom,虚拟Dom会经历挂载、更新,销毁阶段。
- 挂载:组件挂载到dom树上
- 更新:重新渲染页面
- 删除:组件从dom树上删除
挂载过程
constructor:调用构造函数
componentWillMount:组件将要更新
render:组件渲染到页面上
componentDidMount:组件已经加载或者已经挂载到页面上。更新过程
componentWillReceiveProps:组件将要接受参数
shouldComponentUpdata:组件是否应该更新,必须retruntrue或者false,true表示更新,false表示不更新。
componentWillUpdata:组件将要更新
render:组件渲染到页面
getSnaphotBeforeUpdata:获取截图在更新前
componentWillUpdata:组件已经更新卸载过程
componentWillUnmount:组件讲要卸载
React基本使用总结
- JSX基本使用
- 条件渲染条件判断
- 列表渲染
- setState
- 生命周期
- 事件
- 表单
- 组件和props(组件之间的通讯)
React函数组件和calss组件的区别
- 纯函数,输入props,输出JSX
- 没有实例,没有生命周期,没有state
- 不能扩展其他方法
什么是react非受控组件
- 非受控组件
- ref
- defaultValue defaultChecked
- 手动操作DOM元素
非受控组件使用场景
- 必须手动操作DOM 元素,setSate实现不了
- 文件上传
- 某些富文本编辑器,需要传入DOM元素
注意:优先使用受控组件,符合React设计原则,必须操作DOM时,在使用非受控组件(操作DOM影响性能)
import React from 'react'
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
name: 'xiaowu',
flag: true,
}
this.nameInputRef = React.createRef() // 创建 ref
this.fileInputRef = React.createRef()
}
render() {
// input defaultValue
return
{/* 使用 defaultValue 而不是 value ,使用 ref */}
{/* state 并不会随着改变 */}
state.name: {this.state.name}
// checkbox defaultChecked
return
// file
return
}
alertName = () => {
const elem = this.nameInputRef.current // 通过 ref 获取 DOM 节点
alert(elem.value) // 不是 this.state.name
}
alertFile = () => {
const elem = this.fileInputRef.current // 通过 ref 获取 DOM 节点
alert(elem.files[0].name)
}
}
export default App
什么场景需要用React Portals
- 组件渲染到父组件以外
组件默认会按照既定层次嵌套渲染
// 正常渲染
return
{this.props.children} {/* vue slot */}
// 使用 Portals 渲染到 body 上。
// fixed 元素要放在 body 上,有更好的浏览器兼容性。
return ReactDOM.createPortal(
{this.props.children},
document.body // DOM 节点
)
React Context
Context使用场景
- 公共信息(语言、主题)如何传递给每个子组件
- 用props太繁琐
- 用redux没有必须要
import React from 'react'
// 创建 Context 填入默认值(任何一个 js 变量)
const ThemeContext = React.createContext('light')
// 底层组件 - 函数是组件
function ThemeLink (props) {
// const theme = this.context // 会报错。函数式组件没有实例,即没有 this
// 函数式组件可以使用 Consumer
return
{ value => link's theme is {value}
}
}
// 底层组件 - class 组件
class ThemedButton extends React.Component {
// 指定 contextType 读取当前的 theme context。
// static contextType = ThemeContext // 也可以用 ThemedButton.contextType = ThemeContext
render() {
const theme = this.context // React 会往上找到最近的 theme Provider,然后使用它的值。
return
button's theme is {theme}
}
}
ThemedButton.contextType = ThemeContext // 指定 contextType 读取当前的 theme context。
// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar(props) {
return (
)
}
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
theme: 'light'
}
}
render() {
return
}
changeTheme = () => {
this.setState({
theme: this.state.theme === 'light' ? 'dark' : 'light'
})
}
}
export default App
React异步加载组件
- import()
- React.lazy()
- React.Suspense
import React from 'react'
const ContextDemo = React.lazy(() => import('./ContextDemo'))
class App extends React.Component {
constructor(props) {
super(props)
}
render() {
return
引入一个动态组件
Loading... }>