美文网首页
react学习第三篇:state,hooks,context

react学习第三篇:state,hooks,context

作者: 云鹤道人张业斌 | 来源:发表于2021-05-11 18:20 被阅读0次

    一、props和state的区别

    1. props是对外的接口(组件间传递数据),state是对内的接口(组件内传递数据)

    2.state是私有的,可以认为是组件的“私有属性”,使用setState方法修改state

    this.setState({
                  isOpen: !this.state.isOpen,
                });
    
    1. 构建函数constructor是唯一可以初始化state的地方
      constructor(props: Props) {
        // 调用React.Component基础类的构建函数
        super(props);
        this.state = {
          isOpen: false,
        };
      }
    

    4.setState 是同步还是异步? 同步的
    setState是同步执行,异步更新,react会优化真正修改时机,可能会合并多个修改后再渲染
    tip:异步更新是一种结果的表现形式,是因为setState的值被缓存了。执行还是同步的

    state处理发生在生命周期变化的时候
    count : 0
    1.
    setState后count未变化
    <button onClick={() => {
              this.setState({count: this.state.count + 1})
            console.log(this.state.count)   // => 0
             }}>{this.state.count}加一</button>
    2. 
    setState第二参数:函数中可以获取改变后的值
    <button onClick={() => {
              this.setState({count: this.state.count + 1}, () => {
                console.log(this.state.count)  // => 1
              })
              }}>{this.state.count}加一</button>
    3. 
    两次相同的setState 只会使用上一次操作的值来赋值到界面
    <button onClick={() => {
              this.setState({count: this.state.count + 1})
              this.setState({count: this.state.count + 1})
      // => 界面只会输出1
            }}>{this.state.count}加一</button>
    4. 
    解决连续setState:可以将setState的第一个参数改为函数,获取上一次值,改变这个值就会连续生效了
    或者将两个setState放在setTimeout里
    <button onClick={() => {
              this.setState((preState, preProps) => {
                return {count: preState.count + 1}
              })
              this.setState((preState, preProps) => {
                return {count: preState.count + 1}
              })
      // => 界面输出2
            }}>{this.state.count}加一</button>
    
      // => 界面输出2
    setTimeout(() => {
             this.setState({count: this.state.count + 1})
              this.setState({count: this.state.count + 1})
            });
    

    二、事件的处理

    1. 箭头函数
    2. 参数类型可以将鼠标放在onClick上看到


      企业微信截图_1620294005701.png
    这里必须是箭头函数,this才指向的是外部
      handleClick = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
        e.target 是点击元素
       e.currentTarget  绑定事件的元素
       对target类型作判断
    if ((e.target as HTMLElement).nodeName === 'SPAN') {
          this.setState({
            isOpen: !this.state.isOpen,
          });
        }
      };
    
      render() {
        return (
          <div>
            <button onClick={this.handleClick}>购物车</button>
          </div>
        );
      }
    }
    
    1. 使用bind达到实现箭头函数效果
      constructor(props: Props) {
        // 调用React.Component基础类的构建函数
        super(props);
        this.state = {
          isOpen: false,
        };
        this.handleClick = this.handleClick.bind(this)
      }
    
      handleClick (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
        this.setState({
          isOpen: !this.state.isOpen,
        });
      };
    

    三、hooks (钩子)

    1. 可以在非类组件中使用state,函数组件中需要处理副作用,用钩子把外部代码钩进来
    2. hooks 一律使用use作为前缀命名,常用钩子useState,useEffect,useContext(跨组件数据传递),useReducer(管理全局状态)useCallback(处理回调副作用),useRef(返回引用对象,不变)
    3. 有了hooks + 函数式组件, 不再需要类组件
      类组件冗长且复杂,难以复用。想要复用只能用 无状态组件 或 HOC (高阶组件)
    1. 状态钩子 useState()
     这是react自带的hook函数,声明组件状态
     返回值是一个只有两个元素的数组:[状态,状态更新函数(函数命名规则:set + 状态名称)]
    // 这种形式就不需要在jsx中用this.state.count 取值了
    import React, { useState } from "react"; // 引入useState 
    const App:React.FC = (props)=> {
      const [count, setCount] = useState<number>(0)
        return (
          <div >
            <button onClick={() => {
              setCount(count + 1)
            }}>{count}加一</button>
          </div>
        );
      
    }
    
    
    类组件中生命周期,state的改变,请求等操作对于函数(一个输入,只有一个返回值)来说都是副作用,
    既然变为了函数式组件,就需要某种hooks来消除副作用
    
    2. 副作用钩子 useEffect()
    (1) 可以取代生命周期函数componentDidMount,componentDidUpdate和componentWillUnmount
    (2)给函数式组件添加副作用(side effect)
      const [robotGallery, setRobotGallery] = useState<any>([])
    
      第二参数数组中写入变化的值,只有变化才触发第一参数的执行
      useEffect(() => {
        document.title = `${count}次`
      }, [count])
    如果第二参数是空数组,就是在模拟类组件的componentDidMount,只会在第一次渲染执行
    如果没有第二参数,就是在模拟类组件的componentDidUpdate,那在每次ui改变后都会执行该副作用函数(谨慎)
      useEffect(() => {
        fetch('https://jsonplaceholder.typicode.com/users')
        .then(res => res.json())
        .then(data => setRobotGallery(data))
    // 使用async await
    // const fetchData =  async ()  => {
    //   const res = await fetch('https://jsonplaceholder.typicode.com/users')
     //  const data = await res.json()
    //   setRobotGallery(data)
     //   }
    //    fetchData()
      }, [])
    
    1. 使用上下文context (Provider,Consumer)跨组件传递 (vue有provide inject)
      或者用hooks useContext接收数据
    父组件
    // 使用上下文关系状态context跨组件传递数据
    const defaultValue = {
      testName: 'jjj'
    }
    导出创造的上下文
    export const nameContext = React.createContext(defaultValue)
    ReactDOM.render(
      <React.StrictMode>
    使用Provider 包裹子组件,并传递数据defaultValue
        <nameContext.Provider value={defaultValue} >
        <App />
        </nameContext.Provider>
      </React.StrictMode>,
      document.getElementById('root')
    );
    
    
    多级子组件 引入
    1. 使用Consumer组件包裹组件内容。value就是传递来的数据
    import {nameContext} from '../index'
    const Robot: React.FC<RobotProps> = ({ id, name, email }) => {
      return (
        <nameContext.Consumer>
          {(value) => {
            return <li>
            <p>{value.testName}</p>
          </li>
          }}
        
        </nameContext.Consumer>
      );
    };
    
    2.   hooks :useContext  深度注入接受数据
    引入useContext  
    import React, {useContext} from "react";
    const Robot: React.FC<RobotProps> = ({ id, name, email }) => {
      const value = useContext(nameContext)
      return (
        <li>
            <p>{value.testName}</p>
          </li>
      );
    };
    

    5 。全局context

    1. 新建appState.tsx. 导出AppStateProvider组件
    // appState.tsx
    import React, {useState} from 'react'
    
    // 使用上下文context跨组件传递数据
    interface AppStateValue {
       username: string,
       shoppingCart: {items: {id: number, name: string}[]}
    }
    const defaultValue: AppStateValue = {
       username: '123',
       shoppingCart: { items: [] }
     }
     
    export const appContext = React.createContext(defaultValue)
    //创建的context的初始值可以是undefined,
    //类型定义React.Dispatch<React.SetStateAction<AppStateValue>> 
    //在 <appSetStateContext.Provider > 组件上可以找到
    export const appSetStateContext = React.createContext<React.Dispatch<React.SetStateAction<AppStateValue>> | undefined>(undefined)
    
    创建context组件Provider用来传递数据
    export const AppStateProvider: React.FC = (props) => {
       const [state, setState] = useState(defaultValue)
    
       Provider组件只能用value来传递数据,
       return <appContext.Provider value={state}>
    // 需要修改shoppingCart,再创建一个context 用来传递setState方法。
    // 两个Provider 嵌套
           <appSetStateContext.Provider value={setState}>
           {props.children}
           </appSetStateContext.Provider>
           </appContext.Provider>
    }
    
    1. 在父组件引入AppStateProvider组件,包裹起子组件
    import {AppStateProvider} from './AppState'
    
    ReactDOM.render(
      <React.StrictMode>
        <AppStateProvider>
        <App />
        </AppStateProvider>
      </React.StrictMode>,
      document.getElementById('root')
    );
    
    1. 在子组件中通过点击事件更新全局数据
    import React, {useContext} from "react";
    import {appContext, appSetStateContext} from '../AppState'
    
    interface RobotProps {
      id: number;
      name: string;
      email: string;
    }
    // 组件传递数据使用的是props,接口定义传入的参数类型
    const Robot: React.FC<RobotProps> = ({ id, name, email }) => {
    useContext获取不同的context中的数据
      const value = useContext(appContext)
      const setState = useContext(appSetStateContext)
    点击事件,执行传过来的setState 方法,实现改变了全局的shoppingCart数据
      const addToCart = () => {
        if (setState) {
          setState(state => {
            return {
              ...state,
               shoppingCart: {
                 items: [
                  ...state.shoppingCart.items,
                   {id, name}
                 ]
               }
            }
          })
        }
      }
      return (
        <li>
            <img alt="robot" src={`https://robohash.org/${id}`} />
            <h2>{name}</h2>
            <p>{email}</p>
            <p>作者:{value.username}</p>
            <button onClick={addToCart}>加入购物车</button>
          </li>
      );
    };
    

    相关文章

      网友评论

          本文标题:react学习第三篇:state,hooks,context

          本文链接:https://www.haomeiwen.com/subject/vwrddltx.html