与 useEffect 异步并将数据加载到变量中

Async with useEffect and loading data into variables

我正在尝试使用 mainData 这是一个对象,由 Supabase 通过连接填充。使用 segment:SignalJourneyAudiences(segment) ,

哪个会给我:

segment: {segment: xyz}

我有这个新数组,它正在使用从连接中 return 编辑的对象,如下所示。

const unquieConstraints = [
    ...new Map(
      mainData
        .filter(row => row.segment.segment === segment.segment.segment)
        .map(item => [item.constraint_value, item]),
    ).values(),
  ]

但是由于 Supabase 运行异步函数,而我 运行 它在 useEffect 我知道它无法读取。您可以在下面看到它加载为空,然后在 useEffect 调用异步函数后加载 mainData

我该如何解决这个问题,使应用程序不会因为无法读取而崩溃 segment: {segment: xxx} 我已经尝试在过滤器中使用三元运算符。但这不起作用:(

这是我的组件的主要逻辑

  // CONSTRAINT CHECKBOXES
  const [constraintOptions, setConstraintOptions] = useState({
    constraints: [],
  })

  const duplicates = new Set()

  const duplicateObjects = mainData.filter(
    item => duplicates.size === duplicates.add(item.segment.segment).size,
  )

  const duplicateIds = new Set()

  const nextRoundFilteredObjects = duplicateObjects.filter(
    item =>
      duplicateIds.size !== duplicateIds.add(item.audience_id.audience_id).size,
  )

  // We return only the constraint values for the unique segment

  const unquieConstraints = [
    ...new Map(
      mainData
        .filter(row => row.segment === segment.segment)
        .map(item => [item.constraint_value, item]),
    ).values(),
  ]

  if (mainData.length === 0) {
    console.warn('WARNING: mainData is empty')
  } else {
    console.log('mainData', mainData)
  }

  function handleCheckBoxes(e) {
    const {value, checked} = e.target

    if (checked) {
      setConstraintOptions({
        constraints: [...constraintOptions.constraints, value],
      })
    } else {
      setConstraintOptions({
        constraints: constraintOptions.constraints.filter(e => e !== value),
      })
    }
  }

  function openSideBar() {
    setIsOpen(true)
  }

  function closeSideBar() {
    setIsOpen(false)
  }

  // Handles the error / success messages popup
  function popupValidation(type, message) {
    if (type === 'success') {
      setSuccess(true)
      setSuccessMessage(message)

      setTimeout(() => {
        window.location.reload()
      }, 2000)
    } else if (type === 'warning') {
      setWarning(true)
      setWarningMessage(message)

      setTimeout(() => {
        setError(false)
        window.location.reload()
      }, 2500)
    } else if (type === 'error') {
      setError(true)
      setErrorMessage(message)

      setTimeout(() => {
        setError(false)
        window.location.reload()
      }, 2500)
    }
  }

  async function getAudienceConstratints() {
    const {data, error} = await supabase
      .from('SignalJourneyAudienceConstraints')
      .select(
        `
        constraint_id,
        audience_id:SignalJourneyAudiences(audience_id),
        segment:SignalJourneyAudiences(segment) ,
        source:SignalJourneySources(source) ,
        constraint_type:SignalJourneyConstraintType(constraint_type),
        constraint_value,
        targeting,
        frequency,
        period
      `,
      )
      .order('constraint_id', {ascending: true})

    if (data) {
      setMainData(data)
    }
    if (error) {
      setErrorMessage('error', error.message)
    }
  }

  async function addNewRelationship() {
    closeSideBar()

    try {
      const {data, error} = await supabase
        .from('SignalJourneyAudienceConstraintRelations')
        .insert([
          {
            audience_id: segment.audience_id.audience_id,
            relation: relationship.condition,
            constraint_ids: constraintOptions.constraints.join('|'),
          },
        ])

      if (data) {
        popupValidation('success', 'Relationship added successfully')
      }
      if (error) {
        popupValidation('error', error.message)
      }
    } catch (error) {
      console.error(error)
    }

    // RESET Relationships
    setSegment('')
  }

  useEffect(() => {
    getAudienceConstratints()
  }, [])

更新

这现在可以在下面的帮助下工作了

const getAudienceConstratints = async () => {
    const {data, error} = await supabase
      .from('SignalJourneyAudienceConstraints')
      .select(
        `
        constraint_id,
        audience_id:SignalJourneyAudiences(audience_id),
        segment:SignalJourneyAudiences(segment) ,
        source:SignalJourneySources(source) ,
        constraint_type:SignalJourneyConstraintType(constraint_type),
        constraint_value,
        targeting,
        frequency,
        period
      `,
      )
      .order('constraint_id', {ascending: true})

    if (error) {
      setErrorMessage('error', error.message)
    }

    setMainData(data)
    setIsLoading(false)
  }

但是我对此有疑问...

  const unquieConstraints = [
    ...new Map(
      mainData
        .filter(row => row.segment.segment === segment.segment.segment)
        .map(item => [item.constraint_value, item]),
    ).values(),
  ]

As row.segment.segment as as is loaded before the return of the data.

您的代码非常有道理。您的组件无法知道需要在挂载上获取的内容的值。

处理这种情况的经典方法是使用如下状态:

const MyComponent = () => {
    const [isLoading, setIsLoading] = React.useState(true);
    const [error, setError] = React.useState();
    const [data, setData] = React.useState([]);

    React.useEffect(() => {
        getAudience().then((fetchedData) => { 
            setData(fetchedData);
            setIsLoading(false);
        }).catch((err) => {
            setError(err.toString());
            setIsLoading(false);
        });
    }, [])

    if (!!error) return <span>Error: {error}</span>;

    if (isLoading) return <span>Loading...</span>;

    // Here you do stuff on your data since you know it is ready

    return <span>{JSON.stringify(data, null, 2)}</span>;
}

您可以考虑创建一个可重复使用的挂钩以减少代码样板。社区为 Supabase 创建了现有的挂钩。

示例:https://jsfiddle.net/5nhteu9v/