代码之家  ›  专栏  ›  技术社区  ›  Michael Coxon

如何管理react功能组件中预先存在的和不断变化的冗余状态,或再次停止以前的数据加载

  •  0
  • Michael Coxon  · 技术社区  · 3 年前

    我正在努力学习钩子和 useState 这个方法让我很困惑。我正在以数组的形式为一个状态分配一个初始值。中的set方法 使用状态 无论有没有spread语法,都不适用于我。

    我在另一台电脑上制作了一个API,我正在调用它并获取我想要设置为状态的数据。

    这是我的代码:

    <div id="root"></div>
    
    <script type="text/babel" defer>
    // import React, { useState, useEffect } from "react";
    // import ReactDOM from "react-dom";
    const { useState, useEffect } = React; // web-browser variant
    
    const StateSelector = () => {
      const initialValue = [
        {
          category: "",
          photo: "",
          description: "",
          id: 0,
          name: "",
          rating: 0
        }
      ];
    
      const [movies, setMovies] = useState(initialValue);
    
      useEffect(() => {
        (async function() {
          try {
            // const response = await fetch("http://192.168.1.164:5000/movies/display");
            // const json = await response.json();
            // const result = json.data.result;
            const result = [
              {
                category: "cat1",
                description: "desc1",
                id: "1546514491119",
                name: "randomname2",
                photo: null,
                rating: "3"
              },
              {
                category: "cat2",
                description: "desc1",
                id: "1546837819818",
                name: "randomname1",
                rating: "5"
              }
            ];
            console.log("result =", result);
            setMovies(result);
            console.log("movies =", movies);
          } catch (e) {
            console.error(e);
          }
        })();
      }, []);
    
      return <p>hello</p>;
    };
    
    const rootElement = document.getElementById("root");
    ReactDOM.render(<StateSelector />, rootElement);
    </script>
    
    <script src="https://unpkg.com/@babel/standalone@7/babel.min.js"></script>
    <script src="https://unpkg.com/react@17/umd/react.production.min.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script>

    也不 setMovies(result) 也没有 setMovies(...result) 作品

    我希望 result 要推入的变量 movies 大堆

    0 回复  |  直到 4 年前
        1
  •  1
  •   Edward Thomas    2 年前

    很 like .setState() in class components 通过扩展创建 React.Component 或 React.PureComponent ,使用提供的更新程序进行状态更新 useState hook也是异步的,不会立即反映出来。

    此外,这里的主要问题不仅是异步性质,而且是状态值由基于当前闭包的函数使用,状态更新将反映在下一次重新呈现中,现有闭包不受影响,而是创建新的闭包 。现在在当前状态下,钩子中的值是由现有的闭包获得的,当重新呈现时,闭包会根据是否再次创建函数来更新。

    即使您添加 setTimeout 函数,尽管超时将在重新渲染发生的一段时间后运行 setTimeout 仍将使用其上一个闭包中的值,而不是更新后的值。

    setMovies(result);
    console.log(movies) // movies here will not be updated
    

    如果要对状态更新执行操作,则需要使用 useEffect 钩子,很像使用 componentDidUpdate 类中的组件,因为setter由返回 使用状态 没有回调模式

    useEffect(() => {
        // action on update of movies
    }, [movies]);
    

    就更新状态的语法而言, setMovies(result) 将取代以前的 movies 状态中的值与异步请求中可用的值进行比较。

    但是,如果要将响应与以前存在的值合并,则必须使用状态更新的回调语法以及正确使用排列语法,如

    setMovies(prevMovies => ([...prevMovies, ...result]));
    
        2
  •  0
  •   Akaisteph7    2 年前

    的其他详细信息 previous answer :

    而React setState 是 异步(包括类和钩子),并且很容易使用这个事实来解释观察到的行为,它不是 原因 它会发生。

    TLDR:原因是 closure 围绕不可变的范围 const 价值


    解决:

    • 读取呈现函数中的值(不在嵌套函数中):

        useEffect(() => { setMovies(result) }, [])
        console.log(movies)
      
    • 将变量添加到依赖项中(并使用 react-hooks/exhaustive-deps esint规则):

        useEffect(() => { setMovies(result) }, [])
        useEffect(() => { console.log(movies) }, [movies])
      
    • 使用临时变量:

        useEffect(() => {
          const newMovies = result
          console.log(newMovies)
          setMovies(newMovies)
        }, [])
      
    • 使用可变引用(如果我们不需要状态,只想记住值——更新引用不会触发重新渲染):

        const moviesRef = useRef(initialValue)
        useEffect(() => {
          moviesRef.current = result
          console.log(moviesRef.current)
        }, [])
      

    解释为什么会发生这种情况:

    如果异步是唯一的原因,那么就有可能 await setState() .

    然而,两者 props 和 state 是 assumed to be unchanging during 1 render .

    对待 this.state 好像它是不可变的。

    使用钩子时,通过使用 常数值 与 const 关键字:

    const [state, setState] = useState('initial')
    

    两个渲染之间的值可能不同,但在渲染本身内部和任何渲染内部都保持不变 closures (即使在渲染完成后仍能使用更长时间的功能,例如。 useEffect ,事件处理程序,在任何Promise或setTimeout内)。

    考虑跟随fake,但是 同步的 ,类似React的实现:

    // sync implementation:
    
    let internalState
    let renderAgain
    
    const setState = (updateFn) => {
      internalState = updateFn(internalState)
      renderAgain()
    }
    
    const useState = (defaultState) => {
      if (!internalState) {
        internalState = defaultState
      }
      return [internalState, setState]
    }
    
    const render = (component, node) => {
      const {html, handleClick} = component()
      node.innerHTML = html
      renderAgain = () => render(component, node)
      return handleClick
    }
    
    // test:
    
    const MyComponent = () => {
      const [x, setX] = useState(1)
      console.log('in render:', x) // ✅
      
      const handleClick = () => {
        setX(current => current + 1)
        console.log('in handler/effect/Promise/setTimeout:', x) // ❌ NOT updated
      }
      
      return {
        html: `<button>${x}</button>`,
        handleClick
      }
    }
    
    const triggerClick = render(MyComponent, document.getElementById('root'))
    triggerClick()
    triggerClick()
    triggerClick()
    <div id="root"></div>
        3
  •  0
  •   ishandutta2007    2 年前

    我知道已经有了很好的答案。但我想给出另一个想法,如何解决同样的问题,并使用我的模块访问最新的“电影”状态 react-useStateRef 它的每周下载量超过11000次。

    正如您所理解的,通过使用React state,您可以在每次状态更改时呈现页面。但通过使用React ref,您总是可以获得最新的值。

    所以模块 react-useStateRef 让您一起使用state和ref。它向后兼容 React.useState ,所以您可以直接更换 import 陈述

    const { useEffect } = React
    import { useState } from 'react-usestateref'
    
      const [movies, setMovies] = useState(initialValue);
    
      useEffect(() => {
        (async function() {
          try {
    
            const result = [
              {
                id: "1546514491119",
              },
            ];
            console.log("result =", result);
            setMovies(result);
            console.log("movies =", movies.current); // will give you the latest results
          } catch (e) {
            console.error(e);
          }
        })();
      }, []);
    

    更多信息:

        4
  •  0
  •   sharath shankar    2 年前

    React的useEffect有自己的状态/生命周期。它与状态的突变有关,在效果被破坏之前,它不会更新状态。

    只要在参数状态下传递一个参数,或者将其保留为黑色数组,它就会完美地工作。

    React.useEffect(() => {
        console.log("effect");
        (async () => {
            try {
                let result = await fetch("/query/countries");
                const res = await result.json();
                let result1 = await fetch("/query/projects");
                const res1 = await result1.json();
                let result11 = await fetch("/query/regions");
                const res11 = await result11.json();
                setData({
                    countries: res,
                    projects: res1,
                    regions: res11
                });
            } catch {}
        })(data)
    }, [setData])
    # or use this
    useEffect(() => {
        (async () => {
            try {
                await Promise.all([
                    fetch("/query/countries").then((response) => response.json()),
                    fetch("/query/projects").then((response) => response.json()),
                    fetch("/query/regions").then((response) => response.json())
                ]).then(([country, project, region]) => {
                    // console.log(country, project, region);
                    setData({
                        countries: country,
                        projects: project,
                        regions: region
                    });
                })
            } catch {
                console.log("data fetch error")
            }
        })()
    }, [setData]);
    

    或者,您可以尝试React.useRef()在React钩子中进行即时更改。

    const movies = React.useRef(null);
    useEffect(() => {
    movies.current='values';
    console.log(movies.current)
    }, [])