0

I think my question can best be asked by providing a minimum example. Let's say I have a Parent component that renders a Sum. This Parent component can have n children that renders sliders. The final application must allow the user to slide the the sliders from 0 to 10 and as they do so the total Sum in the parent component should also update in real time.

With this in mind let's layout the 2 components, starting with the Parent:

function Parent({ numChildren }) {
  const sum = useSharedValue(0)
  const sumText = useDerivedValue(() => `Sum: ${sum.value}`)
  return (
    <>
      <ReText text={sumText} />
      {Array.from({ length: numChildren }).map(() => (
        <Slider />
      ))}
    </>
  )
}

And the child (Slider) component:

const WINDOW_WIDTH = Dimensions.get("window").width

function Slider() {
  const translateX = useSharedValue(0)
  const startX = useSharedValue(0)
  const gesture = useMemo(
    () =>
      Gesture.Pan()
        .onUpdate((e) => {
          // for simplicity we will ignore bounding the slider to the windows width
          translateX.value = startX.value + e.translationX
        })
        .onEnd(() => {
          startX.value = translateX.value
        }),
    []
  )

  // This value should somehow be reflected in Parent's Sum
  const sliderValue = useDerivedValue(() => {
    return interpolate(
      translateX.value,
      [0, WINDOW_WIDTH],
      [0, 10]
    )
  })

  const handleStyle = useAnimatedStyle(() => {
    return {
      transform: [{ translateX: translateX.value }],
    }
  })

  return (
    <View> // We'll pretend this is the slider's container
      <GestureDetector gesture={gesture}>
        <Animated.View style={handleStyle} />
      </GestureDetector>
    </View>
  )
}

How would you approach getting the parent's Sum to show the total sum of all the sliders as the user interacts with the sliders? I've tried a number of things including:

  • passing the sum shared value to the <Slider /> component
  • creating a shared value with a map useSharedValue({}) that is passed to the children to update with a unique key
  • using state and state updates for sum instead of shared value

One of the biggest hurdle I run into is that I cannot create a dynamic number of useSharedValues because of React hooks top level rule. But running into this usually indicates there is an architectural fault with my current approach.

Thanks.

seanlenny
  • 73
  • 1
  • 6

1 Answers1

0

I have a pretty nasty solution to this. Would still appreciate other ideas on how to solve this. For now this is what I've come up with a solution that adds a SharedValue to the parent to store all the slider values. Also the parent needs to pass the sum shared value to the Slider component so that it can update it when the gesture is active.

function Parent({ numChildren }) {
  const sum = useSharedValue(0)
  const sliders = useSharedValue(new Array(numChildren).fill(0))
  const sumText = useDerivedValue(() => `Sum: ${sum.value}`)
  return (
    <>
      <ReText text={sumText} />
      {Array.from({ length: numChildren }).map(() => (
        <Slider sum={sum} sliders={sliders} />
      ))}
    </>
  )
}
const WINDOW_WIDTH = Dimensions.get("window").width

function Slider({ sum, sliders }) {
  const translateX = useSharedValue(0)
  const startX = useSharedValue(0)
  const gesture = useMemo(
    () =>
      Gesture.Pan()
        .onUpdate((e) => {
          // for simplicity we will ignore bounding the slider to the windows width
          translateX.value = startX.value + e.translationX

          const value = interpolate(
            translateX.value,
            [0, WINDOW_WIDTH],
            [0, 10]
          )
          sliders.value[index] = value
          sum.value = sliders.value.reduce((acc, curr) => acc + curr, 0)
        })
        .onEnd(() => {
          startX.value = translateX.value
        }),
    [translateX, startX, sum, index]
  )

  const handleStyle = useAnimatedStyle(() => {
    return {
      transform: [{ translateX: translateX.value }],
    }
  })

  return (
    <View> // We'll pretend this is the slider's container
      <GestureDetector gesture={gesture}>
        <Animated.View style={handleStyle} />
      </GestureDetector>
    </View>
  )
}

Ideally the <Slider /> component shouldn't know anything about the sum.

seanlenny
  • 73
  • 1
  • 6