crimx / blog-comments

utterances🔮 comments on https://blog.crimx.com.
0 stars 0 forks source link

/2018/02/16/understanding-rxjs/ #23

Open utterances-bot opened 4 months ago

utterances-bot commented 4 months ago

理解 RxJS :四次元编程 | CRIMX BLOG

学习 RxJS 最大的问题是官方造了很多概念,但文档又解释得不太全面和易懂,需要结合阅读各种文章(特别是 Ben Lesh 的,包括视频)。本文试图整体梳理一遍再用另外的角度来介绍,希望能帮助初学者或者对 RxJS 的一些概念比较含糊的使用者。 为什么需要 RxJS RxJS

https://blog.crimx.com/2018/02/16/understanding-rxjs/

Zeno2019 commented 4 months ago

所以还有机会看到博主大佬整理的后续那篇文章吗?关于 Rxjs 这块,看了几篇文章,还是博主的印象较为深入浅出🥹

Zeno2019 commented 4 months ago

正在看博主关于 hooks 结合 Rxjs 这块的那篇,发现居然是 Observable Hooks 的作者,膜拜

crimx commented 4 months ago

所以还有机会看到博主大佬整理的后续那篇文章吗?关于 Rxjs 这块,看了几篇文章,还是博主的印象较为深入浅出🥹

抱歉草稿好像已经丢了哈哈,当时写到一半的时候 RxJS7 刚出来,operators 改了挺多的,概念倒是没什么变化。

你可以看看 rxmarbles 或者 rxtutor

Zeno2019 commented 4 months ago

哈哈感谢回复,第一个之前有搜到过,但感觉没有那么直观,第二个正在看,再次感谢

CRIMX @.***> 於 2024年6月28日週五 上午10:22寫道:

所以还有机会看到博主大佬整理的后续那篇文章吗?关于 Rxjs 这块,看了几篇文章,还是博主的印象较为深入浅出🥹

抱歉草稿好像已经丢了哈哈,当时写到一半的时候 RxJS7 刚出来,operators 改了挺多的,概念倒是没什么变化。

你可以看看 rxmarbles https://rxmarbles.com/ 或者 rxtutor https://rxtutor.org/

— Reply to this email directly, view it on GitHub https://github.com/crimx/blog-comments/issues/23#issuecomment-2195989639, or unsubscribe https://github.com/notifications/unsubscribe-auth/AG7BM5H6TUQTWGF7OFBHEFTZJTCH5AVCNFSM6AAAAABJ7ITCWKVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCOJVHE4DSNRTHE . You are receiving this because you commented.Message ID: @.***>

Zeno2019 commented 3 months ago

大佬打扰了,在写个人项目练习 observable-hooks 过程中,有个问题想请假下,假如对一个 textarea 组件进行封装,现在想实现检测该组件的多个状态(高度变化, 是否有输入内容等),那么我应该拆成多个 useObservableState ,还是在一个 useObservableState 里面处理所有状态的逻辑?

以下为当前代码:

function AskQuestionArea({ className, placeholder = 'Ask me anthing...', ...props }: { className?: string, placeholder?: string }) {
  const textareaRef = useRef<HTMLTextAreaElement>(null)
  const textareaWrappRef = useRef<HTMLDivElement>(null)

  const [height] = useObservableState(() => {
    return defer(() => {
      const textareaEl = textareaRef.current

      if (!textareaEl) {
        console.error('no textareaEl and return')
        return of('20px')
      }

      // 获取初始高度作为最小高度
      const minHeight = textareaEl.clientHeight

      const updateHeight$ = (): Observable<string> => {
        return new Observable<string>((observer) => {
          requestAnimationFrame(() => {
            const scrollPos = textareaEl.scrollTop
            const currHeight = textareaEl.style.height

            textareaEl.style.height = '20px'

            const newHeight = Math.max(textareaEl.scrollHeight, minHeight)
            textareaEl.style.height = currHeight // 使用保存的高度
            textareaEl.scrollTop = scrollPos

            requestAnimationFrame(() => {
              textareaEl.style.height = `${newHeight}px`
              observer.next(`${newHeight}px`)
              observer.complete()
            })
          })
        })
      }

      return merge(
        of(`${minHeight}px`),
        fromEvent<React.ChangeEvent<HTMLTextAreaElement>>(textareaEl, 'input').pipe(
          startWith(null), // 确保初始化也会计算高度
          switchMap(() => updateHeight$()),
        ),
      )
    })
  }, '20px') // 默认值

  const handleFocus = () => {
    if (textareaRef.current && textareaWrappRef.current) {
      textareaWrappRef.current?.setAttribute('data-focus', 'true')
    }
  }

  const handleBlur = () => {
    if (textareaRef.current && textareaWrappRef.current) {
      textareaWrappRef.current?.removeAttribute('data-focus')
    }
  }

  return (
    <div className="group flex w-full flex-col" ref={textareaWrappRef}>
      <div
        className={
          cn([
            'relative',
            'inline-flex',
            '!h-auto',
            'min-h-8',
            'cursor-text',
            'flex-row',
            'items-center',
            'rounded-xl',
            'border-2',
            'bg-[hsl(var(--background))]',
            'p-2',
            'gap-1',
            'shadow-sm',
            'transition-colors',
            '!duration-150',
            'hover:border-indigo-200',
            'group-data-[focus=true]:border-[hsl(var(--ring))]',
            'motion-reduce:transition-none',
          ])
        }
      >
        <Textarea
          ref={textareaRef}
          className={
            cn([
              'bg-[hsl(var(--background))]',
              'text-[hsl(var(--foreground))]',
              'pe-2',
              'focus-visible:ring-0',
              'border-0',
              'max-h-80',
            ], className)
          }
          style={{ height }}
          onFocus={handleFocus}
          onBlur={handleBlur}
          placeholder={placeholder}
          {...props}
        />
        <Button
          size="icon"
          className="size-8 self-end rounded-lg"
        >
          <Icon icon="majesticons:arrow-right-line" />
        </Button>
      </div>
    </div>
  )
}

export default AskQuestionArea
crimx commented 3 months ago

@Zeno2019 我一般是倾向写纯的 operator ,避免在其中做副作用。这样可以独立测试 Observerable 链。比如这里的 textarea 高度,我会算出一个 height$,再用 const height = useObservableState(height$) 走正常的 React 组件设 props 。

(P.S. 现在可以用新的 field-sizing: content

Zeno2019 commented 3 months ago

感谢大佬回复,我研究看看🙏