React笔记

                                      初识react 

1.React是用于构建用户界面JavaScript库, 起源于Facebook的内部项目

2.视图层框架

React主要用于构建UI。你可以在React里传递多种类型的参数,如声明代码,帮助你渲染出UI、也可以是静态的HTML DOM元素、也可以传递动态变量、甚至是可交互的应用组件。

3.以组件的方式开发

React 提供了 stateprops 两个主要的属性
1)state 就是组件里面的 React 的数据(也可以称之为状态)
2)props 是用于父子组件通信的(子组件可以通过 props 拿到父组件的数据或方法)

4.JSX 表达式

我们可以通过 【JSX 表达式】处理数据和 DOM 之间的关系,而不需要操作 DOM

5.虚拟 DOM

React 中采用虚拟 DOM 的机制;

1)在 React 中,我们会把定义好的JSX 标记最终转换为一个虚拟的 DOM,存在内存里

2)如果用户做了对DOM 可能产生影响的操作的时候,虚拟DOM 会把操作前后的数据进行对比,如果操作前后的数据有变化,就会把所有的变动然后统一操作一次 DOM ,如果发现操作前后的数据没有差异的话,就不会再去操作 DOM了;

3)虚拟DOM 有两大优势
1】一个是操作 DOM 前对数据进行对比,只有数据变化的时候才去操作DOM
2】它会整合 DOM 操作,可以减少 DOM 操作,提升性能

6.React 优点

1).声明式设计:React 使创建交互式 UI 变得轻而易举。为你应用的每一个状态设计简洁的视图,当数据变动时 React能高效更新并渲染合适的组件

2)组件化: 构建管理自身状态的封装组件,然后对其组合以构成复杂的 UI。

3)高效:React通过对DOM的模拟,最大限度地减少与DOM的交互。

4)灵活:无论你现在使用什么技术栈,在无需重写现有代码的前提下,通过引入React来开发新功能。

一、jsx语法规则

可以从以下三个方面理解 JSX:
1)JSX 是一种 JS 扩展的表达式
2)JSX 是带有逻辑的标记语法,有别于 HTML 模版
3)并且支持样式、逻辑表达式和事件

jsx语法规则:
              1.定义虚拟DOM时,不要写引号。
              2.标签中混入JS表达式时要用{}。
              3.样式的类名指定不要用class,要用className。
              4.内联样式,要用style={{key:value}}的形式去写。
              5.只有一个根标签
              6.标签必须闭合
              7.标签首字母
                          (1).若小写字母开头,则将该标签转为html中同名元素,若html中无该标签对应的同名元素,则报错。
                          (2).若大写字母开头,react就去渲染对应的组件,若组件没有定义,则报错。
 

        const myId = 'aTgUiGu'
		const myData = 'HeLlo,rEaCt'

		//1.创建虚拟DOM
		const VDOM = (
			<div>
				<h2 className="title" id={myId.toLowerCase()}>
					<span style={{color:'white',fontSize:'29px'}}>{myData.toLowerCase()}</span>
				</h2>
				<h2 className="title" id={myId.toUpperCase()}>
					<span style={{color:'white',fontSize:'29px'}}>{myData.toLowerCase()}</span>
				</h2>
				<input type="text"/>
			</div>
		)
		//2.渲染虚拟DOM到页面
		ReactDOM.render(VDOM,document.getElementById('test'))

二、React创建自定义组件的两种语法

