javascript - react : Table with backend elements does not re-render when clicking a NavLink in the NavBar

标签 javascript reactjs react-router react-router-dom

我正在为<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="b9cbdcd8dacdf98881978b9789" rel="noreferrer noopener nofollow">[email protected]</a>中的 vert 应用程序做CRUDL , <a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="4331262220376e312c3637263103756d7b6d72" rel="noreferrer noopener nofollow">[email protected]</a> 。当我第一次单击 navBar 元素(即宠物)时,它会正确显示带有后端元素的表,这些元素是每个对象(单个宠物/所有者数据)的单独 JSON 文件,由文件夹上的实体(即 . ./data/pets/jsdnsj12.json),但是,当我单击导航栏上的另一个元素(即所有者)时,表格不会使用正确的后端元素重新渲染/更新,直到刷新页面。

注:Page是可重用的函数,根据实体(即宠物、所有者)呈现元素(表格、模式、操作菜单)。

/pets 路径呈现一个包含已注册宠物信息的表格。该表的列是宠物名称、宠物主人和宠物类型(狗、猫等)。另一方面,/owners 路径显示所有者的名字/姓氏及其身份证件(驾驶执照、出生证明等)。最初单击时,每个实体都会正确显示表中的数据,但如果您尝试更改为其他路径(即从/pets 到/owners,或反之亦然),表中的第一个实体数据将保持不变,不会更新到相应的新路径/实体(除非刷新页面)。

这是 App.js。注:<RootLayout/>集成了NavigationBarOutlet组件来自react-router-dom

const router = createBrowserRouter(
  createRoutesFromElements(
    <Route path="/" element={<RootLayout />}>
      <Route
        key={"/pets"}
        path="pets"
        element={<Page title="Pets" entity="pets" />}
      />
      <Route
        key={"/owners"}
        path="owners"
        element={<Page title="Owners" entity="owners" />}
      />
    </Route>
  )
)

function App() {
  return (
    <RouterProvider router={router} />
  );
}

Page.js

class Page extends Component {
  constructor(props){
    super(props);
    this.state = {
      showModal: false,
      entities: [],
      object: {},
      objectId: null,
      method: "POST",
      columns: [],
      options: initialOptions,
      search: '',
      vet: "",
      pet: "",
    };
  }

  /*
   * showEntity is supposed to re render the table as well, but
   * it only renders the actions menu
   */
  showEntity = async (_event = null) => {
    if (_event) {
      _event.preventDefault();
    }
    const { entity } = this.props;
    const { search, columns, vet, pet } = this.state;
    const entities = await listEntity({ entity, search, columns, vet, pet });
    let _columns = [];
    if (Array.isArray(entities) && entities.length > 0) {
      _columns = Object.keys(entities[0]).filter((col) => col !=="id") || [];
    }
    this.setState({ entities, columns: _columns });
  };

  componentDidMount() {
    const { entity } = this.props;
    if (entity === "owners") {
      this.getBackendOptions({});
      return;
    }
    this.showEntity();
  };

  /* 
   * Now, in render, the <ActionsMenu/> component does re render, 
   * which is expected, but the <Table/> component does not
   */

