React 基础 教程 涵盖各个版本

jsx 语法

使用一个父节点包裹

jsx 中不能一次性返回零散的多个节点,如果有多个请包涵在一个节点中。

注释

jsx 中用{/* */}的注释形式

  return (
      // jsx 外面的注释
      <div>
          {/* jsx 里面的注释 */}
          <p>hello world</p>
      </div>
  )

样式

对应 html 的两种形式,jsx 的样式可以这样写:
css样式:<p className="class1">hello world</p>,注意这里是className,而 html 中是class
内联样式:<p style={{display: 'block', fontSize: '20px'}}>hello world</p>,注意这里的{{...}},还有fontSize的驼峰式写法

ref 相当于vue中的ref

let myRef = React.createRef()
<input ref={myRef} />
this.myRef.current.value 获取值

事件

拿 click 事件为例,要在标签上绑定 click 事件,可以这样写

class Hello extends React.Component {
  render() {
      return (
        <p onClick={this.clickHandler.bind(this)}>hello world</p>
      )
  }

  clickHandler(e) {
   	// e 即js中的事件对象,例如 e.preventDefault()
    // 函数执行时 this 即组件本身,因为上面的 .bind(this)
    console.log(Date.now())
  }
}

<button onClick={handler} />
handler = ()=>{
 	这里也能使用this,和bind(this)是相同的意思
}

传递参数 事件的两种写法

第一种bind
onClick={this.handle.bind(this,index)}
第二种箭头函数
onClick={()=>{this.handle(index)}}

注意,onClick是驼峰式写法,以及.bind(this)的作用


循环

在 jsx 中使用循环,一般会用到Array.prototype.map(来自ES5标准)

class Hello extends React.Component {
  render() {
    const arr = ['a', 'b', 'c']
    return (
      <div>
        {arr.map((item, index) => {
             return <p key={index}>this is {item}</p>
         })}
      </div>
    )
  }
}

注意,arr.map是包裹在{}中的,key={index}有助于React的渲染优化,jsx中的{}可放一个可执行的 js 程序或者变量

操作state中的数组时,切记使用arr.slice() 或者 let newList = [...this.state.list]的方法对数组进行复制一份,避免直接操作原数组,对数组添加和删除也是操作复制出来的对象,最后this.setState({list:newList})


判断

jsx中使用判断一般会用到三元表达式(表达式也是放在{}中的),例如:

return (
  <div>
    <p>段落1</p>
    {
      true 
      ? <p>true</p>
      : <p>false</p>
      </div>
    }
  </div>
)

也可以这样使用:

<p style={{display: true ? 'block' : 'none'}}>hello world</p>

条件渲染

{this.state.list.length && <p>111</p>}
className={this.state.list.length>0?'':''}

富文本展示 dangerouslySetInnerhtml

<span dangerouslySetInnerHtml={{__html:item.txt}}</span>

state 状态

只能在当前组件内使用,外部无法获取,state 相当于vue中的data

第一种直接定义
state = {}
第二种通过constructor定义
constructor {
	super()
	this.state = {}
}
class Hello extends React.Component {
 constructor(props, context) {
     super(props, context);
     this.state = {
         // 显示当前时间
         now: Date.now()
     }
 }
 render() {
     return (
         <div>
             <p onClick={this.clickHandler.bind(this)}>hello world {this.state.now}</p>
         </div>
     )
 }
 clickHandler() {
     // 设置 state 的值的时候,一定要用 this.setState ,不能直接赋值修改
     this.setState({
         now: Date.now()
     })
 }
}

setState扩展

setState处在同步的逻辑中,异步更新状态,更新真实Dom

setState处在异步中 同步更新状态 同步更新Dom

setState接受第二个参数,第二个参数是回调函数,状态和dom更新完就会触发

如果不用生命周期,需要结合回调函数使用
this.setState({list:list},()=>{
  new BetterScroll('.div')
})
如果在异步接口中使用,因为是同步的
this.setState({list:res.data.list})
new BetterScroll('.div')

props

如果传递非字符串类型,可以使用花括号,可以传递对象数字等非字符串的类型

render(
	<Nav title="xxx" show={true}/>
)

父组件传递参数的时候 可以通过es6展开运算符把整个对象传递下去,如下图

render(
  <Nav {...item} />
)
在子组件中获取props,最好通过解构的方式,是一个技巧
let {title,show} = this.props

props属性验证

组件名.propTypes = {title:strig,show:boolean}

推荐下面的写法

static propTypes = {title:strig,show:boolean}

props默认值

组件名.defaultProps = {title:'xxx',show:false}

推荐下面的写法

static defaultProps = {title:'xxx',show:false}

新版的react推荐使用函数式组件,代替类组件