1.(1)面向过程 —— function式组件 ——  rfc(函数组件又叫做 无状态组件

//1.创建函数式组件
		function MyComponent(){
			console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
			return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>
		}
		//2.渲染组件到页面
		ReactDOM.render(<MyComponent/>,document.getElementById('test'))
		/* 
			执行了ReactDOM.render(<MyComponent/>.......之后,发生了什么?
					1.React解析组件标签,找到了MyComponent组件。
					2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中。
		*/

(2)面向对象 —— class式组件 —— rcc(类组件又叫做 有状态组件

class MyComponent extends React.Component {
			render(){
				//render是放在哪里的?—— MyComponent的原型对象上,供实例使用。
				//render中的this是谁?—— MyComponent的实例对象 <=> MyComponent组件实例对象。
				console.log('render中的this:',this);
				return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>
			}
		}
		//2.渲染组件到页面
		ReactDOM.render(<MyComponent/>,document.getElementById('test'))
		/* 
			执行了ReactDOM.render(<MyComponent/>.......之后,发生了什么?
					1.React解析组件标签,找到了MyComponent组件。
					2.发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
					3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。
		*/

2、class组件中this指向undefined问题

情形:①class组件中  ②事件处理方法中的this指向undefined

原因:①按钮单击触发,this不指向class对象  

          ②JSX被编译为虚拟DOM对象,没有“事件源”概念 

          ③JSX被编译为“严格模式”下的执行代码,事件处理函数为全局函数——严格模式下全局对象不是window而是undefined

解决方案:

方案1:利用 箭头函数 形式的 class实例 方法

f1 = ()=>{ }

方案2:利用箭头函数自身不绑定 this 的特点

onClick={ ()=>this.f1( ) }

方案3:利用 ES5 中的 bind() 方法,将事件处理程序中的 this 与组件实例绑定到一起

onClick={ this.f1.bind( this ) }

三、React中的三大属性

1.state

state有两个基本功能:

1)能够存储一定的值,从而能被react使用;

2)能够再它改变的时候被React监听到并且重新渲染

类组件

class Weather extends React.Component{
	//初始化状态
	state = {
               isHot:false,
               wind:'微风'
            }

	render(){
		const {isHot,wind} = this.state
		return <h1>
                    今天天气很{isHot ? '炎热' : '凉爽'}{wind}                        
               </h1>
			}
		}

setState修改状态

  • 状态是可变的
  • 语法:this.setState({ 要修改的数据 })
  • 注意:不要直接修改 state 中的值,这是错误的!!!
  • setState() 作用:1. 修改state 2. 更新UI
//1.创建组件
		class Weather extends React.Component{
			//初始化状态
			state = {isHot:false,wind:'微风'}

			render(){
				const {isHot,wind} = this.state
				return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'},{wind}</h1>
			}

			//自定义方法————要用赋值语句的形式+箭头函数
			changeWeather = ()=>{
				const isHot = this.state.isHot
				this.setState({isHot:!isHot})
			}
		}
		//2.渲染组件到页面
		ReactDOM.render(<Weather/>,document.getElementById('test'))

 函数组件

 这里的setData的作用跟setState类似,但是它只能用来改变data的状态,需要改变的时候传入一个回调函数。函数的参数是之前的值,返回一个要改变的值。这个方法本质是需要传入一个新的对象,来改变React之前对象的值。

import {useState} from "react"
function FunctionComponent(){
    const [data,setData] = useState("你要传入的初始值")
    return (
        <div>展示数据 {state}</div>
    )
}

2.props

props功能在于组件间通信(父子组件传参)

注意:所有 React 组件都必须像纯函数一样保护它们的 props 不被更改,是只读的

类组件(父传子)

//父组件传值
class Father extends React.PureComponent{
    render(){
        return (
            <Son value={"son"} />
        )
    }
}

class Son extends React.PureComponent{
    render(){
        return (
            <div>this data is {this.props.value}</div>
        )
    }
}

 类组件(子传父)

//子组件给父组件传值,此时往往需要父组件给子组件先传一个props函数,
//子组件通过调用传入的函数传值改变父组件的值
export default class Fa extends Component {
    state = {faValue:'Fa1'}

    changeFa = (value)=>{
        this.setState(()=>{
            return {faValue:value}
        })
    }
    render() {
        return (
            <>
                <h1>Fa's value is {this.state.faValue}</h1>
                <Son changeFa={this.changeFa}/>
            </>
        )
    }
}

