React学习2

目录

1.React原理 

1.1 setState

 推荐语法

 setState的第二个参数

1.2 JSX的转化原理

 1.3 组件更新机制

 1.4 组件性能优化

1.4.1减轻state

 1.4.2  避免不必要的重新渲染

1.5 纯组件 

2.虚拟DOM和diff算法

2.1 虚拟DOM

 3.React路由

3.1 安装router

(下面BrowserRouter as Router 是给BrowserRouter起别名叫做Router)

3.3 Router组件包裹整个应用

3.4 使用Link组件作为导航菜单(路由入口)

 3.4.1 NavLink组件

3.5 使用Router组件配置路由规则和要展示的组件(路由出口)

3.6 常用组件说明

3.7 路由执行过程 

 3.8 编程式导航

 3.9 默认路由

 3.10 匹配模式

3.10.1 模糊匹配模式

 3.10.2 精确匹配

 3.11 Switch组件

3.12 Redirect组件

3.13 向路由组件传递参数

3.13.1 params参数

  3.13.2 search参数

3.13. 3 state参数

3.14 withRouter

4 ref 

5 redux

5.1 redux是什么?

5.2 什么情况下需要使用redux

5.3  redux的三个核心概念

5.3.1 action

5.3.2  reducer

5.3.3 store

 5.4 redux 核心API

5.4.1  createstore()

5.4.2. store对象 

 5.4.3  applyMiddleware()

5.4.4 combineReducers()

5.5 redux异步编程

5.5.1理解:

5.6 使用步骤

5.7 react-redux

5.7.1 求和案例_react-redux基本使用

5.7.2 求和案例_react-redux优化

5.7.3  求和案例_react-redux数据共享版

5.7.4 求和案例_react-redux开发者工具的使用 

5.7.5  求和案例_react-redux最终版

6.拓展

 6.1 lazyload

6.2 Hooks

6.2.1 React Hook/Hooks是什么?

6.2.2 State Hook

6.2.3 Effect Hook

6.2.4 Ref Hook

6.3 Fragment

6.4 Context


1.React原理 

1.1 setState

多次setState只会进行一次render,会把所有setState合并再进行render

 推荐语法

用两个setState会不一样,会加2,而原来的语法则是只加1

 setState的第二个参数

this.setState(
      (state, props) => {
        return {
          count: state.count + 1
        }
      },
      // 状态更新后并且重新渲染后,立即执行:
      () => {
        console.log('状态更新完成:', this.state.count) // 2
        console.log(document.getElementById('title').innerText)
        document.title = '更新后的count为:' + this.state.count
      }
    )
    console.log(this.state.count) // 1

1.2 JSX的转化原理

 1.3 组件更新机制

 1.4 组件性能优化

1.4.1减轻state

 1.4.2  避免不必要的重新渲染

class App extends React.Component {
  state = {
    count: 0
  }

  handleClick = () => {
    this.setState(state => {
      return {
        count: state.count + 1
      }
    })
  }

  // 钩子函数
  shouldComponentUpdate(nextProps, nextState) {
    // 返回false,阻止组件重新渲染
    // return false

    // 最新的状态:
    console.log('最新的state:', nextState)
    // 更新前的状态:
    console.log('this.state:', this.state)

    return true
  }

  render() {
    console.log('组件更新了')
    return (
      <div>
        <h1>计数器:{this.state.count}</h1>
        <button onClick={this.handleClick}>+1</button>
      </div>
    )
  }
}

1.5 纯组件 

 纯组件会先进行一次比较,如果两次对象都是同一个即使内部数据改变,则不会发生渲染

 

2.虚拟DOM和diff算法

2.1 虚拟DOM

 3.React路由

3.1 安装router

npm add react-router-dom

下面BrowserRouter as Router 是给BrowserRouter起别名叫做Router

import {BrowserRouter as Router,Route,Link} from 'react-router-dom';

3.3 Router组件包裹整个应用

const App= ()=> {
  <Router>
    <div>
      <h1>React路由</h1>
    </div>
  </Router>
}

