万字总结记录我的React学习路程(附各个代码参考)

这篇博客详尽记录了作者学习React的过程,涵盖了JSX、组件、状态管理和通讯、路由、React全家桶等内容。文章通过实例深入浅出地讲解了React的关键概念,包括class和function组件的状态管理、props通信、Redux的使用、高阶组件以及React Hooks的运用,尤其强调了React Hook在解决状态逻辑复用和组件复杂性方面的作用。
摘要由CSDN通过智能技术生成

说在最前面

本文是自己最近再学习React的总结和记录。尽力把每个示例代码都写了出来,尽力写的通俗易懂一些,也算是锻炼自己的讲述能力,据说给别人讲的话学的会更快,所以写下此文。

如有错误或者其他,还望各位批评指正

JSX

简介

在JS里面写的一些标签称为JSX语法

基本语法

在返回的时候不需要加引号

import React from 'react';
import ReactDOM from 'react-dom';
import logo from './logo.svg';

// 基本
const jsx = <h1>hello react</h1>

// 变量
const name = '123456'
const jsxName = <h1>{ name }</h1>

      // 函数
const user = { firstName: 'tom', lastName: 'jerry' };

function formatName(user) {
  return user.firstName + ' ' + user.lastName;
}

const jsxFunction = <h2>{ formatName(user) }</h2>

      // 对象
const greet = <p>hello, Jerry</p>
const jsxObject = <h2>{ greet }</h2>

      
     // 三元表达式
const showTitle = true;
const title = showTitle ? <h2>{ name }</h2> : null;
const jsxOthers = (<div>{
  /* 条件语句句 */
} { title }
</div>);

// 循环
const arr = [1, 2, 3].map(num => <li key={ num }>{ num } </li>)
const jsxArray = (<div>    {/* 数组 */ }
  <ul>{ arr }</ul>
</div>)


// 注意
const jsxAtt = (<div>
  {
    /* 属性:静态值⽤用双引号,动态值⽤用花括号;class、for等 要特殊处理理。
    * 像class需要写成className,for需要写成htmlFor,并且属性名需要采用驼峰命名法
    * */
  }
<img src={ logo } style={ { width: 100 } } className="img"/></div>)

// 函数传参
function greeting(name) {
  if (name) {
    return <h1>Hello, {name}!</h1>;
  }
  return <h1>Hello, Stranger.</h1>;
}
let name2 = '测试';
const element = greeting(name2);

// 循环
let names = ['张三','李四','王五'];
let elements = [];
for(let i=0;i<names.length;i++){
  elements.push(<li>{names[i]}</li>);
}
export { jsx, jsxName, jsxFunction, jsxObject, jsxOthers, jsxArray,jsxAtt,element,elements }

组件

​ 可以将UI切分成一些独立的、可复用的部件,

class 组件

​ class组件通常拥有状态生命周期继承于Component,实现 render方法

基本

import React, {
    Component } from 'react';
class Home extends Component {
   
  render() {
   
    return <h1>Hello, Class Components</h1>;
  }
}

类组件中的状态管理(秒表实例)

import React, {
    Component } from 'react';
class State extends Component {
   
    
  // 使用state属性维护状态,在构造函数中初始化状态
  constructor(props) {
   
    super(props);
    this.state = {
    date: new Date(), number: 0 };
    // this.changeNumber = this.changeNumber.bind(this)
  }

  // 组件挂载时启动定时器每秒更新状态
  componentDidMount() {
   
    this.timerID = setInterval(() => {
   
      // 使⽤用setState方法更新状态
      this.setState({
    date: new Date() });
    }, 1000);
  }

  // 组件卸载时停止定时器
  componentWillUnmount() {
   
    clearInterval(this.timerID);
  }