export default class Son extends React.PureComponent{

    changeValue = ()=>{
        this.props.changeFa(this.inputRef.value)
    }
    render() {
        return (
            <>
                <input type="text" placeholder={"请输入您的值"} ref={(el)=>{this.inputRef = el}}/>
                <button onClick={this.changeValue}>change</button>
            </>
        )
    }
}

 函数组件(父传子)

function Fa(){
    return (
        <Son value={"son"} />
    )
}

function Son(props){
    return (
        <div>this data is {props.value}</div>
    )
}

  函数组件(子传父)

function Fa(){
    const [faValue,setFaValue] = useState("Fa1")

    const changeFa = (value)=>{
        setFaValue(value)
    }
    return (
        <div>
            <h1>Fa's value is {faValue}</h1>
            <Son changeFa={changeFa} />
        </div>
    )
}

function Son(props){
    const inputValue = useRef("")
        //定义改变fa组件的值的函数
        const changeFaValue = ()=>{
            props.changeFa(inputValue.current.value)
        }
        return (
            <>
                <input type="text" placeholder={"请输入您要改变的值"} ref={inputValue}/>
                <button onClick={changeFaValue}>change value</button>
            </>
        )
}

 3.ref

ref是一个对象,它有一个current属性,可以对这个属性进行操作,用于获取DOM元素和保存变化的值。比如setTimeout的返回的ID,就可以把这个值放到ref中。为什么要放到ref中,因为更改ref的值,不会引起组件的重新渲染,因为值与渲染无关,它也不应该引起组件渲染。

怎么获取ref对象呢?调用useRef()函数和createRef()函数,它们返回ref对象。在组件的整个生命周期中,ref对象一直存在。

字符串形式

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>
				)
			}
		}
		//渲染组件到页面
		ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))

 回调引用

//创建组件
		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>
				)
			}
		}
		//渲染组件到页面
		ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))

 使用useRef()

//组件挂载后,ref对象的current属性,就自动指向DOM元素

import React, { useRef } from "react";

const CustomTextInput = () => {
  const textInput = useRef();

  const focusTextInput = () => textInput.current.focus();

  return (
    <>
      <input type="text" ref={textInput} />
      <button onClick={focusTextInput}>Focus the text input</button>
    </>
  );
}

 useRef是一个React Hooks,在函数组件中使用,它还可以接受一个参数,用于初始化useRef返回的对象的current属性。(const ref = useRef(initialValue);)

import React, { useRef } from "react";

const RenderCounter = () => {
  const counter = useRef(0);
  
  useEffect(() => {
    counter.current = counter.current + 1;
  }); 
  
  return (
    <h1>{`The component has been re-rendered ${counter} times`}</h1>
  );
};

使用React.createRef()【类组件写法】

//创建组件
		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>
				)
			}
		}
		//渲染组件到页面
		ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))

 使用React.createRef()【函数组件写法】

function CustomTextInput(props) {
 // textInput必须在此处声明,以便ref可以引用它
 let textInput = React.createRef();

 function handleClick() {
 textInput.current.focus();
 }

 return (
 <div>
  <input
  type="text"
  ref={textInput} />

  <input
  type="button"
  value="Focus the text input"
  onClick={handleClick}
  />
 </div>
 );
}

 四、事件处理

//创建组件
		class Demo extends React.Component{
			/* 
				(1).通过onXxx属性指定事件处理函数(注意大小写)
						a.React使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —————— 为了更好的兼容性
						b.React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ————————为了的高效
				(2).通过event.target得到发生事件的DOM元素对象 ——————————不要过度使用ref
			 */
			//创建ref容器
			myRef = React.createRef()
			myRef2 = React.createRef()

			//展示左侧输入框的数据
			showData = (event)=>{
				console.log(event.target);
				alert(this.myRef.current.value);
			}

			//展示右侧输入框的数据
			showData2 = (event)=>{
				alert(event.target.value);
			}

			render(){
				return(
					<div>
						<input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/>&nbsp;
						<button onClick={this.showData}>点我提示左侧的数据</button>&nbsp;
						<input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/>&nbsp;
					</div>
				)
			}
		}
		//渲染组件到页面
		ReactDOM.render(<Demo a="1" b="2"/>,document.getElementById('test'))