3.4 使用Link组件作为导航菜单(路由入口)

  <Link to="/first">页面一</Link>

 3.4.1 NavLink组件

 NavLink可以实现路由链接的高亮,通过activeClassName指定样式名

 当点击该NavLink组件则该NavLink组件会追加一个类名active,可以通过activeClassName改为其他名称,如图中改为atguigu

<NavLink activeClassName="atguigu" className="list-group-item" to="/about">About</NavLink>
<NavLink activeClassName="atguigu" className="list-group-item" to="/home">Home</NavLink>

当我们NavLink都有同样的样式,都要写一个同样的类名,一个个这样写出来不简洁,于是我们便把NavLink封装起来

import React, { Component } from 'react'
import {NavLink} from 'react-router-dom'

export default class MyNavLink extends Component {
	render() {
		// console.log(this.props);
		return (
			<NavLink activeClassName="atguigu" className="list-group-item" {...this.props}/>
		)
	}
}
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to="/home">Home</MyNavLink>

3.5 使用Router组件配置路由规则和要展示的组件(路由出口)

const First =() => <p>页面一的内容</p>

const App= ()=> (
  <Router>
    <div>
      <h1>React路由</h1>
      <Link to="/first">页面一</Link>
      <Route path="/first" component={First}></Route>
    </div>
  </Router>
)

3.6 常用组件说明

3.7 路由执行过程 

 

 3.8 编程式导航

 3.9 默认路由

 3.10 匹配模式

3.10.1 模糊匹配模式

 3.10.2 精确匹配

 3.11 Switch组件

1.通常情况下,path和component是一一对应的关系。

2.Switch可以提高路由匹配效率(单一匹配)。

3.当路由匹配成功便不会往下继续查找

 将Route组件全部包裹进Switch组件中

<Switch>
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
<Route path="/home" component={Test}/>
</Switch>

3.12 Redirect组件

一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由

<Switch>
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
<Redirect to="/about"/>
</Switch>

3.13 向路由组件传递参数

3.13.1 params参数

              路由链接(携带参数):<Link to='/demo/test/tom/18'}>详情</Link>

              注册路由(声明接收):<Route path="/demo/test/:name/:age" component={Test}/>

              接收参数:this.props.match.params

<Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link>
<Route path="/home/message/detail/:id/:title" component={Detail}/>
// 接收params参数
const {id,title} = this.props.match.params

  3.13.2 search参数

              路由链接(携带参数):<Link to='/demo/test?name=tom&age=18'}>详情</Link>

              注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>

              接收参数:this.props.location.search

              备注:获取到的search是urlencoded编码字符串,需要借助querystring解析

<Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link>
{/* search参数无需声明接收,正常注册路由即可 */}
<Route path="/home/message/detail" component={Detail}/>
//引入qs用于将接受的参数字符串转化为对象
import qs from 'querystring'


// 接收search参数
const {search} = this.props.location
//slice(1)是为了将字符串第一个字符‘?’去掉
const {id,title} = qs.parse(search.slice(1))

3.13. 3 state参数

              路由链接(携带参数):<Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>

              注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>

              接收参数:this.props.location.state

              备注:刷新也可以保留住参数

<Link to={{pathname:'/home/message/detail',state:{id:msgObj.id,title:msgObj.title}}}>{msgObj.title}</Link>
{/* state参数无需声明接收,正常注册路由即可 */}
<Route path="/home/message/detail" component={Detail}/>
// 接收state参数
const {id,title} = this.props.location.state || {}

const findResult = DetailData.find((detailObj)=>{
return detailObj.id === id
}) || {}

3.14 withRouter

withRouter可以加工一般组件,让一般组件具备路由组件所特有的API

withRouter的返回值是一个新组件

import React, { Component } from 'react'
import {withRouter} from 'react-router-dom'

class Header extends Component {

	back = ()=>{
		this.props.history.goBack()
	}

	forward = ()=>{
		this.props.history.goForward()
	}

	go = ()=>{
		this.props.history.go(-2)
	}

	render() {
		console.log('Header组件收到的props是',this.props);
		return (
			<div className="page-header">
				<h2>React Router Demo</h2>
				<button onClick={this.back}>回退</button>&nbsp;
				<button onClick={this.forward}>前进</button>&nbsp;
				<button onClick={this.go}>go</button>
			</div>
		)
	}
}

