【问题标题】:React - Cleanest way to re-render component on array updateReact - 在数组更新时重新渲染组件的最简洁方法
【发布时间】:2021-04-22 11:01:36
【问题描述】:

我是使用 react 钩子的新手,由于以下原因,我一直在努力使用 useEffect 钩子:

我有一个简单的评论板来发布 cmets,并在发布的 cmets 列表下方,我希望每次有新评论时组件都会重新加载以显示它。

这些注释是从 API 中的 useEffect 第一个参数得到的,问题是第二个参数(依赖数组),如果我不放这个参数会导致无限循环,如果我放一个空数组它会避免无限循环,但我的组件不会渲染发布的新 cmets,如果我输入变量状态“cmets”也会导致无限循环。所以在互联网上导航我找到了“解决方案”,它只是添加另一个变量状态以将其传递给依赖项数组,并在我调用函数“makecomponent”时进行修改,这样它会刷新状态,强制我的组件重新渲染:

export const ForumApp = () => {
    const [comment, setComment] = useState("")
    const [comments, setComments] = useState([])
    const [count, setCount] = useState(0)
 
    useEffect(() => {
        const getComments = async () => {
            try {
                const query = await axios.get("http://127.0.0.1:8000/comments")
                setComments(query.data.comments)
            } catch (error) {
                console.log(error);
            }
        }
        getComments()
    }, [count])

    const handleEnter = (e: KeyboardEvent) => {
        if (e.key === ENTER && !e.shiftKey) {
            e.preventDefault();
            makeComment();
        }
    };

    const makeComment = async () => {
        try {
            await axios.post("http://127.0.0.1:8000/comments/create", {
                time: "1:29",
                content: comment,
            })

            alert("Comment posted!")
            setCount((prevCount) => prevCount + 1)
            setComment("")
        } catch (error) {
            console.log(error)
        }
    };

    return (
        <div className="forum-app">
            <div className="comment-board">
                <h1>Post your comment</h1>
                <textarea
                    placeholder="Write something to us"
                    onChange={(e) => setComment(e.target.value)}
                    onKeyDown={handleEnter}
                    value={comment}
                ></textarea>
                <br />
                <button onClick={makeComment}>Comment</button>
            </div>
            <ol>
                {
                    comments.map(({_id, content}) => (
                        <CommentItem key={_id} content={content}/>
                    ))
                }
            </ol>
        </div>
    )
}

所以基本上我的问题是:这是执行此操作的“正确”方式,还是存在另一种更清洁的方式,无需声明另一个变量,只使用我拥有的内容?