五、react受控组件与非受控组件

1.受控组件中输入框的值由其change事件接管,做对应的数据劫持操作,例如可以对输入框的值进行合法输入校验(匹配正则),此时表单数据由 React 组件来管理(“受开发者控制”)。

import axios from "axios";
import React, { useState, useEffect } from "react";

function ControlledComponent() {
//创建一个state值绑定在输入框的value上;
  const [userName, setUsername] = useState("小明");
  //数据组装提交到后台
  function doSumbit() {
    const data = {
      name: userName,
    };
    console.log(data)
    axios("xxx", data);
  }
  useEffect(() => {
    console.log(userName);
  }, [userName]);

  return (
    <>
       //输入框绑定change事件传入事件e
       //change事件中得到输入框的值然后赋值改变state值
      <input value={userName} onChange={(e) => {setUsername(e.target.value);} }></input>
      <button onClick={doSumbit}>提交</button>
    </>
  );
}
export default ControlledComponent;

2.非受控组件则是同过ref直接获取输入框的值,只获取到最后状态的结果值,在输入过程中做不到对输入值的操作,此时表单数据将交由 DOM 节点来处理(“不受开发者控制”)。

import axios from "axios";
import React, { useRef } from "react";

function NotControlledComponent() {
  const inputRef = useRef();
//通过ref.current.value获取输入框的值进行数据组装提交到后台
  function doSumbit() {
    const data = {
      name: inputRef.current.value,
    };
    axios("xxx", data);
  }

  return (
    <>
       //创建一个ref实例绑定到输入框的ref上
      <input ref={inputRef}></input>
      <button onClick={doSumbit}>提交</button>
    </>
  );
}
export default NotControlledComponent;

六、class组件的生命周期

旧:

新: 

 七、消息订阅与发布机制

1.先订阅,再发布(理解:隔空对话的感觉)
安装第三方库pubsub-js   npm add pubsub-js利用钩子函数componentDidMount在组件挂载subscribe()函数进行订阅消息。

 import PubSub from 'pubsub-js' //引入

	//订阅 subscribe()
    componentDidMount(){
        this.token = PubSub.subscribe('usersData',(_,data) => {
            console.log('订阅消息:',data)
            this.setState({...data})
        })
    }

	//发布消息 publish()
	PubSub.publish('usersData',{isFirst:false,isLoading:true})
    //发送网络请求
    axios.get(`https://api.github.com/search/users?q=${keyWord}`).then(
        response => {
            // this.props.UpdateAppState({isLoading:false,users:response.data.items})
            PubSub.publish('usersData',{isLoading:false,users:response.data.items})

        },
        error => {
            //this.props.UpdateAppState({isLoading:false,err:error.message})}
            PubSub.publish('usersData',{isLoading:false,err:error.message})
        }
            
    )

2.适用于任意组件间通信 

3. 要在组件的componentWillUnMount中取消订阅

    //取消订阅 unsubscribe()
    componentWillUnmount(){
        PubSub.unsubscribe(this.token)
    }

 八、React中的数据绑定

注意:React中没有“指令”的概念,即无v-on、v-if、v-for、v-model....

 

 ①内容绑定:

<any>{表达式}</any>

 ②属性绑定:

<any 属性名={表达式}/>

 ③样式绑定:

<any style={ {color:'red'} }/>

 ④事件绑定:

<any onClick={表达式}/>

 ⑤双向数据绑定:

<input value={表达式}  onChange={e=>setXxx(e.target.value)}/>

 ⑥条件渲染:

<any>{ 判定表达式  &&  (JSX)  }</any>    等价于  if( ){ return (JSX) }

<any>{ 判定表达式  ?  (JSX1)  :  (JSX2) }</any> 等价于  if( ){ return (JSX1) }else {return (JSX2)}

 ⑦列表渲染:

 <any>{ arr.map( (e,i)=>(JSX) ) }</any>

九、路由 (v6)

v6 版本相比v5使用上发生了一些改变:

  • v6版本移除了之前的<Switch>,引入了新的替代者<Routes>
  • <Routes><Route>配合使用,且必须用<Routes>包裹<Route>,当url发生变化时<Routes>会查看其所有子<Route>元素找到最佳匹配并呈现组件
  • <Route caseSensitive>属性用于指定:匹配时是否区分大小写(默认为false
  • component 改为 element

  • 路由重定向 Navigate,代替之前的Redirect
    <Route path='/' element={<Navigate to="/home"/>}/>
    
    <Redirect path='/' to="/home" />
  • <NavLink/> 与<Link/>类似,但增加了一个点击之后导航高亮的效果 ,它有一个默认的类名active ,通过这个类名可以修改高亮的颜色

步骤一:下载路由组件库

install i react-router-dom

步骤二:引用Link标签,替代<a>标签进行跳转

步骤三:定义路由对应要跳转的组件

import { BrowserRouter,Link,Route, Routes} from "react-router-dom";

// link相当于 跳转标签,
// Route相当于容器

const Home=()=>{
    return(
        <h1>Home</h1>
    )
}
const About=()=><h1>About</h1>
const Main=()=><h1>main</h1>

export default function Basic(){
    return (
        <BrowserRouter> 
        {/* // 如果项目需要路由,需要这个组件将项目包裹,只有在内部才能使用 */}
        <ul>
            <li>
                <Link to='/'>home</Link>
            </li>
            <li>
                <Link to='/about'>about</Link>
            </li>
            <li>
                <Link to='/main'>main</Link>
            </li>
        </ul>
        {/* 使用component组件渲染对应的组件 */}
        {/* // react是包容性路由,满足条件都渲染,使用 exact精准匹配 */}
        <Routes>
           
            <Route path="/" element={<Home/>} exact></Route>
        <Route path="/about" element={<About/>}></Route>
        <Route path="/main" element={<Main/>}></Route>         
        </Routes>
        </BrowserRouter>
    )
}

 4、Outlet ,useRoutes()

Route 可以嵌套使用,且可以配合useRoutes 配置路由表,通过<Outlet/> 来渲染匹配到的子路由

//一般的嵌套写法
<Routes>
   <Route path='/home' element={<Home />}>
       <Route path='news' element={<HomeNews />} />
       <Route path="message" element={<HomeMessage />} />
   </Route>
</Routes>

//使用使用useRoutes()写法
const element = useRoutes([
    {
      path: '/',
      element: <Navigate to="/home" />
    },
    {
      path: '/home',
      element: <Home />,
      children: [
        {
          path: 'news',
          element: <HomeNews />
        },
        {
          path: 'message',
          element: <HomeMessage />
        }
      ]
    }
  ])


//Home文件
<Home>
      <NavLink to="/home/news">news</NavLink>
      <NavLink to="/home/message">message</NavLink>
 </Home>
 <Outlet/>//渲染路由规则匹配到的组件

 5、路由传参

常见的组件路由传递参数的方式有三种,分别是通过params参数传递利用search属性传递利用state属性传递

1)通过params进行参数传递

一般来说,我们会使用类似于/${id}的方式来传递值

<NavLink to={`/home/messages/details/${messageObj.id}/${messageObj.title}`}>xxx</NavLink>

 虽然传递了多个参数,但是我们并不知道每个参数对应的key是什么,所以需要先规定好每个参数对应的key。