export default withRouter(Header)

4 ref 

组件内的标签可以定义ref属性来标识自己 

 字符串形式(过时的api)

class Demo extends React.Component{
//展示左侧输入框的数据
showData = ()=>{
const {input1} = this.refs
alert(input1.value)
}
//展示右侧输入框的数据
showData2 = ()=>{
const {input2} = this.refs
alert(input2.value)
}
render(){
return(
<div>
<input ref="input1" type="text" placeholder="点击按钮提示数据"/>&nbsp;
<button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
<input ref="input2" onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>
</div>
)
}
}

回调函数形式(react会自动帮你调用这个函数) 

class Demo extends React.Component{
//展示左侧输入框的数据
showData = ()=>{
const {input1} = this
alert(input1.value)
}
//展示右侧输入框的数据
showData2 = ()=>{
const {input2} = this
alert(input2.value)
}
render(){
return(
<div>
<input ref={c => this.input1 = c } type="text" placeholder="点击按钮提示数据"/>&nbsp;
<button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
<input onBlur={this.showData2} ref={c => this.input2 = c } type="text" placeholder="失去焦点提示数据"/>&nbsp;
</div>
)
}
}

但是上面的方式每一次刷新组件都会调用两次这个内联函数,下面代码则可以避免每次刷新都重新调用这个内联函数(其实以后要用依旧可以用内联函数,这两次调用无关紧要)

		class Demo extends React.Component{

			state = {isHot:false}

			showInfo = ()=>{
				const {input1} = this
				alert(input1.value)
			}

			changeWeather = ()=>{
				//获取原来的状态
				const {isHot} = this.state
				//更新状态
				this.setState({isHot:!isHot})
			}

			saveInput = (c)=>{
				this.input1 = c;
				console.log('@',c);
			}

			render(){
				const {isHot} = this.state
				return(
					<div>
						<h2>今天天气很{isHot ? '炎热':'凉爽'}</h2>
						{/*<input ref={(c)=>{this.input1 = c;console.log('@',c);}} type="text"/><br/><br/>*/}
						<input ref={this.saveInput} type="text"/><br/><br/>
						<button onClick={this.showInfo}>点我提示输入的数据</button>
						<button onClick={this.changeWeather}>点我切换天气</button>
					</div>
				)
			}
		}

createRef形式

 React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的(即一个节点只能用一个容器)

class Demo extends React.Component{
	/* 
		React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的
	 */
	myRef = React.createRef()
	myRef2 = React.createRef()
	//展示左侧输入框的数据
	showData = ()=>{
		alert(this.myRef.current.value);
	}
	//展示右侧输入框的数据
	showData2 = ()=>{
		alert(this.myRef2.current.value);
	}
	render(){
		return(
			<div>
				<input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>&nbsp;
				<button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
				<input onBlur={this.showData2} ref={this.myRef2} type="text" placeholder="失去焦点提示数据"/>&nbsp;
			</div>
		)
	}
}

5 redux

中文文档: http://www.redux.org.cn/

英文文档: https://redux.js.org/

5.1 redux是什么?

  1. redux是一个专门用于做状态管理JS(不是react插件库)
  2. 它可以用在react, angular, vue等项目中, 但基本与react配合使用。
  3. 作用: 集中式管理react应用中多个组件共享的状态。

5.2 什么情况下需要使用redux

  1. 某个组件的状态,需要让其他组件可以随时拿到(共享)。
  2. 一个组件需要改变另一个组件的状态(通信)。
  3. 总体原则:能不用就不用, 如果不用比较吃力才考虑使用。

5.3  redux的三个核心概念

5.3.1 action

  1. 动作的对象
  2. 包含2个属性
    1. type:标识属性, 值为字符串, 唯一, 必要属性
    2. data:数据属性, 值类型任意, 可选属性
  3. 例子:{ type: 'ADD_STUDENT',data:{name: 'tom',age:18} }

5.3.2  reducer

  1. 用于初始化状态、加工状态。
  2. 加工时,根据旧的stateaction 产生新的state纯函数

5.3.3 store

1. 将stateactionreducer联系在一起的对象

