kd12345
kd12345

Reputation: 793

React Native - State changed in child, how to update Parent?

currently i am changing the state in my child component and now i want to update my parent. Initially, i am passing data from the parent to the child then in the child i am changing the state. when i do that nothing happens in app because parent is still not updated, but when i reload the app, the changes that were made are updated.

I am also using react navigation to go from my parent screen to child screen.

Here is my code:

Parent Screen:

function PostsScreen({navigation}) {

const [posts, setPosts] = useState([]);
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);

const loadPosts = async () => {
setLoading(true);
const response = await postsApi.getPosts();
setLoading(false);

if (!response.ok) return setError(true);

setError(false);
setPosts(response.data);
};

useEffect(() => {
loadPosts();
}, []);

return(
<ActivityIndicator visible={loading} />
<FlatList
      data={posts}
      keyExtractor={(post) => post.id.toString()}
      renderItem={({ item }) => (
        <Card
          title={item.title}
          subTitle={item.subTitle}
          onPress={() => 
          navigation.navigate(routes.POST_DETAILS,item)}
        />
      )}
    />
  );
  }

Child Screen:

function PostDetailsScreen({ route }) {
const post = route.params;
const { user} = useAuth();

const [addedToLikes, setAddedToLikes] = useState(post.isLiked);
const[likesCount,setLikesCount]=useState(post.likesCount)

const addToLikes = (PostId,userId) => {
postsApi.likePost({PostId,userId});
setAddedToLikes(!addedToLikes);
};

let show_likes="";
if(addedToLikes){
show_likes=(likesCount >1)?(("Liked by you")+" and "+(likesCount - 1)+((likesCount ==2)?( " 
other"):(" others"))):("Liked by you");
}else if(likesCount >0){
show_likes=(likesCount ==1)?(likesCount+ " like"):(likesCount + " likes");
}

return(
<TouchableOpacity onPress={() => {addToLikes(post.id,user.id)}}>
              {addedToLikes?<MaterialCommunityIcons
              name="heart"
            />:<MaterialCommunityIcons
                name="heart-outline"
              />}
</TouchableOpacity>

<View><TextInput>{show_likes}</TextInput></View>

)}

How do i update if a post isLiked and likesCount in the parent component?

Also, i am not using Redux.

Update:

I tried doing the following but i still keep getting an error.

Parent Screen:

function PostsScreen({ navigation }) {
  const [posts, setPosts] = useState([]);
  const [error, setError] = useState(false);
  const [loading, setLoading] = useState(false);

  const loadPosts = async () => {
    setLoading(true);
    const response = await postsApi.getPosts();
    setLoading(false);

    if (!response.ok) return setError(true);

    setError(false);
    setPosts(response.data);
  };

  useEffect(() => {
    loadPosts();
  }, []);

  const [addedToLikes, setAddedToLikes] = useState(post.isLiked);

  const addToLikes = (PostId, userId) => {
    postsApi.likePost({ PostId, userId });
    setAddedToLikes(!addedToLikes);
  };

  const { user } = useAuth();

  return (
    <React.Fragment>
      <ActivityIndicator visible={loading} />
      <FlatList
        data={posts}
        keyExtractor={post => post.id.toString()}
        renderItem={({ item }) => (
          <Card
            title={item.title}
            subTitle={item.subTitle}
            onPress={() => navigation.navigate(routes.POST_DETAILS, item)}
          />
        )}
      />
      <PostDetailsScreen addToLikes={addToLikes(posts.id, user.id)} />
    </React.Fragment>
  );
}

Child Screen:

function PostDetailsScreen({ route, addedToLikes, addToLikes }) {
  const post = route.params;

  const [likesCount, setLikesCount] = useState(post.likesCount);

  let show_likes = "";
  if (addedToLikes) {
    show_likes =
      likesCount > 1
        ? "Liked by you" + " and " + (likesCount - 1) + (likesCount == 2 ? " other" : " others")
        : "Liked by you";
  } else if (likesCount > 0) {
    show_likes = likesCount == 1 ? likesCount + " like" : likesCount + " likes";
  }

  return (
    <React.Fragment>
      <TouchableOpacity
        onPress={() => {
          addToLikes;
        }}
      >
        {addedToLikes ? <MaterialCommunityIcons name="heart" /> : <MaterialCommunityIcons name="heart-outline" />}
      </TouchableOpacity>

      <View>
        <TextInput>{show_likes}</TextInput>
      </View>
    </React.Fragment>
  );
}

Upvotes: 0

Views: 990

Answers (1)

Leslie Alldridge
Leslie Alldridge

Reputation: 1585

Without using some form of shared state such as Redux the best way to achieve the outcome you're after is to decide on your component structure.

It sounds like you have a structure like this:

Parent (doesn't know about likes) -> Child (knows about likes)

But you're wanting something like this:

Parent (knows about likes) -> Child (interaction with likes)

Therefore I recommend having your state in the Parent component keep track of isLiked and likesCount. The parent will also pass down a method handler to the child component such as addToLikes(post.id,user.id).

Example code:

import React from 'react';

class Parent extends React.Component{
    constructor(props){
        super(props);
        this.state = {
            data: null
        }
    }

    handleCallback = (childData) =>{
        this.setState({data: childData})
    }

    render(){
        const {data} = this.state;
        return(
            <div>
                <Child parentCallback = {this.handleCallback}/>
                {data}
            </div>
        )
    }
}

class Child extends React.Component{
  
    onTrigger = (event) => {
        this.props.parentCallback("Data from child");
        event.preventDefault();
    }

    render(){
        return(
        <div>
            <form onSubmit = {this.onTrigger}>
                <input type = "submit" value = "Submit"/>
            </form>
        </div>
        )
    }
}

export default Parent;

Upvotes: 1

Related Questions