<Route path={'/home/messages/details/:id/:title'} component={Details}/>

然后在目标组件中使用this.props.match来获取传递的参数

export default class Details extends Component {
    state = {
        details: [
            {id:'01',comment:'你好,我的祖国'}
            ...
        ]
    }
    render() {
        const {details} = this.state;
        const {params} = this.props.match;
        const detailofMessage = details.find((detailObj)=>{
            return detailObj.id === params.id 
        })
        return (
            <ul>
                <li>ID: {detailofMessage.id}</li>
                <li>Title: {params.title}</li>
                <li>Comment: {detailofMessage.comment}</li>
            </ul>
        )
    }
}

上面是类组件方式,下面为函数组件

//在路由的path上配置:id
{
   path: 'news/:id',
   element: <HomeNews />
 },

//传递
<NavLink to="/home/news/1/消息">news</NavLink>

//获取
function HomeNews() {
  console.log(useParams());//{id: '1', title: '消息'}
  const {id,title} = useParams()//获取url里携带的params参数
  return (
      <News>{id}{title}</News>
  )
}

 

(2)组件路由通过search属性传递参数

种方式基本上和我们常见的在url地址上进行参数拼接是一样的,这种方式写法较为简单,只需要在 Link 标签中定义好key=value形式参数拼接,然后在实际的组件中,直接从prop属性中获取,调用querystring(或者其他处理字符串转对象的函数库)处理参数,并封装成对象返回即可

 <NavLink to={`/home/messages/details?id=${messageObj.id}&title=${messageObj.title}`}>xxx</NavLink>

这种方式下的传参并不需要额外对路由进行配置

然后在实际组件中获取传递的参数(props.location.search属性中把我们步骤一的参数都封装了进去,但是这种方式封装的参数是字符串形式的,我们并不能直接使用,而是要借助queryString库来帮助我们把字符串解析成对象。)

import React, { Component } from 'react'
// querystring 是脚手架初始化的时候自带的函数库,能帮助我们分解和生成key=value形式的对象
import qs from 'querystring'
export default class Details extends Component {
    state = {
        details: [
            {id:'01',comment:'你好,我的祖国'}  
            ...
        ]
    }
    render() {
        console.log('this.props',this.props)
        const {details} = this.state;
        const {search} = this.props.location;
        // parse能将字符串转为key:value形式的对象
        const messageObj = qs.parse(search.slice(1));
        const detailofMessage = details.find((detailObj)=>{
            return detailObj.id === messageObj.id 
        })
        return (
            <ul>
                <li>ID: {detailofMessage.id}</li>
                <li>Title: {messageObj.title}</li>
                <li>Comment: {detailofMessage.comment}</li>
            </ul>
        )
    }
}

上面是类组件方式,下面为函数组件

//传递search参数
<NavLink to="/home/news?id=1&title=消息">news</NavLink>

//接受search参数
function HomeNews() {
  const [search,setsearch] = useSearchParams()//返回search参数 和 更新search参数的函数
  const id = search.get('id')//通过get方法获取
  const title = search.get('title')
  console.log(useSearchParams());//[URLSearchParams, ƒ]
  return (
      <News>
        <button onClick={()=>setsearch('id=2&title=更新search')}>点我更新search参数</button>
        <hr />
        {id}{title}
      </News>
  )
}

 

(3)通过state属性来进行参数的传递

这种方式传递的参数是不会在浏览器中显式的展示的,同时,即使刷新页面,路由子页面的数据还是不会消失。原因是react路由器帮我们对维护了history对象(history对象中又维护了location对象,所以也就有了state对象)

<NavLink to={{pathname:'/home/messages/details',state:{id:messageObj.id,title:messageObj.title}}}>xxx</NavLink>

这种方式下的传参也不需要额外对路由进行配置

