React:当另一个组件发生某些事情时如何更新一个组件 [英] React: How to update one component, when something happens on another component

查看:57
本文介绍了React:当另一个组件发生某些事情时如何更新一个组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有表格的应用程序,表格中有一个复选框可以将租户设置为活动,这个变量是一个全局变量,会影响用户在应用程序的其他屏幕上的操作.

在应用程序的右上角,我有另一个名为 ActiveTenant 的组件,它基本上显示了用户目前在哪个租户中工作.

表格组件的代码是这样的:

import React, { Component } from 'react';从'antd'导入{表,收音机};import { adalApiFetch } from '../../adalConfig';从'../../components/notification'导入通知;类 ListTenants 扩展组件 {构造函数(道具){超级(道具);this.state = {数据: []};}fetchData = () =>{adalApiFetch(获取,/租户",{}).then(response => response.json()).then(responseJson => {如果(!this.isCancelled){const 结果= responseJson.map(row => ({键:row.id,TestSiteCollectionUrl: row.TestSiteCollectionUrl,租户名称:row.TenantName,电子邮件:row.Email}))this.setState({数据:结果});}}).catch(错误=> {控制台错误(错误);});};componentDidMount(){this.fetchData();}使成为() {常量列 = [{title: '租户名称',数据索引:'租户名称',键:'租户名称',},{title: 'TestSiteCollectionUrl',dataIndex: 'TestSiteCollectionUrl',key: 'TestSiteCollectionUrl',},{title: '电子邮件',数据索引:'电子邮件',键:'电子邮件',}];//rowSelection 对象表示需要行选择const rowSelection = {onChange: (selectedRowKeys, selectedRows) =>{if(selectedRows[0].TenantName != undefined){console.log(selectedRows[0].TenantName);常量选项 = {方法:'发布'};adalApiFetch(fetch, "/Tenant/SetTenantActive?TenantName="+selectedRows[0].TenantName.toString(), options).then(响应=>{如果(响应.状态 === 200){通知('成功','租户设置为活动','');}别的{抛出错误";}}).catch(错误=> {通知('错误','租户未激活',错误);控制台错误(错误);});}},getCheckboxProps:记录=>({类型:收音机}),};返回 (<表格 rowSelection={rowSelection} columns={columns} dataSource={this.state.data}/>);}}导出默认 ListTenants;

而且ActiveTenant组件的代码也很简单

import React, { Component } from 'react';从 '../../redux/auth/actions' 导入 authAction;import { adalApiFetch } from '../../adalConfig';类 ActiveTenant 扩展组件 {构造函数(道具){超级(道具);this.state = {租户: ''};}fetchData = () =>{adalApiFetch(fetch, "/Tenant/GetActiveTenant", {}).then(response => response.json()).then(responseJson => {如果(!this.isCancelled){this.setState({ 租户: responseJson.TenantName });}}).catch(错误=> {this.setState({租户:''});控制台错误(错误);});};componentDidMount(){this.fetchData();}使成为() {返回 (<div>您正在使用租户:{this.state.tenant }</div>);}}导出默认 ActiveTenant;

问题是,如果我在我的数据库上注册了多个租户并将它们设置为活动状态,则会发生服务器端操作,并且状态会发生变化,但是在右上角它仍然显示旧租户为活动状态,直到我按 F5 刷新浏览器.

我怎样才能做到这一点?

为了完全理解我的代码,我需要在其他组件下面粘贴:

  1. 包含活动租户的 TopBar

    import React, { Component } from "react";从react-redux"导入{连接};从antd"导入{布局};从../../redux/app/actions"导入appActions;从./topbarUser"导入 TopbarUser;从./topbar.style"导入TopbarWrapper;从./activetenant"导入 ActiveTenant;从 './topbarNotification' 导入 TopbarNotification;const { 标题 } = 布局;const { toggleCollapsed } = appActions;class Topbar 扩展组件 {使成为() {const { toggleCollapsed,url,customizedTheme,locale } = this.props;constcollapsed = this.props.collapsed &&!this.props.openDrawer;常量样式 = {背景:customizedTheme.backgroundColor,位置:'固定',宽度: '100%',身高:70};返回 (<TopbarWrapper><标题样式={样式}类名={崩溃了?"isomorphicTopbar 折叠" : "isomorphicTopbar"}><div className="isoLeft"><按钮类名={崩溃了?"triggerBtn menuCollapsed" : "triggerBtn menuOpen"}style={{ 颜色:customizedTheme.textColor }}onClick={toggleCollapsed}/>

<ul className="isoRight">this.setState({ selectedItem: '通知' })}类名=isoNotify"><TopbarNotification locale={locale}/><li><活动租户/>this.setState({ selectedItem: "user" })}类名=isoUser"><TopbarUser/><div>{process.env.uiversion}</div></标题></TopbarWrapper>);}}导出默认连接(状态 =>({...state.App.toJS(),语言环境:state.LanguageSwitcher.toJS().language.locale,定制主题:state.ThemeSwitcher.toJS().topbarTheme}),{ 切换折叠 })(顶栏);

