简体   繁体   中英

The value of the object always shows “undefined” in React

I have the issue where I cannot access the value of the object possibly because DOM is rendered before the program fetches the data and put it into the object.

I am trying to implement the page where you see all reviews of all user. In the page you can also see which review you already liked or not. In order to achieve it, I want to access all the likes that a user has.

This is the initial state of the object.

const initialState = {
    likes: {},
};

And this object will be populated with the data fetched from the api server like this;

likes = {
    1: {id: 38, user_id: 3, review_id: 1, created_at: "2020-04-11T04:12:14.569Z", updated_at: "2020-04-11T04:12:14.569Z"}
    2: {id: 24, user_id: 3, review_id: 2, created_at: "2020-04-11T03:25:16.589Z", updated_at: "2020-04-11T03:25:16.589Z"}
    3: {id: 20, user_id: 3, review_id: 3, created_at: "2020-04-09T12:07:34.669Z", updated_at: "2020-04-09T12:07:34.669Z"}
}

Below is the component that represents some information of a review such as a title, user name, the number of like counts, etc.. These information are inherited from the parent component with the prop "review".

<ReviewCard.js> 
const ReviewCard = props => {
    let { id, user_id, title, image, rate, good, likes_count, gameId, createdAt } = props.review;
    const userId = useSelector(state => state.authReducer.userId);
    .
    .
    .
    let favorite = <LikeButton 
                        likesCount={likes_count}
                        userId={userId} 
                        reviewId={id}
                    />

return (
        <Card className={classes.root}>
            <CardHeader
                .
                .
                .
                action={favorite}
                title={title}
                subheader={createdAt}
            />
    .
    .
    . 
);

LikeButton component is literally the like button. I want to change the color of the button based on whether the user already liked the review or not. However, if (likes[reviewId]) is always ignored since likes[reviewId] is undefined even after likes object is populated with the actual data fetched from the api.

<LikeButton.js>
const LikeButton = props => {
    let { likesCount, userId, reviewId } = props;
    const dispatch = useDispatch();
    const likes = useSelector(state => state.likeReducer.likes);

    const [count, setCount] = useState(likesCount);

    useEffect(() => {
        dispatch(actions.fetchLike(likes, userId, reviewId));
    }, [props, likes, count]);

    .
    .
    .

    console.log('likes is ', likes);
    console.log('reviewId is', reviewId);
    console.log(`like with reviewId = ${reviewId} is `, likes[reviewId]);

    let favorite = (
        <div className={classes.LikeButton}>
            <FavoriteBorderIcon 
                onClick={onLikeHandler}
                className={classes.Icon}
            />
            {count}
        </div>
    );

    if (likes[reviewId]) {
        favorite = (
            <div className={classes.LikeButton}>
                <FavoriteIcon 
                    onClick={onUnlikeHandler}
                    className={classes.FavoriteIcon}
                />
                {count}
            </div>
        );
    }

    return (
        <div>
            {favorite}
        </div>
    );
};

Below is the like-related action for Redux.

<./actions/like.js>
export const setLike = likes => {
    return {
        type: actionTypes.SET_LIKE,
        likes: likes,
    };
};

export const fetchLike = (likes, userId, reviewId) => {
    return dispatch => {
        const url = `http://localhost:3001/users/${userId}/reviews/${reviewId}/likes/1`;
        axios
            .get(url)
            .then(response => {
                likes[reviewId] = response.data; 
                dispatch(setLike(likes));
            })
            .catch(error => {
                console.log(error);
            });
    }
};

Below is the like-related reducer for Redux.

<./reducer/like.js>
const initialState = {
    likes: {},
};

const setLike = (state, action) => {
    return updateObject(state, {
        likes: action.likes,
    });
};

const reducer = (state = initialState, action) => {
    switch (action.type) {
        case actionTypes.SET_LIKE:
            return setLike(state, action);
        default:
            return state;
    }
};

Once fetchLike fetches likes a user has, it will be stored in likes: {} . Actually, console.log('likes is ', likes); in LikeButton.js shows the content of likes once fetchLike() is completed. However, console.log( like with reviewId = ${reviewId} is , likes[reviewId]); still shows undefined . Image: Results of console.log() ;

I am guessing there might be some problem with the timing when it fetches the data and the page is rendered. (I know useEffect is triggered every time the variables in the second parameter change though...)

I would appreciate it if you could give some advise on this.

Thanks for reading:)

The reason behind that is your axios call is responding asynchronously so your likes state will be updated a bit later. Probably you can capture changes with useEffect() hook.

Try as the following:

useEffect(() => {
   console.log('likes is ', likes);
}, [likes]);

By doing this your likes will be logged into the console once it has a change on the state.

I hope this helps!

Use a loading screen with an if else statement.

if (likes) "Loading...": not loading etc...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM