本文已参与「新人创作礼」活动,一起开启掘金创作之路。
创建React项目
npm create-react-app namevite 创建react项目
基础
JSX
js的语法拓展
class使用className 而不是class
let element = <h1>this is jsx</h1>
JSX { } 包含js表达式
let element = <h1>{{1+1}}</h1>
//字符串字面量
let ele1 = <div property="string"></div>
//js表达式
let ele2 = <img src={user.avatar}/>
组件
函数组件
function Welcome(props){
return (<img src={props.user.avatar}/>)
}
class组件
(props 为this的属性)
class Welcome extends react.Component{
render(){
return (<img src={this.props.user.avatar}/>)
}
}
props只读,不可改
state
直接修改state React 不会重新调用render()去更新Dom
// 使用setState() react能检测到state数据修改了,重新调用render渲染组件
this.setState({
user:user
})
this.setState的更新可能是异步的
原因:出于性能考虑,React 可能会把多个setState()调用合并成一个调用。this.props和this.state可能会异步更新
解决state异步更新导致状态更新不及时
setState()参数传入一个函数
//返回一个执行完成的结果对象
this.setState(
function(state,props){
return {
counter:state.counter+props.number;
}
}
)
数据向下流动
通过props实现父组件对子组件的通信,数据由父组件流向子组件
生命周期
componentDidMount():组件完成挂载时,执行
componentWillUnmount():组件将要卸载时,执行
事件处理
this的指向问
回调函数中默认的this为undefined
一、在回调函数中让this指向component,用bind改变this的指向
class Change extends React.Component{
constructor(props){
super(props);
this.state = {
value:"this"
}
//bind后可以在 handleClick中使用this指向component
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
this.setState({
this:'hello world'
})
}
}
二、使用class filed语法
class Change extends React.Component{
constructor(props){
super(props);
this.state = {
value:"this"
}
}
//class filed 语法
handleClick = ()=>{
this.setState({
this:'hello world'
})
}
}
三、使用箭头函数
render(){
return (
//该方法每次render时都会重新创建匿名函数,不推荐使用
<a onClick={()=>{this.handleClick}}></a>
)
}
事件处理函数传参
//第一种方法(箭头函数)
(<a onClick={(args)=>{this.handleClick(args)}}></a>);
//第二种方法(bind)
<a onclick={this.handleClikc.bind(this,args)}>
条件渲染
//通过if实现 con1,con2分别为两个不同的组件
if(render){
return con1;
}else{
return con2;
}
//使用&&运算符实现条件渲染
{
//render = true 返回&&右边
//render = false 跳过右边
render&&(<h1> hello world</h1>)
}
//使用三目运算符实现条件渲染
render?<h1>hello world</h1>:<h1>welcome React</h1>
隐藏组件
class Header extends React.Component{
constructor(props) {
super(props);
}
render(){
if(this.props.show){
return (<h1>hello world</h1>)
}
else{
//返回null 隐藏组件,页面结构仍然有header
return null;
}
}
}
列表和key
和vue中的v-for和key
使用map来循环遍历
let number = [
{value:1,
id:1
},
{value:2,
id:2
}
]
//不建议使用index为key
const element = number.map((item,index)=>{
return <li key={item.id}>{item.value}</li>
)
//嵌入map式
function List(props){
return (
<ul>{
number.map((item,index)=>{
return <li key={item.id}>{item.value}</li>
})
}</ul>
)
}
受控组件
表单
通过令表单的value=this.state.value来实现表单和数据的绑定
class NameForm extends React.Component{
constructor(props){
super(props);
this.state = {
name:''
}
}
handleChange(event){
this.setState({
name:event.target.value
})
}
render(){
return (
//通过onchang事件实现数据的改变
<form>
<input type='text' value={this.state.name}
onChange={this.handleChange.bind(this)}></input>
<input type='submit' value="提交"></input>
</form>
)
}
}
状态提升
多个组件需要反映相同的变化数据,这时我们建议将共享状态提升到最近的共同父组件中去
组合
可以通过将组件以props参数的形式传入到另一个组件中去
//组件1
function FirstComponent (props){
return (
<div>
{props.children}
</div>
)
}
//组件2
function
context
解决跨组件层级props转递效率问题
首先创建1个Context对象,当React渲染1个订阅了Context对象的组件,
这个组件会从组件树中离自身最近的那个匹配的Provider读取到当前的context值。
每个Context都返回1个React组件,允许消费组件订阅context的变化。
ContextType只能在类组件中使用,
1个组件如果有多个消费者,ContextType只对其中1个有效,
也就是ContextType只能有1个(所以推荐使用Provider和Consumer形式)
创建context
const Mycontext = React.createContext(initalValue)
Provider 提供context
let value = {...data}
<Mycontext.Provider value = {value}>
<\Mycontext.Provider>
消费context的三种方式
consumer组件,contextType,useContext
consumer组件
consumer组件内必须是一个函数
<Mycontext.Consumer>
{
value =>(
<div>{value.data}</div>
)
}
<\Mycontext.Comsumer>
contextType
只能在类组件中使用
给类组件添加类变量contextType,通过实例变量this.context获取provider的value值
class ...{
static contextType = Mycontext;
return (
<div>{this.context.data}</div>
)
}
useContext
useContext hook 只能在函数组件中使用
// value 即获取到Mycontext中provider生产的数据
const value = useContext(Mycontext)
ref
用于标记dom元素,获取dom元素
使用方法
一、React.createRef()
class RefText extends React.Component{
constructor(){
//创建一个ref,用来保存对dom节点的引用 目前current为null
this.inputRef = React.createRef();
}
render(){
return (
//mounted后 this.inputRef.current指向该dom节点
<input ref={this.inputRef}></input>
)
}
}
二、refs 回调函数
ref属性传入一个函数
class RefTest extends React.Component{
constructor(){
this.testRef = null;
}
setRef = (el)=>{
//testRef引用元素
this.testRef = el;
}
render(){
return (
//若传入为props中的参数,可以暴露该dom给父组件
<input ref={this.setRef}></input>
)
}
}
高阶组件
组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件本质上是一个函数,处理加工一个组件,返回一个新的组件。
//高阶组件
function withH(HComponent,data){
//包装原组件,返回包装后的组件
return class extends React.Component{
constructor(props){
super(props);
this.state = {
data:`${data}haha`
}
}
render(){
return (
<HComponent Data={this.state.data}></HComponent>
)
}
}
}
//原被包装组件
class Ha extends React.Component{
constructor(props){
super(props);
this.data = this.props.Data;
}
render(){
return (
<h1>{this.data}</h1>
)
}
}
//调用函数生成新组件实例
const HaHa = withH(Ha,"nihao")
被包装组件接收来自容器组件的所有 prop,同时也接收一个新的用于 render 的
dataprop
render props
render prop 是一个用于告知组件需要渲染什么内容的函数 prop
/*render props*/
class AComponent extends React.Component{
render(){
return (<h2>渲染组件{this.props.mouse}</h2>)
}
}
class Container extends React.Component{
constructor(props){
super(props);
}
render(){
return(
<div>
<h1>Container</h1>
{this.props.render("hello")}
</div>
)
}
}class Finall extends React.Component{
render(){
return(
<Container render={(mouse) => (
<AComponent mouse={mouse} />
)}/>
)
}
}
层级
Fragments
类似于vue中的template,有些许不同,Fragment可以包含多子个元素,无参数时:<></>
虚拟dom 和diff算法
react 路由
BrowserRouter 和 HashRouter
BrowserRouter使用的是h5的history API,不兼容ie9以下
HashRouter使用URL的hash值
路径表现形式不一样
BrowserRouter的路径中没有#,如:localhost:3000/demo/testHashRouter的路径包含#,如:localhost:3000/#/demo/test
刷新后对路由 state 参数的影响
BrowserRouter没有影响,因为state保存在history对象中。HashRouter刷新后会导致路由state参数的丢失!
备注:HashRouter 可以用于解决一些路径错误相关的[问题]
//react-route-dom6
import {BrowserRouter,Routes,Route,Link} from 'react-router-dom'
//BrowserRouter 所有路由都嵌套在其中(也可以是HashRouter)
<BrowserRouter>
//路由链接Link
<nav }>
<Link to="/"}>Home</Link>
<Link to="/about"}>About</Link>
<Link to="/post">Post</Link>
</nav>
//Routes 取代Router5中的 Switch
<Routes>
// Route 渲染组件的路由
// element 指定组件(router5中的component)
<Route path="/" element={<Home/>}/>
<Route path="/about" element={<About/>}/>
// 嵌套路由子级路由
<Route path="/post" element={<Post/>}>
<Route path='/post' element={<PostList/>} />
//路由参数
<Route path=':slug' element={<PostText/>} />
</Route>
</Routes>
</BrowserRouter>
<div>
<h2>Blogs</h2>
// 子级路由占位组件 Outlet
<Outlet />
</div>
// useParams 获取路由传递的参数
const {slug} = useParams();
antd (UI组件库)
引入
安装antd npm i antd --save
按需引入
import 模块引入
import { Button } from 'antd';
import 'antd/dist/antd.css';