N
N
Nikita Sokolov2021-03-31 15:36:13
React
Nikita Sokolov, 2021-03-31 15:36:13

How to force a component to re-render?

I'm seeing strange behavior. I have 3 essentially identical pages - functional components, but they have different routes and prop types. These pages have a TextField component from material ui whose value is bound to the redux state. When switching between these pages, the state is reset and the value of the TextField becomes an empty string. But since we essentially just change the type when moving to another page, this component does not re-render the TextField. As a result, we get an empty string in the redux state, and the past value in the TextField. Question: How can I get React to re-render this TextField? Now the solution is to simply add a layer of different html components in the router for each of these pages to force the tree to be re-rendered, but it looks very bad.

ps If no memoizations are used, at least in my code.

Component code:

import { useCallback, useEffect, useState } from "react"

//ftl api
import { FTLStatusTag, useLayout } from "ftl-dashboards-ui-kit"
import { useListPage, FTLObjectUtils } from "ftl-dashboards-core"
//other
import {
  statusLabels,
  statusItems,
  listTitleToType,
  getExternalServices,
} from "./model"
import { Cell } from "react-table"

//types
import { ExternalService, ExternalServiceRequest } from "../../types/entities"
import { FTLListPage } from "ftl-dashboards-templates"

const columns = [
  {
    Header: "Наименование",
    accessor: "name",
    width: 500,
    disableSortBy: true,
  },
  {
    Header: "Активность",
    accessor: "activeStatus",
    width: 180,
    disableSortBy: true,
    Cell: (props: Cell) => {
      const value = props.value
      return (
        <FTLStatusTag
          status={value === "ACTIVE" ? "success" : "error"}
          style={{
            display: "inline-block",
          }}
        >
          {statusLabels[value]}
        </FTLStatusTag>
      )
    },
  },
  {
    Header: "Создан",
    width: 150,
    accessor: "createdAt",
    align: "right",
    reverse: true,
  },
  {
    Header: "",
    width: 80,
    accessor: "action",
    disableSortBy: true,
  },
]

export const ExternalServicesList = ({
  type = "ORDER_SYNC",
}: Pick<ExternalService, "type">) => {
  const baseUrl = `/${window.location.pathname.split("/")[1]}${
    type === "SMS" ? "/params" : ""
  }`
  const [data, setData] = useState<ExternalService<"GET">[]>([])

  const {
    pageCount,
    isFetching,
    query,
    history,
    sortName,
    sortDirection,
    limit,
    offset,
    filters,
    path,
    setFilters,
    setPageCount,
    setQuery,
    setIsFetching,
  } = useListPage()

  const fetchExternalServices = useCallback(
    async (request: ExternalServiceRequest) => {
      getExternalServices(request, {
        setIsFetching,
        setData,
        setPageCount,
        type,
      })
    },
    [type]
  )

  useEffect(() => {
    setData([])
    setPageCount(0)
  }, [type])

  useEffect(() => {
    fetchExternalServices({
      query: filters.query,
      activeStatus: filters.activeStatus?.value,
      sortName,
      sortDirection,
      limit,
      offset,
      type,
    })
  }, [filters, sortName, sortDirection, limit, offset, type])

  const page = useLayout({
    components: {
      Header: {
        title: listTitleToType[type],
        primaryButton: {
          label: "Новый сервис",
          onClick: () => history.push(`${baseUrl}/new`),
        },
      },
      Table: {
        data,
        columns,
        pageCount,
        isFetching,
        setIsFetching,
        actionsSize: 2,
        isSearching: Boolean(FTLObjectUtils.getNotEmptyFieldsCount(filters)),
        pageSize: limit,
        rowDisable: (row) => {
          if (row.original.activeStatus === "ARCHIVE") return true
          return false
        },
        onRowClick: (row) => {
          row.original.id && history.push(`${baseUrl}/${row.original.id}`)
        },
      },
    },
  })

  return (
    <FTLListPage
      headerComponent={page.Header}
      tableComponent={page.Table}
      searchProps={{
        value: query,
        onChange: (value) => {
          setQuery(value)
        },
        placeholder: "Наименование",
      }}
      filters={[
        {
          isSearchable: false,
          placeholder: "Активность",
          options: statusItems,
          onChange: (option) => {
            setFilters({ activeStatus: option })
          },
          value: filters.activeStatus,
        },
      ]}
    />
  )
}

export default ExternalServicesList

Answer the question

In order to leave comments, you need to log in

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question