taro3.x: 父组件调用子组件方法,相互更新优化(案例集)

使用方法:

useRef && useImperativeHandle && forwardRef
lodash -> isEqual

父组件:

import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { View, Input, ScrollView } from '@tarojs/components'
import classnames from 'classnames'

import app from '@services/request'
import api from '@services/api'
import NavBar from '@components/navbar'
import useNavData from '@hooks/useNavData'
import Photos from '@components/photos'
import Videos from '@components/videos'
import Articles from '@components/articles'
import './index.scss'
import { INewsParam } from '@constants/common'

const Index = () => {
  const { appHeaderHeight, contentHeight } = useNavData()
  const [searchTitle, setSearchTitle] = useState<string>('')
  const [scroll, setScroll] = useState<any>({})
  const [navData, setNavData] = useState<any[]>([])
  const [currentNav, setCurrentNav] = useState<any>({})
  const ref = useRef<any>({})

  useEffect(() => {
    app.request({
      url: app.apiUrl(api.getNewsCate),
      data: {
        status: 1,
        module_id: 1
      }
    }, { loading: false }).then((result: any) => {
      setNavData(result)
      setCurrentNav(result[0])
    })
  }, [])

  const handleNavClick = (item: any) => {
    setCurrentNav(item)
    setSearchTitle('')
    ref.current.onParamChange && ref.current.onParamChange(item.type)
  }

  const handleScroll = (e: any) => {
    const top = e.detail.scrollTop
    if (top > 200) {
      setScroll({
        ...scroll,
        fixed: true,
        style: { top: appHeaderHeight }
      })
    }
    if (top <= 200 && scroll.fixed) {
      setScroll({
        ...scroll,
        fixed: false,
        style: {}
      })
    }
  }

  const handleScrollToLower = useCallback(() => {
    ref.current.onScorllToLower && ref.current.onScorllToLower()
  }, [])

  const handleInputBlur = (e: any) => {
    setSearchTitle(e.detail.value)
    ref.current.onParamChange && ref.current.onParamChange(currentNav.type, e.detail.value)
  }

  const toTop = () => {
    setScroll({
      top: Math.random(),
      fixed: false,
      style: {}
    })
  }

  const INIT_NEWS_PARAM: INewsParam = {
    type: currentNav.type,
    title: '',
    currentPage: 1,
  }

  const module_config = useMemo(() => {
    return {
      'image': <Photos {...INIT_NEWS_PARAM} ref={ref} />,
      'video': <Videos {...INIT_NEWS_PARAM} ref={ref} />,
      'article': <Articles {...INIT_NEWS_PARAM} ref={ref} />
    }
  }, [currentNav.type])

  return (
    <View className="index">
      <NavBar />
      <ScrollView
        scrollY
        style={{ maxHeight: contentHeight }}
        scrollWithAnimation
        scrollTop={scroll.top}
        onScroll={handleScroll}
        lowerThreshold={30}
        onScrollToLower={handleScrollToLower}
      >
        <View className="header">
          <View className="logo"></View>
          <View className="title">案例集</View>
        </View>
        <View className="search">
          <View className="search-content">
            <View className="iconfont icon-search"></View>
            <Input className="search-input" placeholder="搜索" onBlur={handleInputBlur} value={searchTitle}></Input>
          </View>
        </View>
        <View className={classnames('indexnav', scroll.fixed && 'fixed')} style={scroll.style}>
          <ScrollView scrollX>
            {
              navData.map((item: any, index: number) => (
                <View
                  key={index}
                  onClick={() => handleNavClick(item)}
                  className={classnames('indexnav-item', currentNav.type === item.type && 'actived')}>
                  <View className="name">{item.title}</View>
                </View>
              ))
            }
          </ScrollView>
        </View>
        <View className="content">
          {module_config[currentNav.module]}
        </View>
      </ScrollView>
      <View className="action">
        {
          scroll.fixed &&
          <View className="action-item" onClick={toTop}>
            <View className="item-icon">
              <View>TOP</View>
            </View>
          </View>
        }
        {/* <View className="action-item">
          <View className="item-icon">
            <View className="iconfont icon-telephone-out"></View>
          </View>
          <View className="item-text">
            <Text>联系我们</Text>
          </View>
        </View> */}
      </View>
    </View>
  )
}

export default Index

子组件:

import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react'
import Taro from '@tarojs/taro'
import { View, Image, Text } from '@tarojs/components'
import isEqual from 'lodash/isEqual'

import api from '@services/api'
import app from '@services/request'
import { getTotalPage, INIT_PAGE, IPage } from '@utils/page'
import { INewsParam } from '@constants/common'
import './index.scss'


const Photos = (props: INewsParam, ref: any) => {
    const PAGE_LIMIT = 10
    const [page, setPage] = useState<IPage>(INIT_PAGE)
    const [param, setParam] = useState<INewsParam>(props)
    const [loading, setLoading] = useState<boolean>(false)
    const [showEmpty, setShowEmpty] = useState<boolean>(false)
    const [photos, setPhotos] = useState<any[]>([])
    const paramRef = useRef<any>({})

    useEffect(() => {
        if (isEqual(paramRef.current, param)) {
            return
        }
        paramRef.current = param
        app.request({
            url: app.apiUrl(api.newsList),
            data: {
                page: param.currentPage,
                limit: PAGE_LIMIT,
                type: param.type,
                title: param.title
            }
        }, { loading: false }).then((result: any) => {
            setLoading(false)
            const totalPage = getTotalPage(PAGE_LIMIT, result.pagination.totalCount)
            setShowEmpty(totalPage <= props.currentPage)
            setPage({
                totalCount: result.pagination.totalCount,
                totalPage
            })

            if (param.currentPage === 1) {
                setPhotos(result.data)
            } else {
                setPhotos([...photos, ...result.data])
            }
        })
    }, [param])

    useImperativeHandle(ref, () => ({
        onScorllToLower: handleScrollToLower,
        onParamChange: handleParamChange
    }), [page.totalPage, param.currentPage])

    const handleParamChange = (type: string, title: string = '') => {
        setParam({
            type,
            title,
            currentPage: props.currentPage
        })
    }

    const handleScrollToLower = useCallback(() => {
        if (page.totalPage > param.currentPage) {
            setLoading(true)
            setParam({
                ...param,
                currentPage: param.currentPage + 1
            })
        } else {
            setShowEmpty(true)
        }
    }, [page.totalPage, param.currentPage])

    const toPhotoList = useCallback((item: any) => {
        Taro.navigateTo({
            url: `/pages/photo/index?id=${item.id}&title=${item.title}&subtitle=${item.sub_title}`
        })
    }, [])

    return (
        <View className="photos">
            {
                photos.map((item: any, index: number) => (
                    <View key={index} className="item" onClick={() => toPhotoList(item)}>
                        <View className="item-photo">
                            <Image src={item.image_path}></Image>
                        </View>
                        <View className="item-text">
                            <View className="title">{item.title}</View>
                            <View className="sub-title">{item.sub_title}</View>
                        </View>
                        <View className="item-mask"></View>
                    </View>
                ))
            }
            {
                loading &&
                <View className="empty-container">
                    <Text>正在加载中...</Text>
                </View>
            }
            {
                showEmpty &&
                <View className="empty-container">
                    <Text>没有更多数据了</Text>
                </View>
            }
        </View>
    )
}

export default React.memo(forwardRef(Photos))

 

posted @ 2020-10-24 11:18  Nyan  阅读(1715)  评论(0编辑  收藏  举报