likes
comments
collection
share

两小时入门React(以TS和FC为主)

作者站长头像
站长
· 阅读数 22

1. 创建React项目

  • js项目: npx create-react-app js-react
  • ts项目: npx create-react-app ts-react --template typescript

创建成功后启动项目

cd ts-react
npm start

2.元素渲染

ReactDOM.render(element, contain[, callback])
  • 如果提供了可选的回调函数,该回调将在组件被渲染更新后执行
  • 首次调用时,容器节点里的所有DOM元素都会被替换
  • 不会修改容器节点(只会修改容器的子节点),可以在不覆盖现有子节点的情况下,将组件插入已有的DOM节点中
  • 会返回对根组件ReactComponent实例的引用,应避免
  • 组件名称必须大写开头字母React会将以小写字母开头的组件视为原生DOM标签
  • 每次组件更新时render方法都会被调用,但只要在相同的DOM节点中渲染<Clock />就仅有一个Clock组件的class实例被创建使用

3. 数据绑定和事件绑定

数据的绑定和事件的绑定都是通过大括号{}实现的,在TSX语法中,可以在大括号内放置任何有效的JavaScript表达式

<span className="demo-class">{ value }</span>

因为 JSX 语法上更接近 JavaScript 而不是 HTML,所以 React DOM 使用 camelCase(小驼峰命名)来定义属性的名称,而不使用 HTML 属性名称的命名约定。

例如,JSX 里的 class 变成了 className,而 tabindex 则变为 tabIndex

React元素的事件处理和DOM元素的很相似,但是有一点语法上的不同;

  • React事件的命名采用小驼峰式(camelCase),而不是纯小写
  • 使用JSX语法时你需要传入一个函数作为事件处理函数,而不是一个字符串
  • e.stopPropagation()阻止附加到上述标签的事件处理程序触发
  • e.preventDefault()阻止具有它的少数事件的默认浏览器行为

更多的细节使用和描述在e.g上

import React, { FC } from "react";
​
const Demo: FC = () => {
  const msg = "show something";
    // e 是一个合成事件 
  const handleClick = (e: any): void => {
    e.preventDefault(); // 阻止默认行为
    alert("had clicked");
  };
​
  // 数组通过map或者for循环,返回一个列表对象,通过{}渲染
  const demoList = [1, 2, 3, 4, 5, 5, 6].map((i) => (
    // 通过箭头函数指定需要传递的参数,或者通过bind调用来隐式传递事件对象
    <>
      <li onClick={(e) => handleLiClick(i, e)} key={i}>{`li_${i}`}</li>
      {/* <li onClick={handleLiClick.bind(this, i)}>{"s_" + i}</li> */}
    </>
  ));
  const handleLiClick = (i: number, e: any): void => alert(i);
​
  // 条件渲染可以通过if或者&&运算符实现
  const isShow: boolean = true;
  let showButton;
  if (!isShow) showButton = <button>显示按钮</button>;
  else showButton = <a>不显示按钮</a>;
​
  return (
    <>
      <span onClick={handleClick}>{msg}</span>
      <ul>{demoList}</ul>
      <div>
        {isShow && <button>显示按钮</button>}
      </div>
      <div>
          {showButton}
      </div>
    </>
  );
};
​
export default Demo;

4. 组件间通讯 & props

没什么可说的,直接上代码吧,,

import React, { FC } from "react";
​
interface Props {
  id: number;
  msg: string;
  onClick: () => void;
}
// 通过函数的参数传递
const Child: FC<Props> = ({ id, msg, onClick }) => {
  const handleClick = () => {
    onClick(id);
  };
​
  return (
    <>
      <span onClick={handleClick}>{`${id} - ${msg}`}</span>
    </>
  );
};
​
export default Child;
import React, { FC } from "react";
import Child from "./Child";
const Parent: FC = () => {
  const emitClick = (v: number): void => alert(v);
  const id = 12;
  const msg = "props传递";
​
  return <Child id={id} msg={msg} onClick={emitClick} />;
};
​
export default Parent;

5. Hook

useState()

const [state, setState] = useState(initialState)
// initialState 参数只会在组件的初始渲染中起作用,后续渲染时会被忽略,可以传入一个函数初始化复杂state

返回一个state,以及更新state的函数,在初始渲染期间,返回的状态(state)与传入的第一个参数(initialState)值相同,setState函数用于更新state,它接收一个新的state值并将组件的一次重新渲染加入队列,在后学的重新渲染中,useState返回的第一个值始终是更新后最新的state,如果新的state需要通过使用前一个state计算得出,那么可以讲函数传递给setState,该函数将接收先前的state,并返回一个更新后的值

const Times: FC = () => {
  const [count, setCount] = useState(0);
  // useState不会自动合并更新对象,可以使用展开运算符来达到合并更新对象的效果
  const handleMerge = () => {
    setCount(prev => {
      return {...prev, ...updateValues}
    })
  }
  return (
    <>
      Count: {count}
      <button onClick = {setCount(count++)}>加一</button>
      <button onClick = {setCount(c => c*2)}>乘二</button>
    </>
  )
}

useEffect()

useEffect(didUpdate, [...effect])

该Hook接收一个包含命令式、且可能有副作用代码的函数, 赋值给useEffect的函数会在组件渲染到屏幕之后执行,默认情况下,effec将在每轮渲染结束后,在一个延迟事件中被调用,这使得它适用于许多常见的副作用场景,比如设置订阅和事件处理等情况,然而并不是所有的effect都可以被延迟执行(一个对用户可见的DOM变更就必须在浏览器执行下一次绘制前被同步执行,这样用户才不会感觉到视觉上的不一致),虽然 useEffect 会在浏览器绘制后延迟执行,但会保证在任何新的渲染前执行。在开始新的更新前,React 总会先清除上一轮渲染的effect

最常用的用法就是向服务端请求数据,代替class的生命周期

useEffect(
  () => {
    const subscription = props.source.subscribe();
    /**
    * @params Fuction callBack()
    * effect可以返回一个函数,在组件被清除前调用,可以用来清除effec,e.g: 订阅,计时器
    * 如果组件多次渲染,则在执行下一个effect之前,上一个effect就已经被清除
    */
    return () => {
      subscription.unsubscribe();
    };
  },
  // 通过给useEffect传递第二个参数(数组值),来让函数仅在source变化时被调用
  // 如果想只运行一次effect,可以传递一个空数组来实现
  [props.source],
);

useContext()

const value = useContext(MyContext)

简单描述就是事件通讯机制,也有叫状态共享,在需要传值的父组件内注册Contextprovider,在子组件进行消费,它接受一个context对象(React.createContext的返回值)并返回该context的当前值,当前的context值由上曾组件中距离当前组件最近的<MyContext.Provider>value prop决定

主要使用于非父子组件传值, 嵌套组件内避免逐层传递,当然也有其他的替代方案: eventsredux

export const UserContext = React.createContext('') 
​
export default function Parent() {
  return (
    <>
      <UserContext.Provider value={"hugo"}>
        <Child></Child>
      </UserContext.Provider>
    </>
  );
}
import {UserContext} from './Parent';
​
export default function Child(){
  // 直接通过useContext获取传递过来的值
  const userName = useContext(UserContext); // userName: hugo
  return (
    <>
    // 通过Consumer标签来获取
      <UserContext.Consumer>
           {(n: string) => (
               <div>props Name: {n}</div>
           )}
        </UserContext.Consumer>
    </>
  )
}

非基础Hooks

  • useReduce()

    状态管理功能,useState的替代方法,redux的简化版(不包含中间件和time travel)

    // const [state, dispatch] = useReducer(reducer, initialArg, init)
    // 一个简单的计数器demo
    export const CountDemo: FC = () => {
      const [count, dispath] = useReducer((state, action) => {
        // state当前状态值, action dispath调用时传递的参数
        switch(action){
          case 'add': 
            return state + 1;
          case 'subtract':
            return state - 1;
            // 返回新的状态值 -> count
        }
        // 初始化为0
      }, 0)
      
      // 惰性初始化,传递一个初始化函数作为reducer的第三个参数
      // 方便初始化移出,e.g: initial值为props时,方便修改重置
      const init = (initial: number): number => initial;
      
      const [count1, dispatch1] = useReducer((s, a) => {}, 0, init)
      
      return (
        <>
          <span>当前数量: {count}</span>
          <button onClick={() => dispatch('add')}>加</button>
          <button onClick={() => dispatch('subtract')}>减</button>
        </>
      )
    }
    
  • useCallBack()/useMemo()

    两个一起解释,直接给结论,作用时性能优化,并是不必须(非大型项目),只有在依赖变化时,才会调用穿进去的回调函数去重新计算结果,起到缓存的作用(e.g: 回调方法里返回的子组件不会因为其他state变动导致重新渲染)

    // 返回的是一个函数
    const memoizedCallback = useCallback(() => doSomething(...args), [...args])
    // 返回的是一个值 e.g return <Component />
    const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b])
    
  • useRef()

    绑定一个可变的ref对象,其current属性保持最新的值,返回的 ref 对象在组件的整个生命周期内持续存在,举个例子: 在定时器中获取state后,再通过useState更新值,定时器结束获取到的state是更新前的值

    当 ref 对象内容发生变化时,useRef不会通知你。变更 .current 属性不会引发组件重新渲染,如果想要在 React 绑定或解绑 DOM 节点的 ref 时运行某些代码,则需要使用回调 ref 来实现

    const refContainer = useRef(initialValue);
    ​
    // 通过useRef实时获取最新的DOM节点
    const domRef = useRef<HTMLInputElement>(null)
    <input ref={domRef} type="text" />
    ​
    
  • useLayoutEffect()

    useEffect, 但它会在所有的DOM变更之后同步调用effect,可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。

6.附录 - 事件集合

事件类型事件参考对应属性
剪贴板 Clipboard EventsonCopy | onCut | onPasteclipboardData: DOMDataTransfer
复合事件 Composition EventsonCompositionEnd | onCompositionStartdata: string
键盘事件 Keyboard EventsonKeyDown | onKeyPress | onKeyupcharCode: number | key: string | keyCode: number | locale: string | location: number ...
焦点事件 Focus EventsonFocus | onBlurrelatedTarget: DOMEventTarget
表单事件 FormEventsonChange | onInput | onInvalid | onReset |onSubmit
通用事件onError | onLoad
鼠标事件onClick | onMouseMover |onDrageOver ...pageX: number | pageY: number | ctrlKey: boolean | button: number |clientX: number | clientY: number ...
UI事件onScrolldetail: number | view: DOMAbstractView
指针事件onPointDown | onPointMove | onPointOver ...pointerId: number | width: number | height: number | pressure: number | tiltX: number | tiltY: number
转载自:https://juejin.cn/post/7069634797127073799
评论
请登录