1、JSX
1.1、JSX中使用js表达式
-
1)、语法
-
{js表达式}
-
const name = 'zll' function App() { return ( <div className="App"> react-{name} </div> ); } export default App; // 效果: <div class="App">react-zll</div>
-
-
2)、可以使用的表达式
- 字符串、数值、布尔值、null、undefined、object( [] / {} )
- 1 + 2、‘abc’.split(‘’)、[‘a’, ‘b’].join(‘-’)
- fn()
-
3)、特别注意
- if 语句/ switch-case 语句/ 变量声明语句,这些叫做语句,不是表达式,不能出现在
{}
中!!
- if 语句/ switch-case 语句/ 变量声明语句,这些叫做语句,不是表达式,不能出现在
1.2、JSX列表渲染
-
1)、语法
-
使用
map
方法 -
const songs = [ { id: 1, name: '痴心绝对' }, { id: 2, name: '像我这样的人' }, { id: 3, name: '南山南' } ] function App() { return ( <div className="App"> { songs.map(i => <div key={i.id}>{i.name}</div>) } </div> ); } // 效果 <div class="App"> // <div>痴心绝对</div> // <div>像我这样的人</div> // <div>南山南</div> // </div>
-
注意:需要为遍历项添加
key
属性
-
1.3、JSX条件渲染
-
1)、语法
-
使用
三元表达式
和逻辑运算符
-
const flag = false function App() { return ( <div className="App"> <h5>三元</h5> {flag?<div>zll</div>:<div>cjy</div>} <h5>&&</h5> {flag && <div>zll</div>} <h5>||</h5> {flag || <div>cjy</div>} </div> ); } // 效果 // <div class="App"> // <h5>三元</h5> // <div>cjy</div> // <h5>&&</h5> // <h5>||</h5> // <div>cjy</div> // </div>
-
1.4、JSX样式处理
-
1)、语法
-
行内样式
-
const styleObj = { color:'red', fontSize:'32px' } function App() { return ( <div className="App"> <div style={ styleObj }>cjy</div> </div> ); }
-
类名
-
<div style={ styleObj } className='cjy_div'>cjy</div>
-
<div style={ styleObj } className={ getClassName() }>cjy</div>
-
1.5、JSX注意事项
-
JSX必须有一个根节点,如果没有根节点,可以使用
<></>
(幽灵节点)替代 -
所有标签必须形成闭合,成对闭合或者自闭合都可以
-
JSX中的语法更加贴近JS语法,属性名采用驼峰命名法
class -> className
for -> htmlFor
-
JSX支持多行(换行),如果需要换行,需使用
()
包裹,防止bug出现
2、react组件基础
2.1、函数组件
- 使用 JS 的函数(或箭头函数)创建的组件,就叫做
函数组件
2.1.1、语法demo
// 定义函数组件
function HelloFn () {
return <div>这是我的第一个函数组件!</div>
}
// 定义类组件
function App () {
return (
<div className="App">
{/* 渲染函数组件 */}
<HelloFn />
<HelloFn></HelloFn>
</div>
)
}
export default App
- 约定说明
- 组件的名称必须首字母大写,react内部会根据这个来判断是组件还是普通的HTML标签
- 函数组件必须有返回值,表示该组件的 UI 结构;如果不需要渲染任何内容,则返回 null
- 组件就像 HTML 标签一样可以被渲染到页面中。组件表示的是一段结构内容,对于函数组件来说,渲染的内容是函数的返回值就是对应的内容
- 使用函数名称作为组件标签名称,可以成对出现也可以自闭合
2.1.2、事件绑定
-
1)、 如何绑定事件
-
on + 事件名称 = { 事件处理程序 } ,比如:
<div onClick={ onClick }></div>
-
注意点:react事件采用驼峰命名法,比如:onMouseEnter、onFocus
-
demo
// 函数组件 const HelloFn = () => { const sayHello = (e,str) => { console.log('hello',str) console.log('事件对象', e) } return ( <button onClick={(e)=>sayHello(e, 'cjy')}>say hello</button> ) } function App() { return ( <div className="App"> <HelloFn></HelloFn> </div> ); } export default App;
-
2.2、类组件
- 使用 ES6 的 class 创建的组件,叫做类(class)组件
2.2.1、语法demo
// 引入React
import React from 'react'
// 定义类组件
class HelloC extends React.Component {
render () {
return <div>这是我的第一个类组件!</div>
}
}
function App () {
return (
<div className="App">
{/* 渲染类组件 */}
<HelloC />
<HelloC></HelloC>
</div>
)
}
export default App
-
约定说明
- 类名称也必须以大写字母开头
- 类组件应该继承 React.Component 父类,从而使用父类中提供的方法或属性
- 类组件必须提供 render 方法render 方法必须有返回值,表示该组件的 UI 结构
2.2.2、事件绑定
-
与函数组件绑定方式一致
-
demo
// 类组件 class HiFn extends React.Component { sayHi = (e,str) => { console.log('hi',str) console.log('事件对象', e) } render(){ return ( <button onClick={(e)=>this.sayHi(e,'cjy')}>say hi</button> ) } } function App() { return ( <div className="App"> <HiFn/> </div> ); } export default App;
-
类组件中,访问this为undefined
class HiFn extends React.Component { hClick(){ console.log('hClick',this) // undefined } render(){ return ( <> <button onClick={this.hClick}>this</button> </> ) } } function App() { return ( <div className="App"> <HiFn/> </div> ); } export default App;
- 为啥是undefined?
- 1、this隐式丢失,使用默认绑定(全局对象)
- 2、严格模式,禁止this关键字指向全局对象。会用undefined代替。
- this修正方案
- 方案一:显示绑定
- 方案二:剪头函数(this指向创建时的上下文)
- 更多关于this
- 为啥是undefined?
3、组件状态
在React hook出来之前,函数式组件是没有自己的状态的。
3.1、读取状态
-
通过class的实例属性state来初始化
-
state的值是一个对象结构,表示一个组件可以有多个数据状态
-
通过this.state来获取状态
// 引入React import React from 'react' class InitState extends React.Component { state = { count: 0 } render(){ return <div>01、组件状态 {this.state.count} </div> } } export default InitState
3.2、修改状态
- 语法
this.setState({ 要修改的部分数据 })
- setState方法作用
-
- 修改state中的数据状态
- 更新UI
-
注意事项
不要直接修改state中的值,必须通过setState方法进行修改// 引入React import React from 'react' const styleObj = { marginLeft:'20px' } class ChangeState extends React.Component { state = { count: 0, name:'cjy' } change = () => { this.setState({ count: this.state.count + 1 }) } render(){ return <div> 02、组件状态 {this.state.count} {this.state.name} <button style={styleObj} onClick={this.change}>修改状态</button> </div> } } export default ChangeState
3.3、状态不可变
不要直接修改状态,而时使用setState方法修改状态
直接修改,虽然有时会生效,但是许多情况会直接报错,而且严格的状态管理会避免很多莫名其妙的bug。
3.4、表单处理
受控组件和非受控组件
3.4.1、受控组件
什么是受控组件?
input框自己的状态被React组件状态控制
React组件的状态的地方是在state中,input表单元素也有自己的状态是在value中,React将state与表单元素的值(value)绑定到一起,由state的值来控制表单元素的值,从而保证单一数据源特性
-
实现步骤
以获取文本框的值为例,受控组件的使用步骤如下:
- 在组件的state中声明一个组件的状态数据
- 将状态数据设置为input标签元素的value属性的值
- 为input添加change事件,在事件处理程序中,通过事件对象e获取到当前文本框的值(
即用户当前输入的值
) - 调用setState方法,将文本框的值作为state状态的最新值
-
代码
// 引入React import React from 'react' const styleObj = { marginLeft:'20px' } class ControlledComponent extends React.Component { state = { name:'cjy' } change = (e) => { this.setState({ name: e.target.value }) } render(){ return (<div> 03、受控组件 <input style={styleObj} value={this.state.name} onChange={this.change} /> <span style={styleObj} >{this.state.name}</span> </div>) } } export default ControlledComponent
3.4.2、非受控组件
什么是非受控组件?
非受控组件就是通过手动操作dom的方式获取文本框的值,文本框的状态不受react组件的state中的状态控制,直接通过原生dom获取输入框的值
-
实现步骤
- 导入
createRef
函数 - 调用createRef函数,创建一个ref对象,存储到名为
iptRef
的实例属性中 - 为input添加ref属性,值为
iptRef
- 在按钮的事件处理程序中,通过
iptRef.current
即可拿到input对应的dom元素,而其中iptRef.current.value
拿到的就是文本框的值的值
- 导入
-
代码
// 引入React import React, { createRef } from 'react' const styleObj = { marginLeft:'20px' } class UnControlledComponent extends React.Component { iptRef = createRef() state = { iptv:'' } change = () => { this.setState({ iptv: this.iptRef.current.value }) } render(){ return (<div> 04、非受控组件 <span style={styleObj} >{this.state.iptv}</span> <input style={styleObj} ref={this.iptRef} /> <button style={styleObj} onClick={this.change}>click</button> </div>) } } export default UnControlledComponent
4、组件通信
4.1、父传子
-
实现步骤
- 给子组件标签
添加属性
值为 state中的数据 - 子组件中通过
props
接收父组件中传过来的数据- 类组件使用this.props获取props对象
- 函数式组件直接通过参数获取props对象
- 给子组件标签
-
props注意
1. props是只读对象(readonly)
根据单项数据流的要求,子组件只能读取props中的数据,不能进行修改
2. props可以传递任意数据
数字、字符串、布尔值、数组、对象、
函数、JSX
-
代码
import React from 'react' const faStyle = { background:'#f00' } const sonStyle = { background:'#0f0', flex:'1' } const fSonStyle = { background:'#00f', flex:'1' } const flexStyle = { display:'flex' } // 类组件使用this.props获取props对象 class CSon extends React.Component { render(){ return ( <div style={sonStyle}> <p>I m CSon</p> <p>{this.props.msg}</p> </div> ) } } // 函数式组件直接通过参数获取props对象 const FSon = (props) => { return ( <div style={fSonStyle}> <p>I m FSon</p> <p>{props.msg}</p> </div> ) } class FatherSon extends React.Component { render(){ return ( <> <p>01、父传子</p> <div style={faStyle}> <p>I m father</p> <div style={flexStyle}> <CSon msg="hello!"></CSon> <FSon msg={"hi!"}></FSon> </div> </div> </> ) } } export default FatherSon
4.2、子传父
-
实现步骤
- 父组件提供一个回调函数 - 用于接收数据
- 将函数作为属性的值,传给子组件
- 子组件通过props调用 回调函数
- 将子组件中的数据作为参数传递给回调函数
-
代码
import React from 'react' const faStyle = { background:'#f00' } const sonStyle = { background:'#0f0', flex:'1' } const fSonStyle = { background:'#00f', flex:'1' } const flexStyle = { display:'flex' } // 类组件使用this.props获取props对象 class CSon extends React.Component { say = ()=>{ this.props.cb('hello,FSon!') } render(){ return ( <div style={sonStyle}> <p>I m CSon</p> <button onClick={this.say}>say</button> </div> ) } } // 函数式组件直接通过参数获取props对象 const FSon = (props) => { console.log(props) const say = () => { props.cb('hi,CSon!') } return ( <div style={fSonStyle}> <p>I m FSon</p> <button onClick={say}>say</button> </div> ) } class FatherSon extends React.Component { state = { msg:"hello world!" } cb = (msg) => { this.setState({ msg }) } render(){ return ( <> <p>02、子传父</p> <div style={faStyle}> <p>I m father</p> <p>{this.state.msg}</p> <div style={flexStyle}> <CSon cb={this.cb}></CSon> <FSon cb={this.cb}></FSon> </div> </div> </> ) } } export default FatherSon
4.3、兄弟组件通信
-
实现过程
- 子传父
- 父传子
-
代码
import React from 'react' const faStyle = { background:'#f00' } const sonStyle = { background:'#0f0', flex:'1' } const fSonStyle = { background:'#00f', flex:'1' } const flexStyle = { display:'flex' } // 类组件使用this.props获取props对象 class CSon extends React.Component { say = ()=>{ this.props.cb('CSon') } render(){ return ( <div style={sonStyle}> <p>I m CSon</p> <button onClick={this.say}>say</button> </div> ) } } // 函数式组件直接通过参数获取props对象 const FSon = (props) => { return ( <div style={fSonStyle}> <p>I m FSon</p> <p>hello {props.name}</p> </div> ) } class FatherSon extends React.Component { state = { name:"father" } cb = (name) => { this.setState({ name }) } render(){ return ( <> <p>03、兄弟通信</p> <div style={faStyle}> <p>I m father</p> <p>{this.state.msg}</p> <div style={flexStyle}> <CSon cb={this.cb}></CSon> <FSon name={this.state.name}></FSon> </div> </div> </> ) } } export default FatherSon
4.4、跨级通信
-
实现过程,使用
context
-
代码
-
04-grandfater-grandson.jsx
import React from 'react' import CGrandSon from './04-cGrandSon' import FGrandSon from './04-fGrandSon' export const CContext = React.createContext('cConsumer666') export const FContext = React.createContext('fConsumer666') const flexStyle = { display:'flex', background:'#f00' } const Father = () => { return ( <div style={flexStyle}> <CGrandSon /> <FGrandSon /> </div> ) } export default class GrandFatherGrandSon extends React.Component { render(){ return ( <> <p>04、跨级通信</p> {/* <CContext.Provider value={'cConsumer'}> */} <FContext.Provider value={'cConsumer'} > <Father></Father> </FContext.Provider> {/* </CContext.Provider> */} </> ) } }
-
04-cGrandSon.jsx
import React from 'react' import { CContext } from './04-grandfater-grandson' const style = { background:'#0f0', flex:'1' } export default class CGrandSon extends React.Component { render(){ return ( <CContext.Consumer> {value => ( <div style={style}>I am {value}</div> )} </CContext.Consumer> ) } }
-
04-fGrandSon.jsx
import { FContext } from './04-grandfater-grandson' const style = { background:'#00f', flex:'1' } const FGrandSon = () => { return ( <FContext.Consumer> {value => ( <div style={style}>I am {value}</div> )} </FContext.Consumer> ) } export default FGrandSon
-
5、组件进阶
5.1、children属性
- children属性是什么
- 每个组件都可以获取到
props.children
。它包含组件的开始标签和结束标签之间的内容。
- 每个组件都可以获取到
- children属性可以是什么
- 普通文本
- 普通标签元素
- 函数 / 对象
- JSX
5.1.1、children的使用
-
代码
- 01-children.jsx
const style = { width:'400px', height:'400px', backgroundColor:'#ccc' } const headrStyle = { width:'100%', height:'100px', backgroundColor:'#cc0' } const contentStyle = { width:'100%', height:'200px', } const footerStyle = { width:'100%', height:'100px', backgroundColor:'#0cc' } export default function UseChildren (props) { return ( <div style={style}> <div style={headrStyle}> {props.children.header?.() || '标题内容'} </div> <div style={contentStyle} > {props.children.default?.({name:'default-content'}) || '正文内容'} </div> <div style={footerStyle} > {props.children.footer?.() || '底部内容'} </div> </div> ) }
- App.js
<h3>children属性</h3> <UseChildren> {{ // header(){ // return '我是标题内容' // }, default(scope){ return ( <> <p>我是默认内容</p> <p>{scope.name}</p> </> ) }, footer(){ return '我是底部内容' }, }} </UseChildren>
-
和vue中插槽效果一样样的。vue中jsx插槽语法差不多就这样。
5.2 、props校验
-
语法
- 安装依赖
prop-types
import PropTypes from 'prop-types'
- 使用
组件名.propTypes = {}
给组件添加校验规则
- 安装依赖
-
校验规则
- https://reactjs.org/docs/typechecking-with-proptypes.html
-
代码 01-propTypes.jsx
import React, { useState } from 'react'; import PropTypes from 'prop-types' // 类组件 class Ccomponent extends React.Component { render(){ return ( <> 类组件-{this.props.name} </> ) } } Ccomponent.propTypes = { name: PropTypes.string }; // 函数组件 const Fcomponents = (props) => { return ( <> 函数组件-{props.name} </> ) } Fcomponents.propTypes = { name: PropTypes.string.isRequired }; const UsePropTypes = () => { const [name, setName] = useState('cjy'); const [name1, setName1] = useState(''); const set = (e) => { setName(666) setName1(777) } return ( <> <p>01、props校验</p> <div> <button onClick={(e)=>set(e)}>change</button> </div> <Ccomponent name={name}></Ccomponent> <Fcomponents name={name1}></Fcomponents> </> ) } export default UsePropTypes
5.3、props默认值
-
语法 https://reactjs.org/docs/typechecking-with-proptypes.html
-
代码 02-propDefaultValue.jsx
import React, { useState } from 'react'; import PropTypes from 'prop-types' // 类组件 class Ccomponent extends React.Component { static defaultProps = { name: 'cjy' } render(){ return ( <> 类组件-{this.props.name} </> ) } } Ccomponent.propTypes = { name: PropTypes.string }; // 函数组件 const Fcomponents = ({name = 'cjy'}) => { return ( <> 函数组件-{name} </> ) } Fcomponents.propTypes = { name: PropTypes.string }; const UsePropDefaultValue = () => { const [name, setName] = useState(''); const [name1, setName1] = useState(''); const set = (e) => { setName('666') setName1('777') } return ( <> <p>02、props默认值</p> <div> <button onClick={(e)=>set(e)}>change</button> </div> <Ccomponent ></Ccomponent> <Fcomponents ></Fcomponents> </> ) } export default UsePropDefaultValue
6、生命周期
https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
参考链接:https://www.yuque.com/fechaichai/qeamqf/xbai87#80c6b3c5