Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React #8

Open
magicdawn opened this issue Dec 1, 2014 · 18 comments
Open

React #8

magicdawn opened this issue Dec 1, 2014 · 18 comments

Comments

@magicdawn
Copy link
Owner

magicdawn commented Dec 1, 2014

Links

react Component Specifications

通过react.render创建component时提供定义的对象

  • render()
    返回一个 single child component

    return null or false , 表示不想渲染任何东西 , 此时 this.getDOMNode() => false

  • getInitialState()
    在组件被加载之前调用一次,此函数的返回值作为this.state 的值

  • getDefaultProps()
    只调用一次,并cache起来,每个实例的getDefaultProps() 返回的是同一个

  • propTypes
    说明prop的类型

  • mixins
    Array类型,允许在不同的组件之间共享行为...

???????????
???????????
???????????

  • statics
    可以定义静态方法,通过 ComponentClass.static_method 调用
  • displayName
    used in debugging messages

生命周期相关方法

  • componentWillMount
    只调用一次,both on client and server ??? react是client端吧???
    在初始render之前

  • componentDidMount
    只在初始render之后调用一次,在这个时候,component有了真实的DOM节点,通过 this.getDOMNode() 获取

  • componentWillReceiveProps
    当一个组件正在接受新的props时调用,在 render() 的时候没有调用...

    使用这个方法来在render() 方法之前,根据props来设置state

    componentWillReceiveProps: function(newProps){
        // old props access via this.props
        // 使用 this.props 获取旧的props
        // 因为是 willReceive,新的传过来
    }
  • shouldComponentUpdate
    收到新的props或者state时要重新调用render之前调用这个

  • 最开始的render调用之前,没有调用此方法

  • 调用forceUpdate时,不会调用此方法

接收的两个参数,(newProps,newState) 用来判断,是否需要重新update

  • comonentWillUpdate
    当上面那个should返回true的时候,接着调用这个,可以设置一些state
  • componentDidUpdate
    在组件更新之后,可以使用这个方法来操作真实的节点

两个参数 (prevProps,prevState)

  • componentWillUnmount
    组件从DOM里卸载之前立即调用,做一些清理工作,例如可以清理一些timer,一些在 componentDidMount 里创建的DOM节点
@magicdawn magicdawn self-assigned this Dec 1, 2014
@magicdawn magicdawn removed their assignment Dec 2, 2014
@magicdawn
Copy link
Owner Author

http://facebook.github.io/react/docs/events.html

Event System

SyntheticEvent 合成的 事件,就是react给DOM Event对象的wrapper

属性

  • nativeEvent 获取被wrap的event对象
  • bubbles,cancelBubble,eventPhase事件冒泡相关
  • stopPropagation() 阻止冒泡
  • preventDefault() 阻止默认操作
  • target,currentTarget target相关
  • timeStamp
  • type
  • isTrusted ??????????????????

@magicdawn
Copy link
Owner Author

http://www.w3.org/TR/dom/

event.isTrusted

Returns true if event was dispatched by the user agent, and false otherwise.

什么时候是false呢

@magicdawn
Copy link
Owner Author

mixins

wikipedia 上说

In object-oriented programming languages, a mixin is a class which contains a combination of methods from other classes. How such a combination is done depends on the language, but it is not by inheritance. If a combination contains all methods of combined classes it is equivalent to multiple inheritance.

react document 上说

Components are the best way to reuse code in React, but sometimes very different components may share some common functionality. These are sometimes called cross-cutting concerns. React provides mixins to solve this problem.

然后是例子

var SetIntervalMixin = {
  componentWillMount: function() {
    this.intervals = [];
  },
  setInterval: function() {
    this.intervals.push(setInterval.apply(null, arguments));
  },
  componentWillUnmount: function() {
    this.intervals.map(clearInterval);
  }
};

var TickTock = React.createClass({
  mixins: [SetIntervalMixin], // Use the mixin
  getInitialState: function() {
    return {seconds: 0};
  },
  componentDidMount: function() {
    this.setInterval(this.tick, 1000); // Call a method on the mixin
  }
});

其实minxin 属性做的操作就是
this.mixins.forEach(_.extend.bind(_,this))

把mixins里面的方法extend到createClass方法的specifation参数里面

@magicdawn
Copy link
Owner Author

@magicdawn
Copy link
Owner Author

reflux

if(module){
    var Reflux = require('reflux');
}

var actions = Reflux.createActions([
    'A','B'
].map(function(s) { return 'action'+s }))

var a_store = Reflux.createStore({
    init:function() {
        this.listenToMany(actions)
    },

    onActionA: function() {
        console.log(arguments);
    },

    actionA: function() {
        console.log("都有看你怎么办...");
    },

    actionB: function(arg1,arg2) {
        console.log(arg1,arg2);
    }
})

actions.actionA("hello","world")
actions.actionB("this is arg1","this is arg2")

