react : datas displaying in the wrong order after submit

Viewed 11

I'm using react with Context Api and react hook form to add a new post. When I submit a post, the new post is in the last position but when I refresh the page, the post goes to the top position. I want the new post to be in the top position immediately. Do you know what I did wrong, please?

PostsContext.tsx

export interface Post {
  id: number
  content: string
  thumbnail: {
    url: string
  }
  created_at: string
  updated_at: string
  userId: number
}

export interface PostsState {
  posts: Post[]
}

export type PostsAction =
  | { type: 'SET_POSTS'; payload: Post[] }
  | { type: 'ADD_POST'; payload: Post }

const initialState: PostsState = {
  posts: [],
}

const reducer = (state: PostsState, action: PostsAction) => {
  switch (action.type) {
    case 'SET_POSTS':
      return { posts: action.payload }
    case 'ADD_POST':
      return { posts: [...state.posts, action.payload] }
    default:
      return state
  }
}

export const PostsContext = createContext<{
  state: PostsState
  dispatch: React.Dispatch<PostsAction>
}>({
  state: initialState,
  dispatch: () => null,
})

export const PostsProvider = ({ children }: PropsWithChildren<{}>) => {
  const [state, dispatch] = useReducer(reducer, initialState)
  return <PostsContext.Provider value={{ state, dispatch }}>{children}</PostsContext.Provider>
}

Feed.tsx

const Feed = () => {
  const { state, dispatch } = usePostsContext()

  useEffect(() => {
    const fetchPosts = async () => {
      const res = await fetch(`${import.meta.env.VITE_API_URL}/api/posts`, {
        credentials: 'include',
      })
      const data = await res.json()
      if (res.ok) {
        dispatch({ type: 'SET_POSTS', payload: data })
      }
    }
    fetchPosts()
  }, [])

  return (
    <div className="container mx-auto">
      <PostForm />
      {state.posts.length < 1 ? (
        <div className="mt-4 p-8 text-center border border-gray-200 rounded-lg">
          <h2 className="text-2xl font-medium">There's nothing here...</h2>

          <p className="mt-4 text-sm text-gray-500">
            Created posts will appear here, try creating one!
          </p>
        </div>
      ) : (
        state.posts.map((post) => <Posts key={post.id} post={post} />)
      )}
    </div>
  )
}

export default Feed

PostForm.tsx

const PostForm = () => {
  const { dispatch } = usePostsContext()
  const {
    register,
    handleSubmit,
    control,
    watch,
    reset,
    formState: { isSubmitSuccessful, errors },
  } = useForm<FormInput>({
    defaultValues: {
      content: '',
    },
    resolver: yupResolver(postSchema),
  })
  const selectedFile = watch('thumbnailFile')

  const onSubmit: SubmitHandler<FormInput> = async (data) => {
    const formData = new FormData()
    formData.append('content', data.content)
    formData.append('thumbnailFile', data.thumbnailFile[0])

    const response = await fetch(`${import.meta.env.VITE_API_URL}/api/posts`, {
      method: 'POST',
      credentials: 'include',
      body: formData,
    })
    const post = await response.json()
    if (response.ok) {
      console.log('post created', post)
      dispatch({ type: 'ADD_POST', payload: post })
      reset()
    }
  }

  return (
    <MyForm />
  )
}

export default PostForm
1 Answers

Looks like your ADD_POST reducer is adding a new post to the end of the list since you are placing the action payload after the destructing of your old posts.

To place a post at the beginning of the list you need to place the action payload before destructing the old list of posts.

i.e.,

case 'ADD_POST':
      return { posts: [action.payload, ...state.posts] }
Related