  render() {
    const { title = "unnamed", entity } = this.props;
    const {columns, objectId, entities, object, options} = this.state;
    return (
      <> 
        <ActionsMenu 
          changeModal={ this.changeModal } 
          title={title} 
          handleSearchInput={this.handleSearchInput}
          search={this.showEntity}
          entity={entity}   
          options={options} 
        />

        <Table 
          entities= {entities} 
          editEntity={this.editEntity}
          deleteEntity={this.deleteEntity}
          columns={columns}
        />

Table.js:表头。 r和table row分别接收index和state的数据

function Table({
  entities = [], 
  editEntity = () => {}, 
  deleteEntity = () => {}, 
  columns = [],   
}) {
  return (
    <table className="table table-stripped table-hover">
      <TableHeader columns={columns} />
      <tbody id="table-list">
        {entities.map((entity, index) => (
          <TableRow
            key={`row-${index}`} 
            index={entity.id ? entity.id : false} 
            entity={entity} 
            editEntity={editEntity} 
            deleteEntity={deleteEntity}
            columns={columns}
          />
        ))}
      </tbody>
    </table>
  );
}

NavigationBar.js

function NavigationBar() {
  return (
    <>
      <input type="checkbox" id="check" />
      <nav className="navbar">
        <div className="icon"><b>P</b>ethical</div>
        <ul>
          <li className="nav-item">
            <NavLink className="nav-link" to={"/"} end>
              Home
            </NavLink>
          </li>
          <li className="nav-item active">
            <NavLink className="nav-link" to={'/pets'} end>
              Pets
            </NavLink>
          </li>
          <li className="nav-item">
            <NavLink className="nav-link" to={'/owners'} end>
              Owners
            </NavLink>
          </li>
        </ul>
        <label htmlFor="check" className="bar">
          <span className="faBars" id="bars"><FaBars/></span>
          <span className="faTimes" id="times"><FaTimes/></span>
        </label>
      </nav>
    </>
  );
}

请注意,this.getBackendOptions 正在引用一个异步函数,该函数显示宠物 - 所有者之间关系的整个对象(宠物需要有一个所有者,因此 getBackendOptions 会带来整个所有者 JSON 文件并将其显示在宠物表中的“所有者”列)。

getBackendOptions = async (newState) => {
        const {options} = this.state;
        const petsPromise = listEntity({ entity: "pets" });
        const ownersPromise = listEntity({ entity: "owners" });
        let [pet = [], owner = [] = await Promise.all([
            petsPromise,
            ownersPromise,
        ]);

        pet = pet.map((_pet, index) => ({
            value: _pet.id,
            tag: `${_pet.petName} (${_pet.petType})`,
        }));
        const newOptions = {...options, pet, owner};
          this.setState({ ...newState, options: newOptions }, () => {
            this.showEntity();
        });
    };

提前致谢。

最佳答案

我不太明白完整代码的总体作用,但我高度怀疑问题是当同一组件在多个路由上呈现时,路由内容优化 react-router 所做的事情通过保持其安装并简单地使用更新的路由上下文值(即任何路由路径参数、当前location对象等)重新渲染它。

一个简单的修复方法是为每个路由组件分配一个 React 键。 看起来这可能就是您尝试使用 Route 组件上的键执行的操作。将键移至 Page 组件,该组件在 "/pets""/owners" 路由之间导航时实际保持安装状态。

const router = createBrowserRouter(
  createRoutesFromElements(
    <Route path="/" element={<RootLayout />}>
      <Route
        path="pets"
        element={<Page key="pets" title="Pets" entity="pets" />}
      />
      <Route
        path="owners"
        element={<Page key="owners" title="Owners" entity="owners" />}
      />
    </Route>
  )
);

有些人可能会认为这有点“hackish”,并且它会为 React 创建(稍微)更多工作,因为它现在将拆除(即卸载)的前一个“实例” >Page 组件并安装一个新的“实例”。

更正确的版本/实现是处理组件生命周期中的路由路径更改,例如实现 componentDidUpdate 生命周期方法来运行与 componentDidMount 完全相同的逻辑,但仅当路径更改时。在这里使用 entity 属性应该足够了,因为它与路径同时更改。

示例:

class Page extends Component {
  constructor(props){
    ....
  }

  showEntity = async (_event = null) => {
    ....
  };

  setData = () => {
    const { entity } = this.props;
    if (entity === "owners") {
      this.getBackendOptions({});
      return;
    }
    this.showEntity();
  }

  componentDidMount() {
    this.setData();
  };

  componentDidUpdate(prevProps) {
    if (prevProps.entity !== this.props.entity) {
      this.setData();
    }
  }

  ....

关于javascript - react : Table with backend elements does not re-render when clicking a NavLink in the NavBar,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75739926/

相关文章:

javascript - 为什么 'stay on page'对话框暂停异步函数?

node.js - 如何将上下文传递给 React/jsx View ?

reactjs - 如何修复使用 create-react-app 构建后的白屏?

reactjs - 为什么 "Fetch as Google"返回 Not found from my React app on GCP Storage

javascript - 默认选择一行并使用 jQuery DataTables 发送 XHR 请求

javascript - AJax 删除在某些浏览器中不起作用

javascript - 与 RxJS 流冲突的条件语句

javascript - 匹配时提取字符串

javascript - Tailwind V3 导致 TypeError : Cannot read property '500' of undefined

javascript - 使用 Formik 构建向导时不能在子组件中使用 Prop