  // 这里不绑定this 会报错 两种方法 第一种 用箭头函数。第二种在constructor进行绑定声明
    // setState是异步执行的。执行这个结果是 log 是2不是3 证明是异步执行
  changeNumber = () => {
   
    this.setState({
    number: this.state.number + 1 } )
    this.setState({
    number: this.state.number + 2 } )
    // 这里不能进行直接修改
    console.log(this.state.number)
  }
  // 同步改变
  changeNumber2 = ()=> {
   
    this.setState((nextState)=>{
    return {
    number: nextState.number + 1 } } )
    this.setState((nextState)=>{
    return {
    number: nextState.number + 2 } } )
    console.log(this.state.number)

    // 使用定时器
    // setTimeout(() => {  this.setState((nextState)=>{ return { number: nextState.number + 1 } } );  console.log(this.state.counter); }, 0);

    // 原生事件中修改状态
    // componentDidMount(){    document.body.addEventListener('click', this.setState((nextState)=>{ return { number: nextState.number + 1 } } ), false) }
  }
  render() {
   
    return (
        <div>
          {
    this.state.date.toLocaleTimeString() }
          <p>{
    this.state.number }</p>
          <p>setState是异步执行的</p>
          <button onClick={
   this.changeNumber}>异步改变number</button>
          <button onClick={
   this.changeNumber2}>同步改变number</button>
        </div>
    )
  }
}
状态的设置和改变

constructor设置状态。使用setState来改变状态。示例如上

关于setState

注意,setState是 异步的!!!!

如何证明?

见示例代码中changeNumber的描述和实现

如何实现同步?

常见的有三种方法:

  1. 利用函数传递参数
  2. 使用定时器
  3. 直接找到DOM元素直接修改

上述三种方法在示例代码中都可以找到。

函数的this 绑定

