首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >当useState元素不是null时,将其作为null响应钩子记录

当useState元素不是null时,将其作为null响应钩子记录
EN

Stack Overflow用户
提问于 2019-09-01 10:47:57
回答 2查看 9.8K关注 0票数 4

我有个方法,

代码语言:javascript
复制
const handleUpvote = (post, index) => {
  let newPosts = JSON.parse(JSON.stringify(mappedPosts));

  console.log('mappedPosts', mappedPosts); // null
  console.log('newPosts', newPosts); // null

  if (post.userAction === "like") {
    newPosts.userAction = null;
  } else {
    newPosts.userAction = "like";
  }

  setMappedPosts(newPosts);

  upvote(user.id, post._id);
};

它附加到映射元素,

代码语言:javascript
复制
const mapped = userPosts.map((post, index) => ( 
    <ListItem 
      rightIcon = {
        onPress = {
          () => handleUpvote(post, index)
        }
   ......

我也有

代码语言:javascript
复制
  const [mappedPosts, setMappedPosts] = useState(null);

当组件挂载时,它从redux状态获取userPosts,将它们映射到ListItem并适当地显示它。问题是,每当输入handleUpvote()时,它都将mappedPosts视为null,因此在setMappedPosts(newPosts);处将整个列表设置为null。

我在这里做错了什么?当单击mappedPosts时,handleUpvote()确实不是空的,因为.那么,如果首先调用mappedPosts方法的是handleUpvote()元素,那么它怎么可能呢?

我试过

代码语言:javascript
复制
    setMappedPosts({
      ...mappedPosts,
      mappedPosts[index]: post
    });

但那甚至不能编译。不知道从这里往哪里走

编辑

整个构成部分:

代码语言:javascript
复制
const Profile = ({
  navigation,
  posts: { userPosts, loading },
  auth: { user, isAuthenticated },
  fetchMedia,
  checkAuth,
  upvote,
  downvote
}) => {
  const { navigate, replace, popToTop } = navigation;

  const [mappedPosts, setMappedPosts] = useState(null);

  useEffect(() => {
    if (userPosts) {
      userPosts.forEach((post, index) => {
        post.userAction = null;

        post.likes.forEach(like => {
          if (like._id.toString() === user.id) {
            post.userAction = "liked";
          }
        });

        post.dislikes.forEach(dislike => {
          if (dislike._id.toString() === user.id) {
            post.userAction = "disliked";
          }
        });
      });

      const mapped = userPosts.map((post, index) => (
        <ListItem
          Component={TouchableScale}
          friction={100}
          tension={100}
          activeScale={0.95}
          key={index}
          title={post.title}
          bottomDivider={true}
          rightIcon={
            <View>
              <View style={{ flexDirection: "row", justifyContent: "center" }}>
                <Icon
                  name="md-arrow-up"
                  type="ionicon"
                  color={post.userAction === "liked" ? "#a45151" : "#517fa4"}
                  onPress={() => handleUpvote(post, index)}
                />
                <View style={{ marginLeft: 10, marginRight: 10 }}>
                  <Text>{post.likes.length - post.dislikes.length}</Text>
                </View>
                <Icon
                  name="md-arrow-down"
                  type="ionicon"
                  color={post.userAction === "disliked" ? "#8751a4" : "#517fa4"}
                  onPress={() => handleDownvote(post, index)}
                />
              </View>
              <View style={{ flexDirection: "row" }}>
                <Text>{post.comments.length} comments</Text>
              </View>
            </View>
          }
          leftIcon={
            <View style={{ height: 50, width: 50 }}>
              <ImagePlaceholder
                src={post.image.location}
                placeholder={post.image.location}
                duration={1000}
                showActivityIndicator={true}
                activityIndicatorProps={{
                  size: "large",
                  color: index % 2 === 0 ? "blue" : "red"
                }}
              />
            </View>
          }
        ></ListItem>
      ));

      setMappedPosts(mapped);
    } else {
      checkAuth();
      fetchMedia();
    }
  }, [userPosts, mappedPosts]);

  const handleDownvote = (post, index) => {
    let newPosts = JSON.parse(JSON.stringify(mappedPosts));

    if (post.userAction === "dislike") {
      newPosts.userAction = null;
    } else {
      newPosts.userAction = "dislike";
    }

    setMappedPosts(newPosts);

    downvote(user.id, post._id);
  };

  const handleUpvote = post => {
    let newPosts = JSON.parse(JSON.stringify(mappedPosts));

    console.log("mappedPosts", mappedPosts); // null
    console.log("newPosts", newPosts); // null

    if (post.userAction === "like") {
      newPosts.userAction = null;
    } else {
      newPosts.userAction = "like";
    }

    setMappedPosts(newPosts);

    upvote(user.id, post._id);
  };

  return mappedPosts === null ? (
    <Spinner />
  ) : (
    <ScrollView
      refreshControl={
        <RefreshControl
          refreshing={false}
          onRefresh={() => {
            this.refreshing = true;
            fetchMedia();
            this.refreshing = false;
          }}
        />
      }
    >
      {mappedPosts}
    </ScrollView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center"
  }
});

Profile.propTypes = {
  auth: PropTypes.object.isRequired,
  posts: PropTypes.object.isRequired,
  fetchMedia: PropTypes.func.isRequired,
  checkAuth: PropTypes.func.isRequired,
  upvote: PropTypes.func.isRequired,
  downvote: PropTypes.func.isRequired
};

const mapStateToProps = state => ({
  auth: state.auth,
  posts: state.posts
});

export default connect(
  mapStateToProps,
  { fetchMedia, checkAuth, upvote, downvote }
)(Profile);
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-09-01 11:56:19

当前解决方案不能工作的原因是,您在useEffect钩子中呈现useEffect,看起来它只运行一次,最终会“缓存”初始状态,这就是您在处理程序中看到的结果。

您需要使用多个钩子才能正常工作:

代码语言:javascript
复制
const Profile = (props) => {
  // ...
  const [mappedPosts, setMappedPosts] = useState(null)
  const [renderedPosts, setRenderedPosts] = useState(null)

  useEffect(() => {
    if (props.userPosts) {
      const userPosts = props.userPosts.map(post => {
        post.userAction = null;
        
        // ...
      })      
      setMappedPosts(userPosts)
    } else {
      checkAuth()
      fetchMedia()
    }
  }, [props.userPosts])

  const handleDownvote = (post, index) => {
    // ...
    setMappedPosts(newPosts)
  }
  const handleUpvote = (post) => {
    // ...
    setMappedPosts(newPosts)
  }

  useEffect(() => {
    if (!mappedPosts) {
      return
    }
    const renderedPosts = mappedPosts.map((post, index) => {
      return (...)
    })
    setRenderedPosts(renderedPosts)
  }, [mappedPosts])

  return !renderedPosts ? null : (...)
}

下面是一个简化的示例,它完成了您想要做的事情:

另外,请注意,不做这个

代码语言:javascript
复制
const Profile = (props) => {
  const [mappedPosts, setMappedPosts] = useState(null)

  useEffect(() => {
    if (userPosts) {
      setMappedPosts() // DON'T DO THIS!
    } else {
      // ...
    }
  }, [userPosts, mappedPosts])
}

不要在钩子中更新其依赖数组中的状态。您将遇到一个无限循环,这将导致您的组件保持重新呈现,直到它崩溃。

票数 4
EN

Stack Overflow用户

发布于 2019-09-01 12:09:38

让我用一个简化的例子来解释这个问题:

代码语言:javascript
复制
const Example = props => {
    const { components_raw } = props;
    const [components, setComponents] = useState([]);
    const logComponents = () => console.log(components);

    useEffect(() => {
        // At this point logComponents is equivalent to
        // logComponents = () => console.log([])
        const components_new = components_raw.map(_ => (
            <div onClick={logComponents} />
        ));

        setComponents(components_new);
    }, [components_raw]);

    return components;
};

您可以看到调用setComponents的循环,组件是空的[]。一旦状态被赋值,它将与logComponents所拥有的值保持在一起,它在未来的周期中是否发生变化并不重要。

要解决这个问题,您可以从接收到的数据中修改必要的元素,而不是组件。然后在render中的返回中添加onClick。

代码语言:javascript
复制
const Example = props => {
    const { data_raw } = props;
    const [data, setData] = useState([]);
    const logData = () => console.log(data);

    useEffect(() => {
        const data_new = data_raw.map(data_el => ({
            ...data_el // Any transformation you need to do to the raw data.
        }));

        setData(data_new);
    }, [data_raw]);

    return data.map(data_el => <div {...data_el} onClick={logData} />);
};
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57745322

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档