[React] Broadcaster + Operator + Listener pattern -- 21. useBroadcaster & useListener Example 2 (allowWhen)

You often want to ignore values until the user performs a certain action. This lesson walks through setting up an allowWhen broadcaster that will only pass the latest value through when another broadcaster fires. The scenario is using an Enter keypress to allow the latest value of the input text to be passed through.

 

Given an input field, when we press "Enter" we want to print the mesage into the {state}.

复制代码
import React from "react"
import { render } from "react-dom"

import {
  useBroadcaster,
  useListener,
  forOf,
  createTimeout
} from "./broadcasters"
import {  } from "./operators"


let App = () => {
  let onInput = useListener()
  let state = useBroadcaster())

  return (
    <div>
      <input type="text" onInput={onInput}  />
      <br/> {state}
</div>
  )
}

render(<App></App>, document.querySelector("#root"))
复制代码

 

1. Add "onKeyPress" event binding:

复制代码
let App = () => {
  let onInput = useListener()
  let onKeyPress = useListener()
  let state = useBroadcaster()

  return (
    <div>
      <input type="text" onInput={onInput} onKeyPress={onKeyPress} />
      <br/>
      {state}
    </div>
  )
}
复制代码

 

2. We only want to get event target value from input event:

复制代码
import {  targetValue } from "./operators"

let App = () => {
  let onInput = useListener()
  let onKeyPress = useListener()
  let inputValue = targetValue(onInput)
  let state = useBroadcaster()

  return (
    <div>
      <input type="text" onInput={onInput} onKeyPress={onKeyPress} />
      <br/>
      {state}
    </div>
  )
}
复制代码

 

3. Setup "Enter" broadcaster, when "Enter" key pressed:

复制代码
let App = () => {
  let onInput = useListener()
  let onKeyPress = useListener()
  let inputValue = targetValue(onInput)
  let enter = filter(event => event.key === "Enter")(onKeyPress)
  let state = useBroadcaster()

  return (
    <div>
      <input type="text" onInput={onInput} onKeyPress={onKeyPress} />
      <br/>
      {state}
    </div>
  )
}
复制代码

 

4. Because we only want {state} changes when "Enter" key pressed, so we need a operator "allowWhen":

复制代码
const allowWhen = allowBroadcaster => broadcaster => listener => {
  let current
  broadcaster((value) => {
    current = value;
  })
  allowBroadcaster(() => {
    listener(current)
  })
 }

let App = () => {
  let onInput = useListener()
  let onKeyPress = useListener()
  let inputValue = targetValue(onInput)
  let enter = filter(event => event.key === "Enter")(onKeyPress)
  let state = useBroadcaster()

  return (
    <div>
      <input type="text" onInput={onInput} onKeyPress={onKeyPress} />
      <br/>
      {state}
    </div>
  )
}
复制代码

 

5. Compose together:

复制代码
const allowWhen = allowBroadcaster => broadcaster => listener => {
  let current
  broadcaster((value) => {
    current = value;
  })
  allowBroadcaster(() => {
    listener(current)
  })
 }

let App = () => {
  let onInput = useListener()
  let onKeyPress = useListener()
  let inputValue = targetValue(onInput)
  let enter = filter(event => event.key === "Enter")(onKeyPress)
  let broadcaster = allowWhen(enter)(inputValue)
  let state = useBroadcaster(broadcaster)

  return (
    <div>
      <input type="text" onInput={onInput} onKeyPress={onKeyPress} />
      <br/>
      {state}
    </div>
  )
}
复制代码

 


 

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// broadcasters.js
 
import { curry } from "lodash"
import React, {useState, useEffect, useCallback} from "react"
 
export let done = Symbol("done")
 
export let createTimeout = curry((time, listener) => {
  let id = setTimeout(() => {
    listener(null)
    listener(done)
  }, time)
 
  return () => {
    clearTimeout(id)
  }
})
 
export let addListener = curry(
  (selector, eventType, listener) => {
    let element = document.querySelector(selector)
    element.addEventListener(eventType, listener)
 
    return () => {
      element.removeEventListener(eventType, listener)
    }
  }
)
 
export let createInterval = curry((time, listener) => {
  let i = 0
  let id = setInterval(() => {
    listener(i++)
  }, time)
  return () => {
    clearInterval(id)
  }
})
 
//broadcaster = function that accepts a listener
export let merge = curry(
  (broadcaster1, broadcaster2, listener) => {
    let cancel1 = broadcaster1(listener)
    let cancel2 = broadcaster2(listener)
 
    return () => {
      cancel1()
      cancel2()
    }
  }
)
 
export let zip = curry(
  (broadcaster1, broadcaster2, listener) => {
    let cancelBoth
 
    let buffer1 = []
    let cancel1 = broadcaster1(value => {
      buffer1.push(value)
      // console.log(buffer1)
      if (buffer2.length) {
        listener([buffer1.shift(), buffer2.shift()])
 
        if (buffer1[0] === done || buffer2[0] === done) {
          listener(done)
          cancelBoth()
        }
      }
    })
 
    let buffer2 = []
    let cancel2 = broadcaster2(value => {
      buffer2.push(value)
 
      if (buffer1.length) {
        listener([buffer1.shift(), buffer2.shift()])
        if (buffer1[0] === done || buffer2[0] === done) {
          listener(done)
          cancelBoth()
        }
      }
    })
 
    cancelBoth = () => {
      cancel1()
      cancel2()
    }
 
    return cancelBoth
  }
)
 
export let forOf = curry((iterable, listener) => {
  let id = setTimeout(() => {
    for (let i of iterable) {
      listener(i)
    }
    listener(done)
  }, 0)
 
  return () => {
    clearTimeout(id)
  }
})
 
export let useBroadcaster = (broadcaster, deps = []) => {
  let [state, setState] = useState(null)
  useEffect(() => {
    broadcaster((value) => {
      if (value === done) {
        return
      }
      setState(value)
    })
  }, deps)
  return state
}
 
export let useListener = (deps = []) => {
  let listener
  let callbackListener = value => {
    if (typeof value === "function") {
      listener = value
      return
    }
    listener(value)
  }
  return useCallback(callbackListener, deps)
}

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// index.js
 
import React from "react"
import { render } from "react-dom"
 
import {
  useBroadcaster,
  useListener,
  forOf,
  createTimeout
} from "./broadcasters"
import {  targetValue, filter, hardCode} from "./operators"
 
 
const allowWhen = allowBroadcaster => broadcaster => listener => {
  let current
  broadcaster((value) => {
    current = value;
  })
  allowBroadcaster(() => {
    listener(current)
  })
 }
 
let App = () => {
  let onInput = useListener()
  let onKeyPress = useListener()
  let inputValue = targetValue(onInput)
  let enter = filter(event => event.key === "Enter")(onKeyPress)
  let broadcaster = allowWhen(enter)(inputValue)
  let state = useBroadcaster(broadcaster)
 
  return (
    <div>
      <input type="text" onInput={onInput} onKeyPress={onKeyPress} />
      <br/>
      {state}
    </div>
  )
}
 
render(<App></App>, document.querySelector("#root"))

  

posted @   Zhentiw  阅读(129)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2015-11-24 [Javascript] Intro to Recursion
2014-11-24 [AnuglarJS] TweenMax with ngAnimate
点击右上角即可分享
微信分享提示