console.log(a_store);

结果

{ subscriptions: 
   [ { stop: [Function], listenable: [Object] },
     { stop: [Function], listenable: [Object] } ],
  emitter: {},
  eventLabel: 'change',
  init: [Function],
  onActionA: [Function],
  actionA: [Function],
  actionB: [Function] }
{ '0': 'hello', '1': 'world' }
this is arg1 this is arg2

store listenTo 一个action,调用action -> store -> component -> DOM

@magicdawn
Copy link
Owner Author

reflux

gist : https://gist.github.com/spoike/ba561727a3f133b942dc

action

  1. 调用action() 就是 引发 action 事件
  2. var cancel = action.listen(function_handler) 为action 事件绑定 function_handler , 再调用一次 cancel() 会removeListener

store

  1. store.listenTo(一个listenable,一个function_handler) listenable意思是,有listen方法,调用
    listenable.listen(function_handler) , 例如 listenTo(一个action),调用的就是 action.listen(function_handler)
  2. store.listen() , 添加change事件的handler
  3. store.trigger 引发 change事件

@magicdawn
Copy link
Owner Author

magicdawn commented Oct 24, 2015

state 与 props

一开始有state, state可变, setState() 会导致重新绘制, 然后

import { createClass } from 'react'
let A = createClass({
  render: ()=>{
     retrun (
      <B  foo={ this.state.bar } />
    )
  }
})

就是说, A.state.bar 传给B.props.foo, 这样A重绘时, 导致B重绘, 类似 entry

区别

http://www.ruanyifeng.com/blog/2015/03/react.html

由于 this.props 和 this.state 都用于描述组件的特性,可能会产生混淆。一个简单的区分方法是,this.props 表示那些一旦定义,就不再改变的特性,而 this.state 是会随着用户互动而产生变化的特性。

区别 from 官网

getDefaultProps

Invoked once and cached when the class is created. Values in the mapping will be set on this.props if that prop is not specified by the parent component (i.e. using an in check).
This method is invoked before any instances are created and thus cannot rely on this.props. In addition, be aware that any complex objects returned by getDefaultProps() will be shared across instances, not copied.

只在创建 class 的时候调用一次, 之后缓存, 如果 parent 没有设置 this.props.foo, 则会采用 default props.
如果返回了复杂的对象, 多个实例将会 share 这个.

getInitialState

Invoked once before the component is mounted. The return value will be used as the initial value of this.state.

在组件 mount 之前调用一次

在 getInitialState 中使用 props, 是反模式

  1. 如果只是拿 props 来计算一些值, 那么使用 props 来存储在 state 是反模式, 这种情况不需要 state
  2. 如果拿 props 生成 state 默认值, 是可以的, 不是反模式

@magicdawn
Copy link
Owner Author

redux

https://github.com/rackt/redux

core notes

The whole state of your app is stored in an object tree inside a single store.
The only way to change the state tree is to emit an action, an object describing what happened.
To specify how the actions transform the state tree, you write pure reducers.

usage

TODO

@magicdawn
Copy link
Owner Author

magicdawn commented Aug 20, 2017

react 常用包

warning

warning(false, 'warning')

当前面值为 false, 显示 warning

invariant

https://github.com/zertosh/invariant

invariant(someTruthyVal, 'This will not throw');
// No errors

invariant(someFalseyVal, 'This will throw an error with this message');
// Error: Invariant Violation: This will throw an error with this message

hoist-non-react-statics

将非js保留关键字, 非 react 关键字的 Component 属性加到上一层

hoist(UpperComponent, Component, blacklist), 常用于 HoC
blacklist example { someKey: true }

https://github.com/mridgway/hoist-non-react-statics

@magicdawn
Copy link
Owner Author

find React instance by dom

https://stackoverflow.com/questions/29321742/react-getting-a-component-from-a-dom-element-for-debugging

像 Vue 一样, dom.__react__ 找到 React instance

Object.defineProperty(Node.prototype, '__react__', {
  enumerable: false,
  configurable: true,
  get() {
    let dom = this
    let key = Object.keys(dom).find(key=>key.startsWith("__reactInternalInstance$"));
    let internalInstance = dom[key];
    if (internalInstance == null) return null;

    if (internalInstance.return) { // react 16+
        return internalInstance._debugOwner
            ? internalInstance._debugOwner.stateNode
            : internalInstance.return.stateNode;
    } else { // react <16
        return internalInstance._currentElement._owner._instance;
    }
  }
})

@magicdawn
Copy link
Owner Author

magicdawn commented Jan 2, 2020

empty prop value

facebook/react#2166
https://stackoverflow.com/questions/34809178/react-js-default-prop-is-not-used-with-null-is-passed

null & undefined

  • null: 表示一个 object value 的缺失, 不会使用 defaultProps, 但是对于 isRequeired 不能传 null 值.
  • undefined: 表示一个变量没有被赋值, 会使用 defaultProps