【问题讨论】:

    标签: javascript reactjs typescript use-effect


    【解决方案1】:

    另一种更简洁的方式可能是使用 redux 和 redux-thunk 来调度一个动作来进行异步获取。当获取数据并更新 redux 存储时,连接的组件会使用存储中的最新状态重新呈现。

    如果应用程序还没有使用 redux,这是要添加到应用程序中的许多新的移动部件。这里的重要方面是触发另一个数据获取所需的额外“标志”。

    我认为我建议对您的代码进行更直接的调整是实际定义一个fetchData 状态,该状态根据获取数据的状态打开和关闭。这使您还可以在获取数据时显示任何“加载”状态。

    export const ForumApp = () => {
        const [comment, setComment] = useState("")
        const [comments, setComments] = useState([])
        const [fetchComments, setFetchComments] = useState(true) // <-- for initial mount call
     
        useEffect(() => {
            const getComments = async () => {
                try {
                    const query = await axios.get("http://127.0.0.1:8000/comments")
                    setComments(query.data.comments)
                } catch (error) {
                    console.log(error);
                } finally {
                  setFetchComments(false); // <-- signify fetching complete
                }
            }
    
            fetchComments && getComments(); // <-- fetch comments if true
        }, [fetchComments])
    
        const handleEnter = (e: KeyboardEvent) => {
            if (e.key === ENTER && !e.shiftKey) {
                e.preventDefault();
                makeComment();
            }
        };
    
        const makeComment = async () => {
            try {
                await axios.post("http://127.0.0.1:8000/comments/create", {
                    time: "1:29",
                    content: comment,
                })
    
                alert("Comment posted!")
                setFetchComments(true); // <-- signify start fetching
                setComment("")
            } catch (error) {
                console.log(error)
            }
        };
    
        return (
            <div className="forum-app">
                <div className="comment-board">
                    <h1>Post your comment</h1>
                    <textarea
                        placeholder="Write something to us"
                        onChange={(e) => setComment(e.target.value)}
                        onKeyDown={handleEnter}
                        value={comment}
                    ></textarea>
                    <br />
                    <button onClick={makeComment}>Comment</button>
                </div>
                <ol>
                    {
                        fetchComments 
                          ? "...fetching comments" // <-- loading state
                          : comments.map(({_id, content}) => (
                            <CommentItem key={_id} content={content}/>
                        ))
                    }
                </ol>
            </div>
        )
    }
    

    【讨论】:

    • 那么最干净的方法是使用redux?或者至少是你推荐的?我一直在避免它,但我想是时候学习它了。感谢您的代码,目前是我正在寻找的。​​span>
    • @AbrahamArreola 我想在这种情况下“干净”有点主观。我个人认为两者都可以被认为是“干净的”。使用您的方式和我的回答是干净的,因为所有逻辑都包含在一个单元中,易于阅读,并且是一种简单的常见模式。 Redux 是一种干净的事件驱动模式,它允许您抽象和模块化常见的功能,例如异步获取数据和使用常见的应用程序状态。这完全取决于您的应用程序的需求和要求。干杯,祝你好运。
    • @Draw Reese 再次您好,目前我学习并在我的应用程序中实现了 redux,但现在我有一个类似的问题:我是否还需要一个附加标志来触发 fetch 或者我可以用什么来实现这一点我有?我尝试在依赖数组中传递调度,但它不起作用。干杯!
    • @AbrahamArreola 如果您已经在应用程序中实现了 redux,那么这里的模式是通过中间件(如 redux-thunk)使用 asynchronous actions。获取逻辑移至异步操作。您不需要使用任何额外的标志来触发操作,因为您可以直接将异步操作(例如从按钮单击)分派到您的商店。如果您需要更多帮助,请随时在 SO 和(在)我这里提出另一个问题,并提供指向它的链接,您至少可能会更快地得到更多的关注。
    • 我也用 redux-thunk 实现了异步操作,我知道我可以将任何操作分派到商店,但是我想通过这个实现我在这篇文章中提出的相同要求:列出所有 cmets当组件显示并在做出新评论时更新列表。实际上,我确实将调度放在了 useEffect 函数中,但是当我添加新评论时,组件不会重新呈现。现在我的问题是,我是否仍然需要一个标志来控制数据获取,或者我只能使用 redux 方法来实现这一点?
    【解决方案2】:

    我更喜欢将用户和函数作为生成器,并将状态作为参数,然后返回我的地图组件以制作可读的代码


    通过这种方式,您无需了解 JSX 即可了解代码逻辑,并且我认为调试更灵活

    export const ForumApp = () => {
    const [comment, setComment] = useState("")
    const [comments, setComments] = useState([])
    const [count, setCount] = useState(0)
    
    useEffect(() => {
        const getComments = async () => {
            try {
                const query = await axios.get("http://127.0.0.1:8000/comments")
                setComments(query.data.comments)
            } catch (error) {
                console.log(error);
            }
        }
        getComments()
    }, [count])
    
    const handleEnter = (e: KeyboardEvent) => {
        if (e.key === ENTER && !e.shiftKey) {
            e.preventDefault();
            makeComment();
        }
    };
    
    const makeComment = async () => {
        try {
            await axios.post("http://127.0.0.1:8000/comments/create", {
                time: "1:29",
                content: comment,
            })
    
            alert("Comment posted!")
            setCount((prevCount) => prevCount + 1)
            setComment("")
        } catch (error) {
            console.log(error)
        }
    };
    const commentGenerator = (data) => (data.map(({ _id, content }) => <CommentItem key={_id} content={content} />))
    
    return (
        <div className="forum-app">
            <div className="comment-board">
                <h1>Post your comment</h1>
                <textarea
                    placeholder="Write something to us"
                    onChange={(e) => setComment(e.target.value)}
                    onKeyDown={handleEnter}
                    value={comment}
                ></textarea>
                <br />
                <button onClick={makeComment}>Comment</button>
            </div>
            <ol>
                {commentGenerator(comments)}
            </ol>
        </div>
    )
    

    }

    【讨论】:

      猜你喜欢
      • 2020-03-05
      • 2020-10-24
      • 2023-03-28
      • 2021-06-19
      • 2018-04-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多