2. 如何得到此对象?

    (1)import {createStore} from 'redux'

    (2)import reducer from './reducers'

    (3)const store = createStore(reducer)

3. 此对象的功能?

    (1)  getState(): 得到state

    (2)  dispatch(action): 分发action, 触发reducer调用, 产生新的state

    (3)  subscribe(listener): 注册监听, 当产生了新的state时, 自动调用

 5.4 redux 核心API

5.4.1  createstore()

作用:创建包含指定reducer的store对象

5.4.2. store对象 

 5.4.3  applyMiddleware()

作用:应用上基于redux的中间件(插件库)

5.4.4 combineReducers()

作用:合并多个reducer函数

5.5 redux异步编程

5.5.1理解:

  1. redux默认是不能进行异步处理的,
  2. 某些时候应用中需要在redux中执行异步任务(ajax, 定时器)

5.5.2. 使用异步中间件

npm install --save redux-thunk

5.6 使用步骤

安装redux

yarn add redux

生成随机id

npm add nanoid

 1.求和案例_redux精简版 

    (1).去除Count组件自身的状态

    (2).src下建立:

            -redux

              -store.js

              -count_reducer.js

    (3).store.js:

          1).引入redux中的createStore函数,创建一个store

          2).createStore调用时要传入一个为其服务的reducer

          3).记得暴露store对象

    (4).count_reducer.js:

          1).reducer的本质是一个函数,接收:preState,action,返回加工后的状态

          2).reducer有两个作用:初始化状态,加工状态

          3).reducer被第一次调用时,是store自动触发的,

                  传递的preState是undefined,

                  传递的action是:{type:'@@REDUX/INIT_a.2.b.4}

    (5).在index.js中监测store中状态的改变,一旦发生改变重新渲染<App/>

        备注:redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写。

 2.求和案例_redux完整版

 新增文件:

      1.count_action.js 专门用于创建action对象

      2.constant.js 放置容易写错的type值

3.求和案例_redux异步action版    

 (1).明确:延迟的动作不想交给组件自身,想交给action

     (2).何时需要异步action:想要对状态进行操作,但是具体的数据靠异步任务返回。

     (3).具体编码:

          1).yarn add redux-thunk,并配置在store中

          2).创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务。

          3).异步任务有结果后,分发一个同步的action去真正操作数据。

     (4).备注:异步action不是必须要写的,完全可以自己等待异步任务的结果了再去分发同步action。

5.7 react-redux

5.7.1 求和案例_react-redux基本使用

      (1).明确两个概念:

            1).UI组件:不能使用任何redux的api,只负责页面的呈现、交互等。

            2).容器组件:负责和redux通信,将结果交给UI组件。

      (2).如何创建一个容器组件————靠react-redux 的 connect函数

              connect(mapStateToProps,mapDispatchToProps)(UI组件)

                -mapStateToProps:映射状态,返回值是一个对象

                -mapDispatchToProps:映射操作状态的方法,返回值是一个对象

      (3).备注1:容器组件中的store是靠props传进去的,而不是在容器组件中直接引入

      (4).备注2:mapDispatchToProps,也可以是一个对象

5.7.2 求和案例_react-redux优化

  (1).容器组件和UI组件整合一个文件

      (2).无需自己给容器组件传递store,给<App/>包裹一个<Provider store={store}>即可。

      (3).使用了react-redux后也不用再自己检测redux中状态的改变了,容器组件可以自动完成这个工作。

      (4).mapDispatchToProps也可以简单的写成一个对象

      (5).一个组件要和redux“打交道”要经过哪几步?

              (1).定义好UI组件---不暴露

              (2).引入connect生成一个容器组件,并暴露,写法如下:

                  connect(

                    state => ({key:value}), //映射状态

                    {key:xxxxxAction} //映射操作状态的方法

                  )(UI组件)

              (4).在UI组件中通过this.props.xxxxxxx读取和操作状态

5.7.3  求和案例_react-redux数据共享版

(1).定义一个Pserson组件,和Count组件通过redux共享数据。

      (2).为Person组件编写:reducer、action,配置constant常量。

      (3).重点:Person的reducer和Count的Reducer要使用combineReducers进行合并,

          合并后的总状态是一个对象!!!

      (4).交给store的是总reducer,最后注意在组件中取出状态的时候,记得“取到位”。

5.7.4 求和案例_react-redux开发者工具的使用 

(1).yarn add redux-devtools-extension

      (2).store中进行配置

          import {composeWithDevTools} from 'redux-devtools-extension'

          const store = createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))

5.7.5  求和案例_react-redux最终版

(1).所有变量名字要规范,尽量触发对象的简写形式。

 (2).reducers文件夹中,编写index.js专门用于汇总并暴露所有的reducer

6.拓展

 6.1 lazyload

路由懒加载

用这种方法引入路由组件

import React, { Component,lazy,Suspense} from 'react
import Loading from './Loading'
const Home = lazy(()=> import('./Home') )
const About = lazy(()=> import('./About'))

注册路由(当网速慢加载不出来就显示Loading组件)

<Suspense fallback={<Loading/>}>
	{/* 注册路由 */}
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
</Suspense>

6.2 Hooks

6.2.1 React Hook/Hooks是什么?

(1). Hook是React 16.8.0版本增加的新特性/新语法

(2). 可以让你在函数组件中使用 state 以及其他的 React 特性

6.2.2 State Hook

State Hook 里面就两个参数,第一个是存放数值,第二个是更新数值的方法(数组解构可以通过位置来解构,不用同一名称)

const [count,setCount] = React.useState(0)

useState()说明:

参数: 第一次初始化指定的值在内部作缓存

返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数

setCount(count+1) //第一种写法
setCount(count => count+1 )

6.2.3 Effect Hook

Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子) 

当useEffect第二个参数为空数组时,只相当于 componentDidMount(),当没有第二个参数,则监视所有的state的数据更新,每次数据更新就重新更新一次组件,第二个参数中如果写了指定参数,就只监视指定参数的更新,也相当于componentDidUpdate(),当useEffect里返回了一个函数,这个函数就相当于componentWillUnmount() 

React.useEffect(()=>{
let timer = setInterval(()=>{
setCount(count => count+1 )
},1000)
return ()=>{
clearInterval(timer)
}
},[])

6.2.4 Ref Hook

const myRef = React.useRef()

6.3 Fragment

每用一个组件就得在组件最外层包裹一层div,这样堆叠的层级太多了,我们可以用Fragment组件

React识别到它时会丢弃它,从而少一层div,

import React, { Component,Fragment } from 'react'

export default class Demo extends Component {
	render() {
		return (
			<Fragment key={1}>
				<input type="text"/>
				<input type="text"/>
			</Fragment>
		)
	}
}

其实也可以用空标签(但是它俩还是有区别,Fragment可以加而且只能加key属性,当这个组件需要遍历时就可以用Fragment组件)

<>
	<input type="text"/>
	<input type="text"/>
 </>

6.4 Context

一种组件间通信方式, 常用于【祖组件】与【后代组件】间通信

 在祖组件和后代组件都能接触的地方创建Context对象

//创建Context对象
const MyContext = React.createContext()
const {Provider,Consumer} = MyContext

 渲染子组时,外面包裹xxxContext.Provider, 通过value属性给后代组件传递数据:

export default class A extends Component {

	state = {username:'tom',age:18}

	render() {
		const {username,age} = this.state
		return (
			<div className="parent">
				<h3>我是A组件</h3>
				<h4>我的用户名是:{username}</h4>
				<Provider value={{username,age}}>
					<B/>
				</Provider>
			</div>
		)
	}
}

类组件接收方式

class C extends Component {
	//声明接收context
	static contextType = MyContext
	render() {
		const {username,age} = this.context
		return (
			<div className="grand">
				<h3>我是C组件</h3>
				<h4>我从A组件接收到的用户名:{username},年龄是{age}</h4>
			</div>
		)
	}
}

函数组件接收方式

function C(){
	return (
		<div className="grand">
			<h3>我是C组件</h3>
			<h4>我从A组件接收到的用户名:
			<Consumer>
				{value => `${value.username},年龄是${value.age}`}
        {/* {value => <span>{value.username},年龄是{value.age}</span>} */}
			</Consumer>
			</h4>
		</div>
	)
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值