【问题标题】:How to consume API in Redux application如何在 Redux 应用程序中使用 API
【发布时间】:2021-11-15 05:11:33
【问题描述】:

我有一个 CRUD 应用程序,除了创建、编辑和删除帖子之外,我还需要使用一个 API,其中已经创建了一些帖子以最初呈现到屏幕上,但我不知道如何应用到我的代码(我正在使用 redux)...我已经创建了所有其他功能并将在此处发布代码,我想知道如何使用此 API 以及我需要在代码中更改什么这个。

api:https://dev.codeleap.co.uk/careers/

postslice.js:

   import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";

export const fetchPosts = createAsyncThunk(
  "posts/fetchPosts",
  async () => {
    try {
      const response = await fetch("https://dev.codeleap.co.uk/careers/");
      const data = await response.json();
      return data.results;
    } catch {
      
    }
  }
)

const postsSlice = createSlice({
  name: "posts",
  initialState: [],
  reducers: {
    addPost (state, action) {
      state.push(action.payload); 
    },
  editPost(state, action) {
    const { id, title, content } = action.payload;
    const existingPost = state.find((post) => post.id === id);
    if (existingPost) {
      existingPost.title = title
      existingPost.content = content
    }
  },
  postDeleted(state, action) {
    const { id } = action.payload;
    const existingPost = state.some((post) => post.id === id);
    if (existingPost) {
    return state.filter((post) => post.id !== id);
    }
    },
    extraReducers: builder => {
      builder.addCase(fetchPosts.fulfilled, (state, action) => {
        return state.concat(action.payload);
      });
    },
},

});

export const { addPost, editPost, postDeleted } = postsSlice.actions

export default postsSlice

mainscreen.js:

import React, { useState } from "react";

import "../_assets/App.css";
import "../_assets/mainscreen.css";
import { MdDeleteForever } from "react-icons/md";
import { FiEdit } from "react-icons/fi";
import { useSelector } from "react-redux";
import Signup from "./signup";
import Timestamp from "../components/Timestamp";

import BoxPost from "../components/boxPost.js";
import Modal from "../components/modal.jsx";
import EditModal from "../components/editmodal.jsx";

function MainScreen() {
  const { currentUser } = useSelector((state) => state.user);
  const posts = useSelector((state) => state.loadPosts);

  const [openEditModal, setOpenEditModal] = useState();
  const [openModal, setOpenModal] = useState();

  if (currentUser === "") {
    return <Signup />;
  } else {
    console.log({ posts });
    return (
      <div className="containerMainScreen">
        {openModal && <Modal deleteId={openModal} closeModal={setOpenModal} />}
        {openEditModal && (
          <EditModal editId={openEditModal} closeModal={setOpenEditModal} />
        )}
        <div className="bar">
          <h1>Codeleap</h1>
        </div>
        <BoxPost />
        {posts
          .slice()
          .reverse()
          .map((post) => (
            <div className="boxPost" key={post.id}>
              <div className="bar">
                <h1>{post.title}</h1>
                {currentUser === post.user ? (
                  <div className="bar">
                    <MdDeleteForever
                      className="icon"
                      onClick={() => {
                        setOpenModal(post.id);
                      }}
                    />
                    <FiEdit
                      onClick={() => {
                        setOpenEditModal(post.id);
                      }}
                      style={{
                        color: "white",
                        fontSize: "45px",
                        paddingLeft: "23px",
                      }}
                    />
                  </div>
                ) : null}
              </div>
              <div id="postowner">
                <div id="informations">
                  <h3>@{post.user}</h3>
                  <h3>
                    <Timestamp timestamp={post.date} />
                  </h3>
                </div>
                <br></br>
                <textarea
                  style={{ border: "none" }}
                  value={post.content}
                ></textarea>
              </div>
            </div>
          ))}
      </div>
    );
  }
}
export default MainScreen;

store.js:

 import { configureStore } from '@reduxjs/toolkit';
  import userSlice from './userslice';
  import postsSlice from './postsslice'

const store = configureStore({
    reducer: {
        user: userSlice.reducer,
        loadPosts: postsSlice.reducer

    },
  })

  export default store

editmodal(编辑帖子的模式):

import React, {useState, useEffect} from 'react';
import '../_assets/modaledit.css';
import { useDispatch, useSelector } from 'react-redux';

import {editPost} from '../redux/postsslice';

