如何从不是父或子的另一个组件更改功能组件的状态?

Posted

技术标签:

【中文标题】如何从不是父或子的另一个组件更改功能组件的状态?【英文标题】:How to change functional component's state from another component that is not a parent or a child? 【发布时间】:2020-08-15 09:18:15 【问题描述】:

在我的代码中,我有一个功能组件,它使用“加载更多”分页来呈现数据库中的用户列表,当您单击用户时,它会在一些操作旁边显示用户的详细信息是列表,类似于文件资源管理器。其中一项操作是删除用户。当您这样做时,用户列表不会更新,如果您单击刚刚删除的用户,则会出现执行错误,因为代码尝试使用来自空对象的信息。在不使用类组件的情况下,在用户删除时更新列表组件的最佳方法是什么?

App.js - 列表和细节组件的父级

<BrowserRouter>
  <div id = "app">
    <div className = "section sectionTop">
      <Route path = "/" component = BaseTop/>
    </div>
    <div className = "Main">
      <div className = "section sectionMain sectionLeft">
      <Route path = "/" component = BaseLeft/>
      </div>
      <div className = "section sectionMain sectionCenter">
        <Route path = "/" exact component = BaseCenter/>
        <Route path = "/listusers" component = UserList/>
      </div>
      <div className = "section sectionMain sectionRight">
        <Route path = "/" exact component = BaseRight/>
        <Route path = "/adduser" component = UserAdd/>
        <Route path = "/listusers/:id" component = UserInfo/>
      </div>
    </div>
  </div>
</BrowserRouter>

UserList.js - 用户列表

const [users, setUsers] = useState ([]);
const [page, setPage] = useState (0);

useEffect
(
    () =>
    
        setPage (page+1);
    ,
    []
);

useEffect
(
    () =>
    
        try
        
            const runEffect = async () =>
            
                const response = await api.get
                (
                    `/users?page=$page`
                );
                setUsers ([...users, ...response.data.docs]);
            
            runEffect();
        
        catch (error)
        
            console.log (error);
        
    ,
    [page]
);

return (
    <div className = "userListArea">
        
            users.map
            (
                (user, index) =>
                
                    return (
                        <div key = index className = "user">
                            <Link key = index to = `/listusers/$user._id`>
                                <button
                                className = "buttonUser"
                                key = index>
                                    user.name
                                </button>
                            </Link>
                        </div>
                    )
                
            )
        
        <button
        className = "buttonLoadMore"
        onClick = () => setPage (page+1)>
            Carregar mais
        </button>
    </div>
)

UserInfo.js - 用户的详细信息

const [user, setUser] = useState ();
const [_id, set_id] = useState ("");

useEffect
(
    () =>
    
        if (user.hasOwnProperty ("_id") === false)
        
            set_id (match.params.id);
        
        else
        
            if (user._id !== match.params.id)
            
                set_id (match.params.id);
            
        
        const runEffect = async () =>
        
            const response = await api.get
            (
                "/searchuser",
                
                    params:
                    
                        _id
                    
                
            )
            if (user.hasOwnProperty ("name") === false)
            
                setUser (response.data);
            
            else
            
                if (user.name !== response.data.name)
                
                    setUser (response.data);
                
            
        
        runEffect();
    
);

async function handleDeleteUser (_id)

    if (window.confirm(`Você realmente deseja remover o usuário $user.name?`))
    
        const response = await api.delete
        (
            "/users",
            
                params:
                    
                        _id
                    
            
        );
        if (response.data._id === _id)
        
            window.alert(`O usuário $user.name foi excluído.`);
        
    


return (
    <div className = "userInfoArea">
        <div className = "name">user.name</div>
        <div className = "email">user.email</div>
        <button className = "buttonEdit">Editar</button>
        <Link to = "/listusers">
            <button
            className = "buttonDelete"
            onClick = () => handleDeleteUser (user._id)
            >
                Excluir
            </button>
        </Link>
    </div>
)

【问题讨论】:

您好。您布置路由器的方式非常奇怪,并且会给您的组件继承增加很多复杂性。你能重构它,让每条路线都有一个带有部分的模板吗? 如果不使用外部状态管理解决方案,例如Redux 或新的useContext hook,您将无法更新非父或子组件的状态 @Redline 非常感谢。 useContext 钩子做得很好。除了在那种情况下使用它之外,我将能够在存储当前登录的用户时使用它。 @DanielDuong 我明白了。我将按照您建议的方式组织我的代码。但是,就目前而言,Redline 已经回答了我的问题,我认为我没有必要更新这个问题。但是,如果出于任何原因,您真的希望看到代码组织,请告诉我,我会更新问题。 @Capeudinho 刚刚发布了一个答案,以防其他人遇到同样的问题 【参考方案1】:

如果不使用外部状态管理解决方案(例如Redux 或新的useContext api/hook),您无法更新不是父级或子级的组件的状态。这些库涉及将所有单个组件的状态移动到更大的集中式全局状态,所有组件都可以更新和访问该状态。

【讨论】:

以上是关于如何从不是父或子的另一个组件更改功能组件的状态?的主要内容,如果未能解决你的问题,请参考以下文章

CSS 在没有父或子内容的情况下悬停时更改边框大小“跳跃”

Java - 从单独的组件中重绘组件

TFS 中的跨分支合并?

如何在反应中将最新状态从子组件传递给父组件

Discord.js 我的机器人在 Heroku 上托管时找不到任何父或子频道

Laravel 递归关系