一般有两种方法

  1. constructor里面用bind方法绑定。例如this.changeNumber = this.changeNumber.bind(this)
  2. 利用ES6的箭头函数。示例changeNumber = () => { // 执行代码 }。(**原因解释:**箭头函数默认指向定义它时,所处上下文的对象的this指向)

function组件

基本

import React from "react"; 
import User from "./pages/User";
function App() {
     
    return (    
        <div>      
        	<User />    
        </div>  
    ); 
}
export default App;

后文的 react HOOKs会做详细解释

组件通讯

props通讯

适用于父子组件通讯

// index.js 
ReactDOM.render(<App title="测试代码" />, document.querySelector('#root'));
// App.js 
<h2>{
   this.props.title}</h2>  // 输出为 测试代码
Props 的只读性

官网原话 :组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props

状态提升

官方解释: 通常,多个组件需要反映相同的变化数据,这时我们建议将共享状态提升到最近的共同父组件中去。

实例代码

import React from 'react'
class Child_1 extends React.Component{
   
  constructor(props){
   
    super(props)
  }
  render(){
   
    return (
        <div>
          <h1>{
   this.props.value+'child 1'}</h1>
        </div>
    )
  }
}
class Child_2 extends React.Component{
   
  constructor(props){
   
    super(props)
  }
  render(){
   
    return (
        <div>
          <h1>{
   this.props.value+'child 2'}</h1>
        </div>
    )
  }
}
class Parent extends React.Component {
   
  constructor(props){
   
    super(props)
    this.state = {
   
      txt:"我是父组件"
    }
    this.handleChange = this.handleChange.bind(this)
  }
  handleChange(e){
   
    this.setState({
   
      txt:e.target.value
    })
  }
  render(){
   
    return (
        <div>
          <input type="text" value={
   this.state.txt} onChange={
   this.handleChange}/>
          <p>{
   this.state.txt}</p>
          <Child_1 value={
   this.state.txt}/>
          <Child_2 value={
   this.state.txt}/>
        </div>
    )
  }
}
export default Parent

context 跨组件通讯

跨层级组件之间通信

单层传递
// ContextFather.js
import React from 'react';
import ContextChild from './child';
const Context = React.createContext()
const Provider = Context.Provider
const Consumer = Context.Consumer
const data = {
   
  name: '我是father数据',
}

function ContextApp() {
   
  return (
      <div>
        <Provider value={
    data }>
          <Consumer>
            {
   
              value => <ContextChild {
   ...value}/>
            }
          </Consumer>
        </Provider>
      </div>
  )
}
export default ContextApp

//  child.js
import React, {
    Component } from 'react';

class ContextChild extends Component {
   
  constructor(props) {
   
    super(props);
  }
  render() {
   
    console.log(this.props)
    return (<h1>hello {
   this.props.name}</h1>);
  }
}
export default ContextChild

这样可以获取到传递过来的name

多层传递
// Context.js
import React from 'react';

const Context = React.createContext()
const Provider = Context.Provider
const Consumer = Context.Consumer

export {
   Provider,Consumer}
// ContextFather
import React from 'react';
import ContextChild from './child';
import {
   Consumer,Provider} from './Context';

const data = {
   
  name: '我是father数据',
}

function ContextApp() {
   
  return (
      <div>
        <Provider value={
    data }>
      // 这个层级不需要就不用写 Consumer
          <ContextChild/>
        </Provider>
      </div>
  )
}
export default ContextApp

// child.js
import React, {
    Component } from 'react';
import ContextChild2 from './child2';
import {
   Consumer} from './Context';

class ContextChild extends Component {
   
  constructor(props) {
   
    super(props);
  }
  render() {
   
    return (
        <div>
          <h1>我是第一层</h1>
        // 这里获取的是一个空值,没有数据
          <h2>{
   this.props.name}</h2>
        // 这个层级需要就吧数据传递过去
          <Consumer>
            {
   
              value => <ContextChild2 {
   ...value}/>
            }
          </Consumer>

        </div>
    )
  }
}
export default ContextChild

// child2.js
import React, {
    Component } from 'react';

class ContextChild2 extends Component {
   
  constructor(props) {
   
    super(props);
  }
  render() {
   
    console.log(this.props)
    console.log('我是第二层的')
    return (
        <div>
          <h1>我是第二层的</h1>
          <h1>{
   this.props.name}</h1>
        </div>
    );
  }
}
export default ContextChild2
特别注意
  1. 不能有多个提供者,例如在每个文件里 写上Context.js里面的语句。则会获取不到数据。不同的provider提供的值不一致
  2. 在React的官方文档中,Context被归类为高级部分(Advanced),属于React的高级API,但官方并不建议在稳定版的App中使用Context。不过,这并非意味着我们不需要关注Context。事实上,很多优秀 的React组件都通过Context来完成⾃自己的功能,比如react-redux

redux 类似Vuex

没学过Vue的可以理解为全局状态管理,即你在哪里都能访问的到(我个人是这么理解的)

知识扩展
Reducer

解释: reducer 就是一个纯函数,接收旧的 stateaction,返回新的 state

reduce

本例子来源于MDN

const array1 = [1, 2, 3, 4];
const reducer = (total, currentValue) => total + currentValue;

// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10

// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output: 15
compose(没太理解)

compose就是执行一系列的任务(函数)

思考

如何输出1 2 3

function f1() {
     console.log("1"); } 
function f2() {
     console.log("2"); }
function f3() {
     console.log("3"); }

第一种

f1();f2();f3();

第二种

f3(f2(f1()))

第三种

function compose(...funcs) {
   
  // funcs 是一个函数列表
  if (funcs.length === 0) {
   
    console.log('empty');
  } else if (funcs.length === 1) {
   
    return funcs[0];
  } else {
   
    return funcs.reduce(
        (left, right) => (...args) => {
   
          // console.log(args)
          // console.log(right)
          // console.log(left)
          right(left(...args)) // 相当于 f3(f2(f1()))
        }
    );
  }
}
compose(f1,f2,f3)()
Redux
使用步骤
  1. 需要一个store来存储数据
  2. storereducer初始化state并定义state修改规则
  3. 通过dispatch一个action来提交对数据的修改
  4. action提交到reducer函数里,根据传入的actiontype,返回新的 state

实例代码

// 创建store  store/ReduxStore
import {
    createStore } from 'redux';
// 创建数据并指定行为
const counterReducer = (state = 0, action) => {
   
  switch (action.type) {
   
    case 'add':
      return state + 1
    case 'minus':
      return state - 1
    default:
      return state
  }
}
// 创建一个store
const store = createStore(counterReducer)
export default store
// ReduxPage
import React, {
    Component } from 'react';

import store from '../store/ReduxStore';

export default class ReduxPage extends Component {
   
  componentDidMount() {
    // 挂在之后
    store.subscribe(() => {
    // 执行订阅
      console.log('subscribe');
      this.forceUpdate(); // 重新render
      //this.setState({}); // 这个也可以,内部方法也是forceUpdate
    })
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值