包含顶部栏的 App.js

import React, { Component } from "react";从react-redux"导入{连接};从antd"导入{布局,LocaleProvider};import { IntlProvider } from "react-intl";从反应节流阀"导入 { Debounce };从react-window-size-listener"导入WindowResizeListener;import { ThemeProvider } from "styled-components";从../../redux/auth/actions"导入authAction;从../../redux/app/actions"导入appActions;从../Sidebar/Sidebar"导入侧边栏;从../Topbar/Topbar"导入Topbar;从./AppRouter"导入 AppRouter;从../../settings"导入{siteConfig};从../../settings/themes"导入主题;从../../settings"导入{themeConfig};从./commonStyle"导入 AppHolder;导入./global.css";从../../dashApp"导入 { AppLocale };从../../containers/ThemeSwitcher"导入 ThemeSwitcher;const { 内容,页脚 } = 布局;const { 注销 } = authAction;const { toggleAll } = appActions;导出类 App 扩展组件 {使成为() {const { url } = this.props.match;const { locale, selectedTheme, height } = this.props;const currentAppLocale = AppLocale[locale];返回 (<LocaleProvider locale={currentAppLocale.antd}><国际供应商locale={currentAppLocale.locale}消息={currentAppLocale.messages}><ThemeProvider theme={themes[themeConfig.theme]}><AppHolder><布局样式={{ height: "100vh" }}><Debounce time="1000" handler="onResize"><WindowResizeListeneronResize={windowSize =>this.props.toggleAll(windowSize.windowWidth,窗口大小.窗口高度)}/></去抖动><顶栏 url={url}/><布局样式={{ flexDirection: "row", overflowX: "hidden" }}><侧边栏 url={url}/><布局className="isoContentMainLayout"风格={{高度:高度}}><内容类名=同构内容"风格={{填充:70px 0 0",flexShrink: "0",背景:#f1f3f6",职位:相对"}}><AppRouter url={url}/></内容><页脚风格={{背景:#ffffff",文本对齐:居中",borderTop: "1px solid #ededed"}}>{siteConfig.footerText}</页脚></布局></布局><主题切换器/></布局></AppHolder></ThemeProvider></IntlProvider></LocaleProvider>);}}导出默认连接(状态 =>({身份验证:状态.身份验证,语言环境:state.LanguageSwitcher.toJS().language.locale,selectedTheme: state.ThemeSwitcher.toJS().changeThemes.themeName,高度:state.App.toJS().height}),{ 注销,toggleAll })(应用程序);

我认为这应该足以说明我的问题.

解决方案

您在那里没有正确使用 redux.您需要将您的活动租户保留在您的 redux 状态中;该信息将成为您的唯一真实来源,并将在整个应用程序的组件之间共享.每个需要该信息的组件都将连接到状态的那​​部分,并且不需要为该信息保存内部状态.

Actions,您可以在其中调用 API 以获取活动租户信息或设置一个新租户作为活动.这些操作将调用 reducers 来改变您的 redux 状态(包括您的活动租户信息)和这些 redux 状态更改将更新您的应用组件.

您可能应该花一些时间阅读或重新阅读redux 文档,它简短且解释得很好!

I have an application with a table, the table has a checkbox to set a Tenant as Active, this variable is a global variable that affects what the user does in other screens of the application.

On the top right of the application, I have another component called ActiveTenant, which basically shows in which tenant the user is working at the moment.

The code of the table component is like this:

import React, { Component } from 'react';
import {  Table, Radio} from 'antd';
import { adalApiFetch } from '../../adalConfig';
import Notification from '../../components/notification';

class ListTenants extends Component {

    constructor(props) {
        super(props);
        this.state = {
            data: []
        };
    }



    fetchData = () => {
        adalApiFetch(fetch, "/Tenant", {})
          .then(response => response.json())
          .then(responseJson => {
            if (!this.isCancelled) {
                const results= responseJson.map(row => ({
                    key: row.id,
                    TestSiteCollectionUrl: row.TestSiteCollectionUrl,
                    TenantName: row.TenantName,
                    Email: row.Email
                  }))
              this.setState({ data: results });
            }
          })
          .catch(error => {
            console.error(error);
          });
      };


    componentDidMount(){
        this.fetchData();
    }

    render() {
        const columns = [
                {
                    title: 'TenantName',
                    dataIndex: 'TenantName',
                    key: 'TenantName',
                }, 
                {
                    title: 'TestSiteCollectionUrl',
                    dataIndex: 'TestSiteCollectionUrl',
                    key: 'TestSiteCollectionUrl',
                }, 
                {
                    title: 'Email',
                    dataIndex: 'Email',
                    key: 'Email',
                }
        ];

        // rowSelection object indicates the need for row selection
        const rowSelection = {
            onChange: (selectedRowKeys, selectedRows) => {
                if(selectedRows[0].TenantName != undefined){
                    console.log(selectedRows[0].TenantName);
                    const options = { 
                        method: 'post'
                    };

                    adalApiFetch(fetch, "/Tenant/SetTenantActive?TenantName="+selectedRows[0].TenantName.toString(), options)
                        .then(response =>{
                        if(response.status === 200){
                            Notification(
                                'success',
                                'Tenant set to active',
                                ''
                                );
                        }else{
                            throw "error";
                        }
                        })
                        .catch(error => {
                        Notification(
                            'error',
                            'Tenant not activated',
                            error
                            );
                        console.error(error);
                    });
                }
            },
            getCheckboxProps: record => ({
                type: Radio
            }),
        };

        return (
            <Table rowSelection={rowSelection} columns={columns} dataSource={this.state.data} />
        );
    }
}

export default ListTenants;

And the code of the ActiveTenant component its also very simple

import React, { Component } from 'react';
import authAction from '../../redux/auth/actions';
import { adalApiFetch } from '../../adalConfig';

class ActiveTenant extends Component {
  constructor(props) {
    super(props);
    this.state = {
      tenant: ''
    };
  }

  fetchData = () => {
    adalApiFetch(fetch, "/Tenant/GetActiveTenant", {})
      .then(response => response.json())
      .then(responseJson => {
        if (!this.isCancelled) {
          this.setState({ tenant: responseJson.TenantName });
        }
      })
      .catch(error => {
        this.setState({ tenant: '' });
        console.error(error);
      });
  };


componentDidMount(){
    this.fetchData();
}

  render() {
    return (
      <div>You are using tenant: {this.state.tenant }</div>
    );
  }
}
export default ActiveTenant;

The problem is, if I have multiple tenants on my database registered and I set them to active, the server side action occurs, and the state is changed, however on the top right its still showing the old tenant as active, UNTIL I press F5 to refresh the browser.

How can I achieve this?

For the sake of complete understandment of my code I will need to paste below other components:

  1. TopBar which contains the active tenant

    import React, { Component } from "react";
    import { connect } from "react-redux";import { Layout } from "antd";
    import appActions from "../../redux/app/actions";
    import TopbarUser from "./topbarUser";
    import TopbarWrapper from "./topbar.style";
    import ActiveTenant from "./activetenant";
    import TopbarNotification from './topbarNotification';
    
    
    const { Header } = Layout;
    const { toggleCollapsed } = appActions;
    
    class Topbar extends Component {
      render() {
        const { toggleCollapsed, url, customizedTheme, locale } = this.props;
        const collapsed = this.props.collapsed && !this.props.openDrawer;
        const styling = {
          background: customizedTheme.backgroundColor,
          position: 'fixed',
          width: '100%',
          height: 70
        };
        return (
          <TopbarWrapper>
            <Header
              style={styling}
              className={
                collapsed ? "isomorphicTopbar collapsed" : "isomorphicTopbar"
              }
            >
              <div className="isoLeft">
                <button
                  className={
                    collapsed ? "triggerBtn menuCollapsed" : "triggerBtn menuOpen"
                  }
                  style={{ color: customizedTheme.textColor }}
                  onClick={toggleCollapsed}
                />
              </div>
    
              <ul className="isoRight">
                <li
                  onClick={() => this.setState({ selectedItem: 'notification' })}
                  className="isoNotify"
                >
                  <TopbarNotification locale={locale} />
                </li>
                <li>
                  <ActiveTenant />
                </li>
                <li
                  onClick={() => this.setState({ selectedItem: "user" })}
                  className="isoUser"
                >
                  <TopbarUser />
                  <div>{ process.env.uiversion}</div>
                </li>
              </ul>
            </Header>
          </TopbarWrapper>
        );
      }
    }
    
    export default connect(
      state => ({
        ...state.App.toJS(),
        locale: state.LanguageSwitcher.toJS().language.locale,
        customizedTheme: state.ThemeSwitcher.toJS().topbarTheme
      }),
      { toggleCollapsed }
    )(Topbar);
    

App.js which contains the top bar

import React, { Component } from "react";
import { connect } from "react-redux";
import { Layout, LocaleProvider } from "antd";
import { IntlProvider } from "react-intl";
import { Debounce } from "react-throttle";
import WindowResizeListener from "react-window-size-listener";
import { ThemeProvider } from "styled-components";
import authAction from "../../redux/auth/actions";
import appActions from "../../redux/app/actions";
import Sidebar from "../Sidebar/Sidebar";
import Topbar from "../Topbar/Topbar";
import AppRouter from "./AppRouter";
import { siteConfig } from "../../settings";
import themes from "../../settings/themes";
import { themeConfig } from "../../settings";
import AppHolder from "./commonStyle";
import "./global.css";
import { AppLocale } from "../../dashApp";
import ThemeSwitcher from "../../containers/ThemeSwitcher";



const { Content, Footer } = Layout;
const { logout } = authAction;
const { toggleAll } = appActions;
export class App extends Component {
  render() {
    const { url } = this.props.match;
    const { locale, selectedTheme, height } = this.props;
    const currentAppLocale = AppLocale[locale];
    return (
      <LocaleProvider locale={currentAppLocale.antd}>
      <IntlProvider
        locale={currentAppLocale.locale}
        messages={currentAppLocale.messages}
      >
      <ThemeProvider theme={themes[themeConfig.theme]}>
        <AppHolder>
          <Layout style={{ height: "100vh" }}>
            <Debounce time="1000" handler="onResize">
              <WindowResizeListener
                onResize={windowSize =>
                  this.props.toggleAll(
                    windowSize.windowWidth,
                    windowSize.windowHeight
                  )
                }
              />
            </Debounce>
            <Topbar url={url} />
            <Layout style={{ flexDirection: "row", overflowX: "hidden" }}>
              <Sidebar url={url} />
              <Layout
                className="isoContentMainLayout"
                style={{
                  height: height
                }}
              >
                <Content
                  className="isomorphicContent"
                  style={{
                    padding: "70px 0 0",
                    flexShrink: "0",
                    background: "#f1f3f6",
                    position: "relative"
                  }}
                >
                  <AppRouter url={url} />
                </Content>
                <Footer
                  style={{
                    background: "#ffffff",
                    textAlign: "center",
                    borderTop: "1px solid #ededed"
                  }}
                >
                  {siteConfig.footerText}
                </Footer>
              </Layout>
            </Layout>
            <ThemeSwitcher />
          </Layout>
        </AppHolder>
      </ThemeProvider>
      </IntlProvider>
      </LocaleProvider>
    );
  }
}

export default connect(
  state => ({
    auth: state.Auth,
    locale: state.LanguageSwitcher.toJS().language.locale,
    selectedTheme: state.ThemeSwitcher.toJS().changeThemes.themeName,
    height: state.App.toJS().height
  }),
  { logout, toggleAll }
)(App);

I think this should be enough to illustrate my question.

解决方案

You're not using redux correctly there. You need to keep somewhere in your redux state your active tenant; That information will be your single source of truth and will be shared among your components throughout the app. Every component that will need that information will be connected to that part of the state and won't need to hold an internal state for that information.

Actions, are where you would call your API to get your active tenant information or set a new tenant as active. These actions wil call reducers that will change your redux state (That includes your active tenant information) and those redux state changes will update your app components.

You should probably take some time to read or re-read the redux doc, it's short and well explained !

这篇关于React:当另一个组件发生某些事情时如何更新一个组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