基础内容
-
使用脚手架创建react项目
npx create-react-app my-app //如果报错了就使用下面命令 在npm uninstall -g create-react-app之后,我遇到了这个问题,这对我起了作用: npm cache clean --force npm cache verify npx clear-npx-cache以清除npx缓存。 npx create-react-app your-app or npx create-react-app@latest my-app --use-npm
-
创建react元素-引入react --不推荐使用createElement,推荐使用jsx
import React from 'react' let dom = react.createElement('h1',null,'111')
-
jsx语法–需要babel编译处理脚手架中已经自动配置
let dom = (<h1 className="title">111<h1>) //在jsx语法中class要写为className
-
渲染react元素-引入react-dom
import ReactDOM from 'react-dom' ReactDOM.render(dom,document.getElementById('root')) //渲染多个时使用数组渲染 ReactDOM.render([dom,dom1,dom2],document.getElementById('root'))
-
嵌入js表达式
let name = JSX let dom = (<h1 className="title">{name}<h1>)
-
条件渲染
const isLoading = true const loadData = () =>{ if (isLoading) { return <div>loading...</div> } return <div>数据加载</div> } let dom = (<h1 className='title'>{loadData()}</h1>) ReactDOM.render(dom,document.getElementById('root'))
-
列表渲染
const songs = [{id:1,name:"a"},{id:2,name:"b"},{id:3,name:"c"}] //注意要添加key,否则会报错 let dom = (<ul> {songs.map(item=><li key={item.id}>{item.name}</li>)} </ul>) ReactDOM.render(dom,document.getElementById('root'))
-
jsx样式处理
<h1 style={{color:'red',background:'yellow'}}>jsx样式</h1>//不推荐 //推荐使用类名处理样式 import './index.css'//引入css文件 let dom = (<h1 className="title">{name}<h1>) //css文件内容 .title{ color: red; text-align: center; }
组件
-
函数组件 – 函数名称必须以大写开头,必须有返回值 --无状态组件
function Hello(){ return ( <div>函数组件</div> ) } //function Hello = ()=> <div>函数组件</div>//箭头函数 ReactDOM.render(<Hello/>,document.getElementById('root'))
-
类组件 – 有状态组件
//继承组件--类组件必须有render class Hello extends React.Component{ render(){ return ( <div>函数组件</div> ) } }
-
组件抽离
//Hello.js文件内容 import React from "react" class Hello2 extends React.Component{ render(){ return ( <div>函数组件111</div> ) } } export default Hello2 //index.js文件内容 import ReactDOM from 'react-dom'; import Hello from './Hello' ReactDOM.render(<Hello/>,document.getElementById('root'))
事件处理
-
点击事件
//函数组件事件处理程序 function Hello(){ function handleClick(){ console.log("点我") } return ( <button onClick={handleClick}>函数组件111</button> ) } //类组件事件处理程序 class Hello extends React.Component{ // 事件处理程序 handleClick(){ console.log("点我") } render(){ return ( <button onClick={this.handleClick}>函数组件111</button> ) } }
-
阻止事件触发
function Hello(){ function handleClick(e){ e.preventDefault(); console.log(e) } return ( <a onClick={handleClick} href="https://www.baidu.com">函数组件111</a> ) }
状态初始化
-
class Hello extends React.Component{ constructor(){ super() this.state = { count:10 } } render(){ return ( <div>计数器{this.state.count}</div> ) } }
-
class Hello extends React.Component{ state = { count:10 } render(){ return ( <div>计数器{this.state.count}</div> ) } }
修改状态
-
要使用setState修改数据–不能直接使用this.state.count++
class Hello extends React.Component{ state = { count:10 } render(){ return ( <div>计数器{this.state.count} <button onClick={()=>{ this.setState({ count:this.state.count +1 }) }}>+1</button> </div> ) } }
-
this指向
class Hello extends React.Component{ state = { count:10 } render(){ //render中的this指向组件实例 return ( <div>计数器{this.state.count} <button onClick={()=>{//箭头函数中的this指向render,this指向所处的对象 this.setState({ count:this.state.count +1 }) }}>+1</button> </div> ) } }
class Hello extends React.Component{ state = { count:10 } onIncrement(){ this.setState({//报错。。。this指向有问题 undefined count:this.state.count +1 }) } render(){ //render中的this指向组件实例 return ( <div>计数器{this.state.count} <button onClick={this.onIncrement()}>+1</button> </div> ) } } 解决方法一 class Hello extends React.Component{ state = { count:10 } onIncrement(){ this.setState({//注意this指向,指向调用者 count:this.state.count +1 }) } render(){ //render中的this指向组件实例 return ( <div>计数器{this.state.count} <button onClick={()=>this.onIncrement()}>+1</button> </div> ) } } 解决方法二 class Hello extends React.Component{ state = { count:10 } this.onIncrement = this.onIncrement.bind(this) onIncrement(){ this.setState({//注意this指向,指向调用者 count:this.state.count +1 }) } render(){ //render中的this指向组件实例 return ( <div>计数器{this.state.count} <button onClick={this.onIncrement}>+1</button> </div> ) } } 解决方法三 class Hello extends React.Component{ state = { count:10 } this.onIncrement = this.onIncrement.bind(this) onIncrement = ()=>{ this.setState({//注意this指向,指向调用者 count:this.state.count +1 }) } render(){ //render中的this指向组件实例 return ( <div>计数器{this.state.count} <button onClick={this.onIncrement}>+1</button> </div> ) } }
表单处理
-
受控组件–表单绑定的对应的值,但react也想要控制表单的值
state = {txt:''} <input type="text" value={this.state.txt} onChange={e=>this.setState({txt:e.target.value})}/> //通过绑定value //复选框通过绑定checked 也可以通过name属性多项处理
-
非受控组件要使用ref
//创建Ref this.txtRef = React.createRef() <input type="text" ref={this.txtRef} /> console.log(this.txtRef.current.value)
组件通讯
-
props–可以传递函数,表达式,默认为字符串–不能修改props
<Hello name="jack" age={19} /> function Hello(props){ console.log(props) return ( <div>{props.name}</div> ) } class Hello extends React.Component(){ constructor(props){ //写了constructor之后就要传递props到supre super(props) } render(){ return ( <div>{props.name}</div> ) } }
-
父组件给子组件传递数据
class Parent extends React.Component{ state = { name:"aaa" } render(){ return ( <div> <Child name={this.state.name} /> </div> ) } } class Child extends React.Component{ render(){ return ( <div className="child"> <p>子组件,接收父组件的值{props.name}</p> </div> ) } }
-
子组件传递数据给父组件
class Parent extends React.Component{ state = { parentMsg = '' } getChildMsg = data =>{ console.log('子组件传递的数据',data) this.setState = { parentMsg:data //将子组件的数据赋值给父组件 } } render(){ return ( <div> <Child getMsg={this.getChildMsg} /> </div> ) } } class Child extends React.Component{ state = { msg:"子组件的数据" } handleChick = ()=>{ this.props.getMsg(this.state.msg)//传递数据 } render(){ return ( <div className="child"> 子组件<button onClick={this.handleChick}> </div> ) } }
-
兄弟组件传递值 – 将数据共享到同一个父组件中
class Parent extends React.Component{ state = { count: 0 } //修改数据 setCount = ()=>{ this.setState({ count: this.state.count +1 }) } render(){ return ( <div> <Child1 count={this.state.count} /> <Child2 setCount={this.setCount}/> </div> ) } } const Child1 = props =>{ return <h1>计数器: </h1> } const Child2 = props =>{ return <button onClick={()=>props.setCount()}>计数器: </button> }
-
多层组件嵌套传递数据–Context
const {Provider, Consumer} = React.createContext(); <Provider value={/*共享的数据*/}> 父组件内容 /*里面可以渲染对应的内容*/ </Provider> <Consumer> 子组件内容 {value => /*根据上下文 进行渲染相应内容*/} </Consumer>
-
children属性 类似于vue中的作用域插槽
const App = props=>{ return( <h1>组件标签的子节点</h1> {props.children} ) } ReactDOM.render(<App><p>子节点</p></App>,document.getElementById('root'))
-
prop校验–创建类型 array,bool,func,number,object,string
也可以指定react元素类型 element
必填项: isRequire
特定结构的对象:shape({ })
//约束props传入的类型 安装prop-types(yarn add props-types/npm i props-types) import PropTypes from 'prop-types' const App = props=>{ const arr = props.colors const lis = arr.map(item=>{<li key={index}>{item.name}</li>}) return( <div>{lis}</div> ) } App.PropTypes = { colors: PropTypes.array add: PropTypes.func.isRequire //必填项的函数类型 option:PropTypes.shape({ colors: PropTypes.array fontSize: PropTypes.number })//特定类型 } App.defaultProps = { pageSize: 10//添加props默认值 }
组件的生命周期–新版生命周期
-
只有类组件才有生命周期–以废除几个带will的生命周期
-
创建时(挂载阶段)
constructor->render->componentDidMount constructor创建时就执行,一般用来状态初始化,为事件处理绑定this render每次组件渲染都会触发,不能调用setState componentDidMount组件挂载(完成DOM渲染)后,用来发送网络请求和DOM操作
-
更新时
new props,setState(),forceUpdate()都会触发更新 this.forceUpdate为强制更新 render->componentDidUpdate componentDidUpdate(完成DOM渲染)后,用来发送网络请求和DOM操作,要setState()时要放在if中否则会导致递归更新 componentDidUpdate(prevProps){ console.log(prevProps,'是更新前的数据',this.props,'是更新后的数据') } shouldComponentUpdate//更新时触发 class Hello extends React.Component{ shouldComponentUpdate(nextprops,nextstate){ if(...){ return false //返回false就不会重新渲染 } } }
-
卸载时
componentWillUnmount页面消失是--执行清理工作 componentWillUnmount(){ //清理定时器 clearInterval(定时器名称) }
-
不常用的钩子函数
getDerivedStateFromProps//创建和更新时触发 getSnapshotBeforeUpdat//更新时触发
高阶组件
-
创建一个函数,约定以with开头
-
参数为大写字母开头
-
函数内部创建一个类组件,提供复用代码并返回
function withMouse(WrappedComponent) { class Mouse extends React.Component { state = { x: 0, y: 0 } handleMouseMove = e => { this.setState({ x: e.clientX, y: e.clientY }) } componentDidMount() { window.addEventListener('mousemove', this.handleMouseMove) } componentWillUnmount() { window.removeEventListener('mousemove', this.handleMouseMove) } render(){ return <WrappedComponent {...this.state}></WrappedComponent> } } return Mouse } const Position = props =>( <p> 鼠标当前位置:(x:{props.x},y:{props.y}) </p> ) const img = props =>( <img src="../public/logo192.png" alt="react" style={{ position:'absolute', top:props.y - 64, left:props.x -64 }} /> ) const MousePosition = withMouse(Position) const Mouse = withMouse(img) ReactDOM.render(<MousePosition/>,document.getElementById('root'))
-
displayName–设置组件返回的名称
function withMouse(WrappedComponent) { class Mouse extends React.Component {...} Mouse.displayName = `withMouse${getDisplayName(WrappedComponent)}` return Mouse } function getDisplayName(WrappedComponent){ return WrappedComponent.dispalyName || WrappedComponent.name || 'component' }
-
props丢失问题
function withMouse(WrappedComponent) { class Mouse extends React.Component { ... render(){ return <WrappedComponent {...this.state} {...this.props}></WrappedComponent> } } return Mouse } const Position = props =>( <p> 鼠标当前位置:(x:{props.x},y:{props.y}) </p> ) const MousePosition = withMouse(Position) ReactDOM.render(<MousePosition/>,document.getElementById('root'))
setState
-
异步更新数据
-
可以多次调用setState,但只会触发一次render重新渲染,并且后面的setState不依赖前面的setState
this.setState({ count: state.count+1 }) 同时调用两次count还是为2 推荐使用 this.setState((state,props)=>{ return{ count: state.count+1 } }) 同时调两次count变为3
-
第二个参数
this.setState((state,props)=>{ return{ count: state.count+1 } },()=>{ console.log(this.state.count)//为dom更新后的值 })
组件的更新
- 在父组件更新中,只会同步更新子组件中的状态,但只会渲染当前组件子树
路由
-
安装路由
yarn add react-router-dom
-
引入路由–注意在react-router-dom 6.0.1中Route要写在Routes中
import { BrowserRouter as Router,Route , Link,Routes } from 'react-router-dom' const First = ()=><p>页面一的内容</p> function App() { return ( <Router> <div> <h1>函数组件</h1> {/* 指定路由入口 */} <Link to="/first">页面一</Link> {/* 指定路由出口 */} <Routes> <Route path="/first" element={<First/>} /> </Routes> </div> </Router> ) }
-
常用路由HashRouter–url带#号和BrowserRouter
-
编程式导航在v6版本中使用useNavigate()
-
默认路由path写为’/’
-
路由匹配模式,模糊匹配path为"/a",to:"/a",to:"/a/xcv"都会匹配成功
精确匹配给Route添加exact就变为精确匹配
<Route exact path="/first" element={<First/>} />
Hooks–异步
-
useState
import {useState} from 'react' cosnt [count,setCount] = useState(0) count--为状态 setCount--为行为 useState(0)--给count赋值 修改count的值 ()=>{setCount(count + 1)}
-
Effect
import {useEffect} from 'react' useEffect(()=>{ console.log(count,'变化') return ()=>{ console.log('离开页面打印') } },[count]) //第二个参数count表示只有count改变时才执行return的方法,若第二个参数为[]表示页面跳转才执行 useEffect相当于componentDIDMount和componentDidUpdate两个生命周期
-
useContext–用于组件传值
import React,{useState,createContext,useContext} from 'react'; const CountContext = createContext(); function Counter(){ let count = useContext(CountContext) return (<h2>{count}<h2>) } function demo(){ const [count,setCount] = useState(0) return ( <div> <p>{count}</p> <button onClick={()=>{setCount(count + 1)}}></button> <CountContext.provider value={count}> <Counter/> </CountContext.provider> </div> ) }
-
useReducer
import {useReducer} from 'react' function ReducerDemo(){ const [count,dispatch] = useReducer((state,action)=>{ switch(action){ case 'add': return state + 1 case 'sub': return state - 1 default: return state } }) return( <div> <p>{count}</p> <button onClick={()=>{dispatch('add')}}></button> <button onClick={()=>{dispatch('sub')}}></button> </div> ) } 根据useReducer和父子组件传值还能实现redux
-
useMemo–缓存一个值,解决子组件重复执行
const actionname = useMemo(()=>{actionname(name),[name]})
-
useRef–获取dom
const inputEl = useRef(null) <input ref={inputEl} type="text"> console.log(inputEl,'获取input的dom节点')
-
useCallback–缓存一个函数
const [size,setSize] = useState({ width:document.documentElement.clientWidth, height:document.documentElement.clientHeight }) const onResize = useCallback(()=>{ setSize({ width:document.documentElement.clientWidth, height:document.documentElement.clientHeight }) },[])
-
自定义hooks
function useWinSize(){ const [size,setSize] = useState({ width:document.documentElement.clientWidth, height:document.documentElement.clientHeight }) const onResize = useCallback(()=>{ setSize({ width:document.documentElement.clientWidth, height:document.documentElement.clientHeight }) },[]) return size } function demo(){ const size = useWinSize() return (<div>{size}</div>) }
React.memo
const demo = memo(params)=>{
}//控制何时重新渲染组件
React.pureComponent
class demo extends PureComponent//子组件不跟着父组件渲染
Redux
yarn add redux-app
yarn add redux
yarn add react-