这里没有this,可以通过props获取属性
export default function Nav (props) {}

state与props的区别

state用于组件保存控制修改自己的状态

props是让使用该组件的父组件传入属性配置组件

无状态的组件相当于木偶组件可以通过通信交流

受控组件

组件的数据渲染是否被调用者传递的props完全控制来区别受控组件与非受控组件

表单受控与非受控

在html中表单元素通常自己维护自己的状态,根据用户输入进行更新,而在react中通过组件的state控制取值的表单元素叫做受控组件

<input value={this.state.value} onChange={(evt)=>{
	this.setState({value:evt.current.value})
}}

父子组件通信

父组件通过props 子组件通过回调函数

父组件
<Inp val={this.state.value} onChanges={(vl)=>{console.log(vl)}}
子组件
<input value={this.props.val} onChange={(evt)=>{
	this.props.onchanges(evt.value)
}}

通过ref通信

const inp = React.createRef()
<Inp ref={this.inp} />

非父组件通信

一、状态提升,借用父组件的状态给兄弟组件传参,类似与父子通信

二、发布订阅,原生js原理后面的Reduce也是基于它实现的

发布就是遍历list将回调函数执行,一定要先订阅再去发布,否则什么也拿不到

let bus = {
	list:[]
	订阅
	subscrbe (callback) {this.list.push(callback)}
	发布
	publish () {
		this.list.forEach(callback=> callback&&callback())
	}
}

三、context状态树传参

const GlobalContext = React.createContext() 创建对象

在父组件用 <GlobalContext.provider value={{
	info: this.state.info,
	changeInfo: (val) => {
		this.setState({info:val})
	}
}} > 把html包起来 他是供应商
子组件 是消费者  <GlobalContext.consumer>
	{
		(value) => {
			return <div onClick={()=>{
				value.changeInfo('xx')
			}}>xxxx</div>
		}
	}
</GlobalContext.consumer>

插槽

对与组件的封装一定要留好插槽,可以使组件更加灵活 复用性更好

父组件
render(
	<div>
		<child>
			<div>111</div>
			<div>222</div>
		</child>
	</div>
)
子组件. 通过数组的形式和父组件的模版相对应,下标可以任意修改
<div>
	{this.props.children[2]}
	{this.props.children[1]}
</div>

生命周期

分为 初始化 运行中 销毁阶段,函数式组件没有生命周期

初始化

componentWillMount:render之前最后一次修改状态的机会

render:只能访问props state 表示正在渲染

componentDidMount:挂载完成 可以修改Dom 放请求

运行中

componentWillUpdate:不能修改属性状态

render 同上

componentDidUpdate (preProps, preState) {} 旧属性 旧状态

shouldComponentUpdate:组件是否应该更新,用于优化性能 return false 不能新 true 为更新

shouldComponentUpdate(nextProps,nextState){
	旧状态保存在 this.state中 新状态在 nextState
	如果多个参数要对比
	通过JSON.stringfly(this.state)与 nextState字符串直观比较
}

使用场景

如果一个页面要渲染100个盒子,每次修改其中一个盒子的样式,会发现react会进行所有的渲染对比,这时候就需要SCU只更新当前盒子

shouldComponentUpdate(nextProps,nextState){	
	if(this.props.current == this.props.index || nextProps.current == nextProps.index){
		return true
	}
	return false
}

componentWillReceviceProps:父组件修改属性触发 最先获取父组件传来的属性,可以利用属性进行ajax,把属性转化为自己的状态或者逻辑处理,在这里获取 this.props.xxx还是旧值

销毁阶段

componentWillUnmount:释放定时器、监听函数

老生命周期的问题

  1. componentWillMount 在ssr中会多次被调用,在这里绑定事件会无法解绑回内存泄漏,不安全,逐步废弃
  2. componentWillReceiveProps 对外部频繁更新,传多个不同的props会导致不必要的异步请求
  3. componentWillUpdate 会导致状态不准,因为它在render之前会被render干扰

新生命周期

getDerivedStateFromProps

第一次初始化及后续更新,返回一个对象作为新的state它是一个类方法

这个return 会和当前组件的state合并
static getDerivedStateFromProps(nextProps,nextState){
	return {name:nextState.name.substring(0,1)+'OA'}
}

它需要结合componentDidUpdate使用 可以实现ajax请求,主要用于父传子

componentDidUpdate(prevProps,prevState){
	if (this.state.name === prevState.name){return}
	else 触发逻辑请求
}

getSnapShotBeforeUpdate

==它取代了componentWillUpdate,它在render后执行,dom渲染之前返回一个值,作为componentDidUpdate的第三个参数