然后在具体组件中获取传递的参数

export default class Details extends Component {
    state = {
        details: [
            {id:'01',comment:'你好,我的祖国'}
            ...
        ]
    }

    render() {
        const {details} = this.state;
        const {id,title} = this.props.location.state;
        const detailofMessage = details.find((detailObj)=>{
            return detailObj.id === id 
        })
        return (
            <ul>
                <li>ID: {detailofMessage.id}</li>
                <li>Title: {title}</li>
                <li>Comment: {detailofMessage.comment}</li>
            </ul>
        )
    }
}

上面是类组件方式,下面为函数组件

//传递state参数
<NavLink to="/home/news" state={{id:1,title:'消息'}} >news</NavLink>

//接受state参数
function HomeNews() {
  const { state:{id,title}} = useLocation()//连续解构赋值
  return (
      <News>{id}{title}</News>
  )
}

6.编程式路由导航 useNavigate 代替v5中的this.props.history

 默认是push 模式

export default function HomeNews() {
  const navigate = useNavigate();
  const jump = ()=>{
    navigate('/home')
  }
  return (
      <News>
        <button onClick={jump}>点击跳转</button>
      </News>
  )
}

使用{replace:true} 就会变为replace模式

navigate('/home', { replace: true });

也可以使用 navigate(-1) 传入一个数字来进行跳转

navigate(1)//传入数字

7.BrowserRouter与HashRouter的区别

            1.底层原理不一样:
                        BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
                        HashRouter使用的是URL的哈希值。
            2.path表现形式不一样
                        BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
                        HashRouter的路径包含#,例如:localhost:3000/#/demo/test
            3.刷新后对路由state参数的影响
                        (1).BrowserRouter没有任何影响,因为state保存在history对象中。
                        (2).HashRouter刷新后会导致路由state参数的丢失!!!
            4.备注:HashRouter可以用于解决一些路径错误相关的问题。

十、react hook 

Hook 不能在 class 组件中使用 !!!只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用。只能在React 的函数组件或自定义的 Hook中调用 Hook

1.useState

用于定义变量和相关的更新变量的函数(将state按照相关性进行划分,这样有利于独立出自定义hook)

 1:  import React, { useState } from 'react';
 2:
 3:  function Example() {
 4:    const [count, setCount] = useState(初始值);
 5:
 6:    return (
 7:      <div>
 8:        <p>You clicked {count} times</p>
 9:        <button onClick={() => setCount(count + 1)}>
10:         Click me
11:        </button>
12:      </div>
13:    );
14:  }

2.useEffect

相当于 componentDidMount 和 componentDidUpdate、componentWillUnmount

a.如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([])作为第二个参数。这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值。

useEffect(() => {
    getList(current)
  }, [])

b.采用传递第二个参数,判断是否需要重新执行

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时更新

c.组件即将挂载

useEffect( ()=>{
   return  ()=>{
      //生命周期方法4 = 任意状态数据发生改变 + 组件即将卸载
   }
} )

 3.useRef

const refContainer = useRef(initialValue);

useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。一个常见的用例便是命令式地访问子组件:

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    // `current` 指向已挂载到 DOM 上的文本输入元素
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </>
  );
}

useRef 会在每次渲染时返回同一个 ref 对象,类似于class组件的this,可用于保存变量

function Timer() {
  const intervalRef = useRef();
 
  useEffect(() => {
    const id = setInterval(() => {
      // ...
    });
    intervalRef.current = id;
    return () => {
      clearInterval(intervalRef.current);
    };
  });
 
  // ...
}

十一、React小知识:

React小知识:在JSX里如何创建一个不生成任何HTML元素的父元素?

Vue.js中:

    方法①  <template></template>、  

    方法②  <block></block>

React中:

    方法①  <></>

    方法②  <React.Fragment></React.Fragment>     片段

十二、redux 

安装

npm install -save redux