export function EditModal({ closeModal, editId}) {

    
    const post = useSelector((state) => state.loadPosts.find((post) => post.id === editId))
    const dispatch = useDispatch()



    const [title, setTitle] = useState(post?.title)
    const [content, setContent] = useState(post?.content)

    const [buttonGreyOut, setButtonGreyOut] = useState("#cccccc");

   useEffect(() => {
    if (title && content !== "") {
      setButtonGreyOut("black");
    } else {
      setButtonGreyOut("#cccccc");
    }
  },[title, content]);

    

    const onTitleChanged = e => setTitle(e.target.value)
    const onContentChanged = e => setContent(e.target.value)

    const onSavePostClicked = (e) => {
        
            e.preventDefault()
            dispatch(editPost({id: editId, title, content}))
            closeModal(false)
           
    }


    return (
        <div className="editmodalbackground">
            <div className="editmodalcontainer">
                <div className="title"><h1>Edit item</h1></div>
                <h2>Title</h2>
                <form>
                <input
              type="text"
              placeholder="Hello World"
              name="name"
              value={title}
              onChange={onTitleChanged}
            ></input>
            <h2>Content</h2>
            <textarea
              placeholder="Content"
              name="content"
              value={content}
              onChange={onContentChanged}
            ></textarea>
                
                    <button onClick={onSavePostClicked} disabled={!title || !content} style={{backgroundColor: buttonGreyOut}}>SAVE</button></form>
                
            </div>
        </div>
    )
}

export default EditModal

删除模式:

import React from 'react';
import '../_assets/modal.css';
import { useDispatch } from 'react-redux';

import { postDeleted } from '../redux/postsslice'


function Modal({ closeModal, deleteId }) {

    const dispatch = useDispatch()

    const onSavePostClicked = (e) => {   
        e.preventDefault()
        dispatch(postDeleted({id: deleteId}))
        closeModal(false)
       
}

    return (
        <div className="modalBackground">
            <div className="modalContainer">
                <div className="title"><h1>Are you sure you want to delete this item</h1></div>
                <div className="options">
                    <button onClick={() => closeModal(false)}>Cancel</button>
                    <button  onClick={onSavePostClicked}>OK</button>
                </div>
            </div>
        </div>
    )
}

export default Modal

【问题讨论】:

  • 我明白了。好吧,您可以使用挂载 (empty dependency array) useEffect 钩子并通过 fetch 发出 GET 请求,然后调度一个操作以将响应结果存储到帖子状态中,或者如果您'有点冒险,您可以尝试使用 RTK 中包含的异步中间件。如果您不熟悉 Thunks/异步操作,那么使用 useEffect 钩子和调度操作可能更容易理解。

标签: reactjs rest react-redux react-hooks


【解决方案1】:

实际上,现在在 RTK 中使用 Thunk 也很简单。

创建一个新的异步操作 (createAsyncThunk)。该函数采用字符串 type 和有效负载创建者函数参数。向端点发出 GET 请求并返回解压后的响应值。

import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";

export const fetchPosts = createAsyncThunk(
  "posts/fetchPosts",
   async () => {
    try {
      const response = await fetch("https://dev.codeleap.co.uk/careers/");
      const data = await response.json();
      return data.results;
    } catch {
      // if you want to handle any errors, failed fetches, etc...
    }
  }
);

并在帖子切片的extraReducers 键中添加一个新的reducer case。在这里,您将使用已完成的操作类型并返回当前状态以及从 reducer 函数连接到数组的获取的帖子。

const postsSlice = createSlice({
  name: "posts",
  initialState,
  reducers: {
    ... actions/reducers
  },
  extraReducers: builder => {
    builder.addCase(fetchPosts.fulfilled, (state, action) => {
      return state.concat(action.payload);
    });
  },
});

应用程序

您需要导入 fetchPosts 操作并在组件挂载时调度它。

import { useDispatch } from "react-redux";
import { fetchPosts } from "./redux/posts.slice";

...

function App() {
  const dispatch = useDispatch();

  useEffect(() => {
    dispatch(fetchPosts());
  }, [dispatch]);

  return (
    <Router>
      <Routes />
    </Router>
  );
}

主屏幕

此新数据与您建立的状态形状不完全匹配,即它使用 username 字段属性,而您使用的是 author。这里的解决方案是切换到在您的MainScreen 组件中使用username。只有两个地方需要更新。

  1. 提交新帖子时,现在使用username 而不是`author。

    const handleSubmitSendPost = (e) => {
      e.preventDefault();
      dispatch(
        addPost({
          id: uuidV4(),
          title,
          content,
          username: currentUser // <-- author to username
        })
      );
      setTitle("");
      setContent("");
    };
    
  2. 映射数据时,是post.username,而不是post.author

    <h3>@{post.username}</h3> // <-- post.author to post.username
    

【讨论】:

  • 我在redux devtools上检查了一下,数据被正确捕获了,但是我认为渲染没有工作,是我的代码有错误还是我应该仍然实现捕获数据的渲染?
  • @HeitorKenzou 是否正确捕获到 redux 状态?但是您在渲染它时遇到问题?你正在经历什么?您是否看到任何错误?
  • api 中的帖子出现在 redux devtools 操作中,但帖子没有呈现在屏幕上
  • @HeitorKenzou 他们肯定会在我链接的 CSB 中为我呈现 10 个新帖子。它在那里工作,适合你吗?
  • 不...你能把沙盒链接发给我,我可以用我的代码检查它吗?
猜你喜欢
  • 2019-02-07
  • 2016-12-11
  • 1970-01-01
  • 2023-03-17
  • 2016-06-04
  • 1970-01-01
  • 2016-08-10
  • 2017-11-22
  • 1970-01-01
相关资源
最近更新 更多