getSnapShotBeforeUpdate(){ return 100}
componDidUpdate(prevProps,prevState,value){ value 的值就是100}

pureComponent

与SUC有相同的效果,如果state或者props永远都变,就不适合用它

import React ,{pureComponent} from 'react'
export default class App extends pureComponent

react hooks 钩子

解决生命周期复杂,无状态组件后期又要改为有状态组件的大量修改

useState 保存组件状态

import React, {useState} from 'react'
const [name,setName] = useState('xx');
通过setName改变name值

useEffect 处理副作用

第一个参数是回调函数,第二个参数是数组,如果是空数组里面的回调函数只执行一次
类似于vue3中的watchEffect自动追踪属性
useEffect(()=>{},[])

使用时先对追踪的属性解构
let {x1,x2} = props
useEffect(() =>{},[x1,x2])

模拟生命周期从创建到销毁

useEffect(()=>{
	事件监听,定时器
	return()=>{
		负责销毁事件监听,定时器
	}
},[])

useEffect与useLayoutEffect的区别

  1. useLayoutEffect 调用机制不同,他和componentDidMount、componentDidUpdata一致,完成dom更新后马上同步调用
  2. 会阻塞页面渲染,而useEffect会在整个页面渲染完成才调用
  3. 如果需要操作dom代码就用useLayoutEffect,官方建议优先使用useEffect

useCallback 记忆函数

防止组件更新渲染,导致方法重新创建起到缓冲的作用,只有第二个参数变化了才会重新申明一次

const [text,setText] = useState('');
const handle = useCallback((ev)=>{setText(ev.value)},[])

useMemo 记忆组件

useMemo和useCallback功能相同,区别:useCallback不会执行第一个函数,而是返回给你,而useMemo会立即执行并返回结果,更适合经过函数计算得到一个确定的值,和vue中的计算属性很像

let {list,text}  = this.props
需要依赖的属性需要放在数组里
const getList = useMemo(()=>{},[list,text])

useRef

useRef相当于React.createRef()

useContext

能够简化供应商与消费者中消费者的代码

const GlobalContext = React.createContext() 创建对象

在父组件用 <GlobalContext.provider value={{
	info: this.state.info,
	changeInfo: (val) => {
		this.setState({info:val})
	}
}} > 把子组件包起来 他是供应商 </GlobalContext>

没有使用useContext

子组件 是消费者  <GlobalContext.consumer>
	{
		(value) => {
			return <div onClick={()=>{
				value.changeInfo('xx')
			}}>xxxx</div>
		}
	}
</GlobalContext.consumer>

使用useContext

不用在额外包裹标签,从回调函数中获取value
const value = useContext(GlobalContext)

useReducer

==通常他和useContext一起使用,解决父组件下多个子组件传参的问题,在父组件通过供应商把state和dispatch做为服务对外提供出去,通过useContent获取state和dispatch

import React, {useReducer} from 'react'
const reducer = (prevState,action){
	let newValue = {...prevState}
	switch(action){
		case 'min':
			return action.polyload
	}
}
const intialState = {
	count: 0
}
export default function App () {
	const [state, dispatch] = useReducer(reducer,intialState);
	return (
		<div><button onClick={()=>{ dispatch({type:'min',polyload:'2'})}}></button>
		<p>{state.count}</p>
		</div>
	)
}

自定义hooks

在打函数中提取小函数,把共享的逻辑提取出来,以use开头,相当于vue3的组合式函数

路由

import {HashRouter as Router, Route, switch} from 'react-router-dom'
单独提取一个文件 定义下面的内容

<HashRouter>
	防止页面刷新路由, exact精准定位
	<switch>
		<Route path="/home" component={Home} />
		<Redirect from="/" to="/home" exact/>
		<Route component={NotFound} />
	</swith>
</HashRouter>

嵌套路由

在home组件中定义路由,就是当前组件的子路由

render(
	<div>
		<switch>
			<Route path="" component={} />
			<Redirect from="/" to="/home" exact/>
		</switch>
	</div>
)

路由跳转

  1. 声明式导航:<navLink to=“” activeClassName=“” //>
  2. 编程式导航:this.props.history.push(‘/’)

动态路由

const history = useHistory()
<Route path="/detail/:id" component={} />
通过props.match.params.id获取参数

route的源码就是一个组件,把外部的component通过它的props获得,并作为它的子组件,所以route与component是父子关系

路由传参

  1. 需要设置动态路由 配合 history.push(`/detail/${id}') 通过props.match.params.id获取参数
  2. query传参 history.push({pathname:‘’,query: {id:id}}) ,使用props.location.query.id获取
  3. state传参 history.push({pathname:‘’,state: {id:id}}) 使用props.location.state.id获取

2、3刷新参数会丢失,推荐使用1的方法

路由拦截

import {HashRouter as Router, Route, switch} from 'react-router-dom'
<HashRouter>
	防止页面刷新路由, exact精准定位
	<switch>
		<Route path="/home" component={Home} />
		<Redirect from="/" to="/home" exact/>
		<Route path="/index" component={NotFound} render:{()=>{
			return isAuth ? <Center/> : <Redirect to="/login" /
		}} />
	</swith>
</HashRout

<Route path=“home” <Route path=“path/a” 如果第一个路由不加exact 就永远跳不到第二路由

路由模式

HashRouter,BrowserRouter,但是BrowserRouter朝后端发送请求,会被认为后端路径,会出现404的情况

解决center组件无法拿到location

通过render的props

<Route path="/index" component={NotFound} render:{(props)=>{
	return isAuth ? <Center {...props}/> : <Redirect to="/login" /
}} />

通过withRouter

const centers = withRouter(center)
export default centers

反向代理

import http-proxy-middleware
module.exports = function(app){
	app.use(
		'/api',
		createProxyMiddleware({
			target: '',
			changeOrigin: true
		})
	)
}

cssmodule

react会自动重命名,防止冲突,多用ID及class 标签选择器无效,如果不想重命名可以使用 :global(.class)

import style from './css/film.module.css'
<p className={style.active} />

flux

单向数据流的方式组合视图组件,他是一个模型,view发布用户的action dispatch收到action让state相应更新,state更新后发出一个change事件,view收到change事件更新界面

Redux

redux是flux的一种实现方案

    定义计算规则,即 reducer
    function counter(prevState = {
    		state: 0  这里可以定义默认值
    	}, action) {
    	let newState = {...prevState}
        switch (action.type) {
            case 'INCREMENT':
                return newState.state = action.payload
            case 'DECREMENT':
                return newState.state - 1
            default:
                return newState.state
        }
    }

    根据计算规则生成 store
    let store = createStore(counter)

    // 触发数据变化
    store.dispatch({type: 'INCREMENT',payload: '23'})
    store.dispatch({type: 'DECREMENT'})
	
	获取state
	store.getState().xxx

combineReducer

拆分出来的多个reducer可以进行合并

const reducer = combineReducers({cityReducer,tabReducer})

异步支持 redux-thunk(创K) 返回方法

import reduxThunk from 'redux-thunk'
const store = createStore(reducer,applyMiddleWare(reduxThunk))
在redux文件下创建action文件夹
function Action () {
	return (dispatch) =>{
		axios({}).then((res) =>{
			dispatch({type: '', payload: ''})
		})
	}
}
export default Action
useEffect(()=>{
	store.dispatch(Action());
	通过订阅
	let unSubscribe = store.subscribe(()=>{store.getState().xxx})
	return()=>{
		unSubscribe() 取消订阅
	}
})

异步支持 redux-promise 返回promise

store同时支持thunk和promise

import reduxThunk from 'redux-thunk'
import reduxPromise from 'redux-promise'

既支持返回一个函数,也支持返回一个promise对象
const store = createStore(reducer,applyMiddleWare(reduxThunk,reduxPromise))
在redux文件下创建action文件夹
async function Action () {
	let list = await axios({}).then((res) =>{
			dispatch({type: '', payload: ''})
		})
	}
	return list
}
export default Action

react-redux

提供一个高阶组件connect生成订阅和取消订阅

indexjs

挂到app上以后子组件就不用引入store了

import {provider} from 'react-deux'
import store from 'xx/store'
render(
	<provider store={store}><App/></provider>
)

如何使用呢?

const mapStateToProps = (state) =>{
	return {
		show: state.show
	}
}
const mapDispatchToProps = {
	下面都是action
	hide,
	show
}
export default connect (mapStateToProps,mapDispatchToProps)(组件名)

高阶组件 高阶函数 原理

function Kc (callback){
	var value = callback();
	return (myComponent) => {
		return (props) => {
			return (<div style="x">
			<{... value} {..props} {...obj} myComponent/></div>)
		}
	}
}

redux 持久化 redux-persist

原理本地存储
import {persiststore, persistReducer} from 'redux-persist'
import storage from 'xx'
const PersistConfig = {key: 'xx', storage}
const PersistReducer = persistReducer(PersistConfig,reducer)
const store = ({PersistReducer, xxxx})
let persistor = persiststore(store)
export {store, persistor}
再修改indexjs把组件加一层标签<PersistGate>

Immutable

每次修改一个immutable都会创建一个新的不可变的对象,在新对象上操作不会影响原对象数据,避免了deepCopy把所有节点都复制一遍的性能损耗,如果对象一个节点变化只修改这个节点和受它影响的节点

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值