首页 > 解决方案 > React Native - 尝试取消订阅侦听器时出错

问题描述

我有一个用作侦听器的 GraphQL 订阅:

 this.createPostListener = API.graphql(
  graphqlOperation(onCreatePost),
).subscribe({
  next: postData => {
    const newPost = postData.value.data.onCreatePost;
    const prevPosts = this.state.posts.filter(
      post => post.id !== newPost.id,
    );

    const updatedPosts = [newPost, ...prevPosts];

    this.setState({posts: updatedPosts});
  },
});

在我的componentWillUnmount方法中:

  componentWillUnmount() {
    this.createPostListener.unsubscribe();
  }

我的问题是,当我navigate进入页面时,我立即在模拟器中收到此错误:

类型错误:未定义不是对象(评估“this.createPostListener.unsubscribe”)

我不太明白这个错误,因为它发生在我导航到页面时,所以我还没有创建帖子。

编辑:这是整个帖子:

 class SoundwavesScreen extends React.Component {
    constructor(props) {
    super(props);
    this.toggleSortTrending = this.toggleSortTrending.bind(this);
    this.toggleSortNewest = this.toggleSortNewest.bind(this);
    this.toggleSortForYou = this.toggleSortForYou.bind(this);
    this.toggleDistance = this.toggleDistance.bind(this);

    this.popupBody = React.createRef();
    this.postInput = React.createRef();
    this.popupBodyBottom = React.createRef();
    this.popup = React.createRef();

    this.state = {
      confirmDiscardPopup: false,
      posts: [],
      isLoading: true,
      latitude: '',
      longitude: '',
      postContent: '',
      postTitle: 'test',
      writePostPopup: false,
      user: {
        posterID: '',
      },
      distance: this.props.screenProps.distance,
      postInputIsFocused: false,
      popupIsVisible: false,
    };
    }

    sortByTrending() {
    const {posts} = this.state;
    let newPostList = posts;
    newPostList = posts.sort((a, b) => a.totalUpvotes < b.totalUpvotes);
    this.setState({
      posts: newPostList,
    });
  }

  sortByNewest() {
    const {posts} = this.state;
    let newPostList = posts;
    newPostList = posts.sort(
      (a, b) => new Date(a.createdDate) < new Date(b.createdDate),
    );
    this.setState({
      posts: newPostList,
    });
  }

  sortByForYou() {
    const {posts} = this.state;
    let newPostList = posts;
    newPostList = posts.sort((a, b) => a.totalComments < b.totalComments);
    this.setState({
      posts: newPostList,
    });
  }

  toggleSortTrending() {
    this.sortByTrending();
  }
  toggleSortNewest() {
    this.sortByNewest();
  }
  toggleSortForYou() {
    this.sortByForYou();
  }
  toggleDistance(item) {
    console.log('dis it? ');
    console.log(item);
    this.setState({distance: item});
  }

  componentDidMount = async() => {
    this.props.navigation.setParams({toggleTrending: this.toggleSortTrending});
    this.props.navigation.setParams({toggleNewest: this.toggleSortNewest});
    this.props.navigation.setParams({toggleForYou: this.toggleSortForYou});
    this.props.navigation.setParams({toggleDistance: this.toggleDistance});

    Geolocation.getCurrentPosition(
      position => {
        var location = JSON.stringify(position);
        this.setState({
          latitude: position.coords.latitude,
          longitude: position.coords.longitude,
        });
      },
      error => console.log('currentLocation ERROR: ' + error),
      {enableHighAccuracy: true, timeout: 20000, maximumAge: 1000},
    );
    
     await this.getPosts(); 
    
    this.createSoundwaveListener = API.graphql(
      graphqlOperation(onCreateSoundwave),
    ).subscribe({
      next: postData => {
        const newPost = postData.value.data.onCreateSoundwave;
        const prevPosts = this.state.posts.filter(
          post => post.id !== newPost.id,
        );

        const updatedPosts = [newPost, ...prevPosts];

        this.setState({posts: updatedPosts});
      },
    });

    this.deleteSoundwaveListener = API.graphql(
      graphqlOperation(onDeleteSoundwave),
    ).subscribe({
      next: postData => {
        const deletedPost = postData.value.data.onDeleteSoundwave;
        const updatedPosts = this.state.posts.filter(
          post => post.id !== deletedPost.id,
        );

        this.setState({posts: updatedPosts});
      },
    });
  }

  componentWillUnmount() {
    this.createSoundwaveListener.unsubscribe();
    this.deleteSoundwaveListener.unsubscribe();
  }

  getPosts = async () => {
    await API.graphql(graphqlOperation(listSoundwaves))
    .then( result => {
      this.setState({ posts: result.data.listSoundwaves.items,
        isLoading: false, isMounted: false})
    })
  }

  toggleWritePopup() {
    this.postInput.current.focus();
    this.popup.current.snapTo(1);
    this.setState({
      writePostPopup: true,
      confirmDiscardPopup: false,
    });
  }
  toggleConfirmDiscardPopup() {
    this.setState({
      writePostPopup: true,
      confirmDiscardPopup: true,
    });
    // this.postInput.current.blur();
  }
  toggleCancelDiscard() {
    this.setState({
      writePostPopup: true,
      confirmDiscardPopup: false,
    });
    this.postInput.current.focus();
  }
  toggleDiscard() {
    // this._panel.hide();
    this.popup.current.snapTo(0);
    this.postInput.current.blur();
    this.setState({
      writePostPopup: false,
      confirmDiscardPopup: false,
      postContent: '',
    });
    this.postInput.current.clear();
  }

  handlePostInputFocus = () => this.setState({postInputIsFocused: true});
  handlePostInputBlur = () => this.setState({postInputIsFocused: false});

  showPopup = () => this.setState({popupIsVisible: true});
  hidePopup = () => this.setState({popupIsVisible: false});

  renderDeletePost = () => {
    if (this.state.confirmDiscardPopup === true) {
      return (
        <View style={styles.popupBackground}>
          <View style={styles.popupDelete}>
            <Text style={styles.deleteTitle}>
              Would you like to discard this post?
            </Text>
            <View style={styles.deleteButtonsContainer}>
              <TouchableOpacity
                style={styles.popupCancel}
                onPress={() => this.toggleCancelDiscard()}>
                <Text style={styles.popupCloseText}>Cancel</Text>
              </TouchableOpacity>
              <TouchableOpacity
                style={styles.popupDiscard}
                onPress={() => this.toggleDiscard()}>
                <Text style={styles.popupPostText}>Discard</Text>
              </TouchableOpacity>
            </View>
          </View>
        </View>
      );
    }
  };

  renderBottomSheet = () => (
    <View style={styles.popup}>
      {/* {this.renderDeletePost()} */}
      <View style={styles.popupButtonsContainer}>
        <TouchableOpacity
          style={styles.popupClose}
          onPress={() => {
            if (this.state.postContent.length > 0) {
              this.toggleConfirmDiscardPopup();
            } else {
              this.toggleDiscard();
            }
          }}>
          <Image source={require('../assets/cancel.png')} />
          {/* <Text style={styles.popupCloseText}>Close</Text> */}
        </TouchableOpacity>
        <TouchableOpacity
          style={styles.popupPost}
          onPress={() => {
            let missingField = '';
            if (this.state.postTitle.length === 0) {
              missingField += 'title';
            }
            if (this.state.postContent.length === 0) {
              if (missingField.length > 0) {
                missingField += ' and ';
              }
              missingField += 'content';
            }
            if (missingField.length > 0) {
              Alert.alert(
                'Post Error',
                'Failed to create post!\nEmpty ' + missingField,
                [
                  {
                    text: 'OK',
                  },
                ],
              );
            } else {
              const post = {
                id: generateUniqueID(),
                approved: true, //false,
                content: this.state.postContent,
                createdDate: new Date().toISOString(), // get current time
                hidden: false, //true, //inverse of approved
                latitude: this.state.latitude.toString(),
                longitude: this.state.longitude.toString(),
                posterID: this.state.user.posterID,
                reported: false,
                title: this.state.postTitle,
                totalUpvotes: 0,
                totalComments: 0,
                //user: this.state.user, //TODO: update with user data
              };
              console.log(post);
              createPost(post)
                .then(data => {
                  this.toggleDiscard();
                  console.log('Created post in DB!');
                  console.log(data);
                  /*
                                    EXAMPLE JSON ON CREATE
                                    {"data": {"createSoundwave": {"_deleted": null, "_lastChangedAt": 1588561690399, "_version": 1, "approved": true, "comments": [Object], "content": "aaaa", "createdDate": "1588561689985", "hidden": false, "id": "36f9-db00-280c-9b8c-8f0b-5140-b572-19c1", "posterID": "c98b-57f2-3f3b-a19c-8635-c964-e2fc-99ff", "prompt": null, "promptID": null, "reported": false, "title": "aaaa", "totalComments": 0, "totalUpvotes": 0, "user": null}}}
                                  */
                  let postID = data.data.createSoundwave.id;
                })
                .catch(err => {
                  console.log('Created post in DB failed!');
                  console.log(err);
                  Alert.alert(
                    'Post Failure',
                    'Failed to create post!\nTry again!',
                    [
                      {
                        text: 'OK',
                      },
                    ],
                  );
                });
            }
          }}>
          <Image source={require('../assets/sendButton.png')} />
        </TouchableOpacity>
      </View>
      <View style={styles.popupBody} ref={this.popupBody}>
        <Image
          style={styles.popupProfilePic}
          source={require('../assets/default_profile_photo.png')}
        />
        <TextInput
          placeholder="What's on your mind?"
          style={styles.popupContent}
          multiline={true}
          maxLength={500}
          onChangeText={text => this.setState({postContent: text})}
          // autoFocus={true}
          keyboardAppearance={'default'}
          ref={this.postInput}
          inputAccessoryViewID={'charCount'}
          onFocus={this.handlePostInputFocus}
          onBlur={this.handlePostInputBlur}
        />
      </View>
      <View style={styles.popupBodyBottomPadding} ref={this.popupBodyBottom} />
    </View>
  );

  render() {
    if (this.state.isLoading) {
      return (
        <View>
          <ActivityIndicator />
        </View>
      );
    } else {
      // console.log('posts: \n');
      // console.log(this.state.posts);
      let postCards = [];
      this.state.posts.forEach(element => {
        console.log('on soundwave page, verison: ' + element._version);
        postCards.push(
          <View key={element.id}>
            <Post
              hidden={element.hidden}
              soundwaveContent={element.content}
              soundwaveDate={element.createdDate}
              soundwaveID={element.id}
              soundwaveLatitude={element.latitude}
              soundwaveLongitude={element.longitude}
              soundwavePosterID={element.posterID}
              soundwaveTitle={element.title}
              soundwaveUpvotes={element.totalUpvotes}
              soundwaveComments={element.totalComments}
              userPosterID={this.state.user.posterID}
              _version={element._version}
            />
          </View>,
        );
      });

      return (
        <View style={styles.container}>
          <LinearGradient
            colors={['#0E98DC', '#C55DBB', '#A273D8', '#A7ACD2']}
            start={{x: 0, y: 0}}
            end={{x: 0, y: 1}}
            locations={[0.0417, 0.3385, 0.6667, 1]}
            style={styles.linearGradient}>
            <View style={styles.titleWrapper}>
              <View style={styles.buttonRow}>
                <Image
                  style={styles.title}
                  source={require('../assets/default_profile_photo.png')}
                />
                <SoundwavesDrawerButton />
              </View>
              <Text style={styles.range}>{this.state.distance} miles away</Text>
              {/* <Image
                style={styles.bar}
                source={require('../assets/titleLine.png')}
              /> */}
            </View>

            {postCards.length === 0 && <Text>No posts found nearby.</Text>}
            {/* {this.renderWritePost()} */}
            {this.renderDeletePost()}
            <ScrollView>{postCards}</ScrollView>
            <TouchableOpacity
              style={styles.write}
              onPress={() => this.toggleWritePopup()}>
              <Image source={require('../assets/Post.png')} />
            </TouchableOpacity>
            <InputAccessoryView nativeID={'charCount'}>
              <LinearGradient
                colors={['#B8A5FE', '#B5B4FF']}
                start={{x: 0, y: 0}}
                end={{x: 0, y: 1}}
                locations={[0.5989, 0.9242]}
                style={styles.charCountBar}>
                <Text style={styles.charCountBarText}>
                  {this.state.postContent.length}/500
                </Text>
              </LinearGradient>
            </InputAccessoryView>
            <BottomSheet
              ref={this.popup}
              snapPoints={[0, '110%']}
              renderContent={this.renderBottomSheet}
              enabledGestureInteraction={false}
            />
              </LinearGradient>
            </View>
          );
        }
      }
    }

这是我导航到此页面的方式: 我有一个功能:

handleOpenURL = event => {
    this.navigate(event.url);
  };

navigate = url => {
 if (routeName === 'Login') {
        let currentSession = this.checkIfAuthenticated;
        if (currentSession) {
          console.log(
            'Authenticated and using Login path, heading to Soundwaves...',
          );
          this.props.navigation.navigate('Soundwaves', {
            id,
            name: 'testlogin',
            distance: 100,
          });
        } else {
          console.log('Error! Not authenticated but using Login path');
        }
      }
}

标签: reactjsreact-nativegraphql

解决方案


推荐阅读