1. Action 的作用就是告诉状态管理器需要做什么样的操作

const add =()=>{
	return {
		type:"add",
		data:id,
	}
}

2.Reducer  这是一个确定状态将如何变化,以及更新状态的地方。

 Reducer 是一个纯函数,接受 旧 state 和 action,根据不同的 Action 做出不同的操作,并返回新的 state 。即:(state, action) => state

/* 
	1.该文件是用于创建一个为Count组件服务的reducer,reducer的本质就是一个函数
	2.reducer函数会接到两个参数,分别为:之前的状态(preState),动作对象(action)
*/

const initState = 0 //初始化状态
export default function countReducer(preState=initState,action){
	// console.log(preState);
	//从action对象中获取:type、data
	const {type,data} = action
	//根据type决定如何加工数据
	switch (type) {
		case INCREMENT: //如果是加
			return preState + data
		case DECREMENT: //若果是减
			return preState - data
		default:
			return preState
	}
}

3.Store – 整个程序的状态state保存在Store中

//import { createStore } from 'redux';
//const store = createStore(reducer);
/* 
	该文件专门用于暴露一个store对象,整个应用只有一个store对象
*/

//引入createStore,专门用于创建redux中最为核心的store对象
import {createStore} from 'redux'
//引入为Count组件服务的reducer
import countReducer from './count_reducer'
//暴露store
export default createStore(countReducer)

 这就是 store, 用来管理 state 的单一对象,其中有三个方法:
1、store.getState(): 获取state ,如上,经过 reducer 返回了一个新的 state,可以用该函数获取。
2、store.dispatch(action) :发出 action,用于触发 reducer,更新 state,

import { createStore } from 'redux';
const store = createStore(fn);

const action = {
    type: 'change_input',
    value: 'Learn Redux',
};

store.dispatch(action);


3、store.subscribe(listener):监听变化,当 state 发生变化时,就可以在这个函数的回调中监听。

import { Component} from 'react';
import store from './store/index';

class List extends Component {
    constructor(props) {
        super(props);
        store.subscribe(this.storeChange.bind(this)); // store发生改变时,自动触发storeChange方法
    }
    render() {
        return (
            // ....
        );
    }
    storeChange() {
        // store发生改变时,将自动触发
        let newState = store.getState();  // 得到当前state状态,包含所有store属性
        this.setState(newState);  // 重新渲染 View
    }
}

export default List;

store.subscribe方法返回一个函数,调用这个函数就可以解除监听

let unsubscribe = store.subscribe(() =>
  console.log(store.getState())
);

unsubscribe(); // 解除监听

1)redux中如何进行异步操作?(借助异步中间件来进行异步处理)

redux-thunk中间件的使用方法:

链接地址

2)connect用法(连接 React 组件与 Redux Store

导入:

import { connect } from 'react-redux'

使用: 

connect( mapStateToProps , mapDispatchToProps )()

 mapStateToProps

/*定义该参数后,组件就可以监听 Redux Store 的变化,任何时候只要store发生变化,
该函数就会被调用;
该函数必须返回一个纯对象,它会与组件的 props 结合,
而第二个参数 ownProps 被指定时,它代表传递到该组件的props,
且只要组件收到新的 props ,mapStateToProps 就被调用*/

const mapStateToProps = (state, ownProps) => {
    return {
        value: state.count //UI组件中需要什么参数,对象中就写哪些参数
    }
}

mapDispatchToProps

/*如果mapDispatchToProps是一个函数,会得到dispatch和ownProps(容器组件的props对象)两个参数。作为函数,应该返回一个对象,该对象的每个键值对都是一个映射,定义了 UI 组件的参数怎样发出 Action*/

const mapDispatchToProps = (dispatch, ownProps) => {
    return {
        onIncreaseClick: () => dispatch({type: 'increase'}),
        onReduceClick: () => dispatch({type: 'reduce'})
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值