Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/reactjs/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 渲染后对元素进行反应滚动_Javascript_Reactjs_Apollo_React Apollo - Fatal编程技术网

Javascript 渲染后对元素进行反应滚动

Javascript 渲染后对元素进行反应滚动,javascript,reactjs,apollo,react-apollo,Javascript,Reactjs,Apollo,React Apollo,我正在使用React和Apollo Graphql创建一个应用程序。我的应用程序的一部分包括向用户显示选项列表,以便用户可以选择一个选项。一旦他选择了其中一个选项,其他选项就会隐藏起来 这是我的密码: /** * Renders a list of simple products. */ export default function SimplesList(props: Props) { return ( <Box> {props.childProduc

我正在使用React和Apollo Graphql创建一个应用程序。我的应用程序的一部分包括向用户显示选项列表,以便用户可以选择一个选项。一旦他选择了其中一个选项,其他选项就会隐藏起来

这是我的密码:

/**
 * Renders a list of simple products.
 */
export default function SimplesList(props: Props) {
  return (
    <Box>
      {props.childProducts
        .filter(child => showProduct(props.parentProduct, child))
        .map(child => (
          <SingleSimple
            key={child.id}
            product={child}
            menuItemCacheId={props.menuItemCacheId}
            parentCacheId={props.parentProduct.id}
          />
        ))}
    </Box>
  );
}
更新2:

我根据Kornflex的建议再次更改了我的组件,但它仍然不起作用:

export default function SingleSimple(props: Props) {
  const classes = useStyles();
  const ref = useRef(null);

  const [needScroll, setNeedScroll] = useState(false);
  useEffect(() => {
    if (needScroll) {
      scrollToElement();
    }
  }, [ref]);

  const [flipQuantity] = useFlipChosenProductQuantityMutation({
    variables: {
      input: {
        productCacheId: props.product.id,
        parentCacheId: props.parentCacheId,
        menuItemCacheId: props.menuItemCacheId,
      },
    },
    onError: err => {
      if (process.env.NODE_ENV !== 'test') {
        console.error('Error executing Flip Chosen Product Quantity Mutation', err);
        Sentry.setExtras({ error: err, query: 'useFlipChosenProductQuantityMutation' });
        Sentry.captureException(err);
      }
    },
  });

  const [validateProduct] = useValidateProductMutation({
    variables: { productCacheId: props.menuItemCacheId },
    onError: err => {
      if (process.env.NODE_ENV !== 'test') {
        console.error('Error executing Validate Product Mutation', err);
        Sentry.setExtras({ error: err, query: 'useValidateProductMutation' });
        Sentry.captureException(err);
      }
    },
  });

  const scrollToElement = useCallback(() => {
    if (ref && ref.current) {
      ref.current.scrollIntoView({
        behavior: 'smooth',
        block: 'start',
      });
    }
  }, [ref]);

  const onClickHandler = useCallback(async () => {
    await flipQuantity();
    if (props.product.isValid !== ProductValidationStatus.Unknown) {
      validateProduct();
    }

    setNeedScroll(true);
  }, [flipQuantity, props.product.isValid, validateProduct, scrollToElement]);

  return (
    <ListItem className={classes.root}>
      <div ref={ref}>
        <Box display='flex' alignItems='center' onClick={onClickHandler}>
          <Radio
            edge='start'
            checked={props.product.chosenQuantity > 0}
            tabIndex={-1}
            inputProps={{ 'aria-labelledby': props.product.name! }}
            color='primary'
            size='medium'
          />
          <ListItemText
            className={classes.text}
            primary={props.product.name}
            primaryTypographyProps={{ variant: 'body2' }}
          />
          <ListItemText
            className={classes.price}
            primary={getProductPrice(props.product)}
            primaryTypographyProps={{ variant: 'body2', noWrap: true, align: 'right' }}
          />
        </Box>
        {props.product.chosenQuantity > 0 &&
          props.product.subproducts &&
          props.product.subproducts.map(subproduct => (
            <ListItem component='div' className={classes.multiLevelChoosable} key={subproduct!.id}>
              <Choosable
                product={subproduct!}
                parentCacheId={props.product.id}
                menuItemCacheId={props.menuItemCacheId}
                is2ndLevel={true}
              />
            </ListItem>
          ))}
      </div>
    </ListItem>
  );
}

我之前已经解决了这个问题,在出现时应该滚动到的项目中添加一个本地州标志:

apolloClient.mutate({
  mutation: MY_MUTATE,
  variables: { ... },
  update: (proxy, { data: { result } }) => {
    // We mark the item with the local prop `addedByThisSession` so that we know to
    // scroll to it once mounted in the DOM.
    apolloClient.cache.writeData({ id: `MyType:${result._id}`, data: { ... result, addedByThisSession: true } });
  }
})
然后,当它挂载时,我强制滚动并清除标志:

import scrollIntoView from 'scroll-into-view-if-needed';

...

const GET_ITEM = gql`
  query item($id: ID!) {
    item(_id: $id) {
      ...
      addedByThisSession @client
    }
  }
`;

...

const MyItem = (item) => {
  const apolloClient = useApolloClient();
  const itemEl = useRef(null);

  useEffect(() => {
    // Scroll this item into view if it's just been added in this session
    // (i.e. not on another browser or tab)
    if (item.addedByThisSession) {
      scrollIntoView(itemEl.current, {
        scrollMode: 'if-needed',
        behavior: 'smooth',
      });

      // Clear the addedByThisSession flag
      apolloClient.cache.writeFragment({
        id: apolloClient.cache.config.dataIdFromObject(item),
        fragment: gql`
          fragment addedByThisSession on MyType {
            addedByThisSession
          }
        `,
        data: {
          __typename: card.__typename,
          addedByThisSession: false,
        },
      });
    }
  });

  ...

这样做意味着我可以将突变与项目的呈现完全分离,并且我可以确保只有当项目存在于DOM中时才会出现滚动。

您可以使用useRef(作为处理元素ref的正确挂钩),然后更新您的代码示例吗?是的,我尝试使用
useRef
挂钩,但结果是一样的。在这种情况下,我不知道如何在选择组件后触发
scrollIntoView
。您可以使用
const[scrollNeeded,setScrollNeeded]=useState(false)
。用
setScrollRequired(true)
替换
scrollToElement()
。添加一个效果,当ref更改时,检查
scrollNeeded
是否为true,将
scrollNeeded
设置为false并调用
scrollToElement
我也尝试过,但没有成功。问题是,
SingleSimple
组件总是由
SimplesList
重新创建,因此当我的状态更改时,
SingleSimple
组件被重新创建,并且这个新组件的
scrollNeed
将始终是
false
index.js:1375 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
apolloClient.mutate({
  mutation: MY_MUTATE,
  variables: { ... },
  update: (proxy, { data: { result } }) => {
    // We mark the item with the local prop `addedByThisSession` so that we know to
    // scroll to it once mounted in the DOM.
    apolloClient.cache.writeData({ id: `MyType:${result._id}`, data: { ... result, addedByThisSession: true } });
  }
})
import scrollIntoView from 'scroll-into-view-if-needed';

...

const GET_ITEM = gql`
  query item($id: ID!) {
    item(_id: $id) {
      ...
      addedByThisSession @client
    }
  }
`;

...

const MyItem = (item) => {
  const apolloClient = useApolloClient();
  const itemEl = useRef(null);

  useEffect(() => {
    // Scroll this item into view if it's just been added in this session
    // (i.e. not on another browser or tab)
    if (item.addedByThisSession) {
      scrollIntoView(itemEl.current, {
        scrollMode: 'if-needed',
        behavior: 'smooth',
      });

      // Clear the addedByThisSession flag
      apolloClient.cache.writeFragment({
        id: apolloClient.cache.config.dataIdFromObject(item),
        fragment: gql`
          fragment addedByThisSession on MyType {
            addedByThisSession
          }
        `,
        data: {
          __typename: card.__typename,
          addedByThisSession: false,
        },
      });
    }
  });

  ...