@magicdawn
Copy link
Owner Author

useEffect & useLayoutEffect

https://zhuanlan.zhihu.com/p/348701319

看了依然没看懂....
背一下吧

  • useLayoutEffect 在内容渲染到屏幕之前(???????), 同步执行
  • useEffect, 在渲染之后, 异步执行

@magicdawn
Copy link
Owner Author

2022 react 生态

https://v2ex.com/t/850921?p=2

@magicdawn
Copy link
Owner Author

状态管理

valtio

上一条帖子中发现的, 还算好用 #141

@magicdawn
Copy link
Owner Author

magicdawn commented May 24, 2022

useSyncExternalStore

解释
如何理解 React 18 中的 useSyncExternalStore ? - 3Shain的回答 - 知乎
https://www.zhihu.com/question/502917860/answer/2252338680

API

useSyncExternalStore(subscribe, getSnapshot)

使用

因为这个 API 以 hooks 的形式存在, react-redux 的 connect hoc, 也是使用 FC 包了一层
https://github.com/reduxjs/react-redux/blob/v8.0.2/src/components/connect.tsx#L518

@magicdawn
Copy link
Owner Author

magicdawn commented Jun 25, 2022

react@18 + TypeScript

breaking change: 之前不需要手动标注 children prop, @types/react@18 需要手动添加 children type

interface Props {
+  children?: React.ReactNode;
}

@magicdawn
Copy link
Owner Author

magicdawn commented Jun 25, 2022

@types/react / react-typescript-cheatsheet

JSX.IntrisicElements

intrinsic 固有的

type AProps = ComponentProps<'a'>
type AProps2 = JSX.IntrinsicElements['a']

let p1: AProps = {
  href: '',
}
let p2: AProps2 = {
  title: '',
}

p2 = p1

ComponentType / ElementType

type ElementType<P = any> =
    {
        [K in keyof JSX.IntrinsicElements]: P extends JSX.IntrinsicElements[K] ? K : never
    }[keyof JSX.IntrinsicElements] |
    ComponentType<P>;
type ComponentType<P = {}> = ComponentClass<P> | FunctionComponent<P>;
  • 可以表示一个组件的类型
  • 下面可以看到 ElementType = IntrisicElements | ComponentType (伪代码)
const x: ElementType = 'a'
const y: ElementType = 'link'
const z: ElementType<typeof Demo> = Demo
const foo: ElementType = Demo

function Demo() {
  return <div>Demo</div>
}

Ref / RefObject / MutableRefObject

  • RefObject 是 react.createRef 返回值, .current 属性只读
  • Ref = RefObject | RefCallback
  • MutableRefObjectuseRef / forwardRef 返回值类型, 可写

关于 createRef 为什么是 readonly?
DefinitelyTyped/DefinitelyTyped#31065

现在(@types/react v18)的做法可以手动指定类型

const ref = react.createRef(null) as MutableRefObject<SomeType | null>

useRef 有几个重载

function useRef<T>(initialValue: T): MutableRefObject<T>;
function useRef<T>(initialValue: T|null): RefObject<T>;
function useRef<T = undefined>(): MutableRefObject<T | undefined>;

forwardRef

forwardRef<TRef, TProps>((props: TProps, ref: TRef) => {
  return <>hello</>
})

使用的时候注意, TProps 默认为 {}, 不要写在 props: 后, 需要写在 forwardRef<TRef, TProps> 里


image


image

@magicdawn
Copy link
Owner Author

magicdawn commented Mar 22, 2023

input 受控组件光标跳到最后

hooks 版

  const inputRef = useRef<{
    input: HTMLInputElement
    selectionStart: number | null
    selectionEnd: number | null
  } | null>(null)
  useLayoutEffect(() => {
    if (!inputRef.current) return
    const { input, selectionStart, selectionEnd } = inputRef.current
    input.selectionStart = selectionStart
    input.selectionEnd = selectionEnd
    inputRef.current = null
  }, [searchKeyword])

// ...


  <input value={searchKeyword} onChange={e => {
    const input = e.target
    inputRef.current = {input, selectionStart: input.selectionStart, selectionEnd: input.selectionEnd}
    // update searchKeyword state
  }} />

or 简洁点的

function createInputRecoverAction(input: HTMLInputElement) {
  const { selectionStart, selectionEnd } = input
  return () => {
    input.selectionStart = selectionStart
    input.selectionEnd = selectionEnd
  }
}

  const inputActionRef = useRef<(() => void) | null>(null)
  useLayoutEffect(() => {
    inputActionRef.current?.()
    inputActionRef.current = null
  }, [searchKeyword])

   <input value={searchKeyword} onChange={e => {
    const input = e.target
    inputActionRef.current = createInputRecoverAction(input)
    // update searchKeyword state
  }} />

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant