diff --git a/docs/faq.md b/docs/faq.md index 2178dce737..cffbeca9da 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -2,6 +2,135 @@ title: FAQ --- +## How do I stop infinite rendering loops? + +If you are using React, there is a very common pitfall that can cause infinite rendering. If you fail to give your `columns`, `data`, or `state` a stable reference, React will enter an infinite loop of re-rendering upon any change to the table state. + +Why does this happen? Is this a bug in TanStack Table? **No**, it is not. *This is fundamentally how React works*, and properly managing your columns, data, and state will prevent this from happening. + +TanStack Table is designed to trigger a re-render whenever either the `data` or `columns` that are passed into the table change, or whenever any of the table's state changes. + +> Failing to give `columns` or `data` stable references can cause an infinite loop of re-renders. + +### Pitfall 1: Creating new columns or data on every render + +```js +export default function MyComponent() { + //😵 BAD: This will cause an infinite loop of re-renders because `columns` is redefined as a new array on every render! + const columns = [ + // ... + ]; + + //😵 BAD: This will cause an infinite loop of re-renders because `data` is redefined as a new array on every render! + const data = [ + // ... + ]; + + //❌ Columns and data are defined in the same scope as `useReactTable` without a stable reference, will cause infinite loop! + const table = useReactTable({ + columns, + data, + }); + + return ...
; +} +``` + +### Solution 1: Stable references with useMemo or useState + +In React, you can give a "stable" reference to variables by defining them outside/above the component, or by using `useMemo` or `useState`, or by using a 3rd party state management library (like Redux or React Query 😉) + +```js +//✅ OK: Define columns outside of the component +const columns = [ + // ... +]; + +//✅ OK: Define data outside of the component +const data = [ + // ... +]; + +// Usually it's more practical to define columns and data inside the component, so use `useMemo` or `useState` to give them stable references +export default function MyComponent() { + //✅ GOOD: This will not cause an infinite loop of re-renders because `columns` is a stable reference + const columns = useMemo(() => [ + // ... + ], []); + + //✅ GOOD: This will not cause an infinite loop of re-renders because `data` is a stable reference + const [data, setData] = useState(() => [ + // ... + ]); + + // Columns and data are defined in a stable reference, will not cause infinite loop! + const table = useReactTable({ + columns, + data, + }); + + return ...
; +} +``` + +### Pitfall 2: Mutating columns or data in place + +Even if you give your initial `columns` and `data` stable references, you can still run into infinite loops if you mutate them in place. This is a common pitfall that you may not notice that you are doing at first. Something as simple as an inline `data.filter()` can cause an infinite loop if you are not careful. + +```js +export default function MyComponent() { + //✅ GOOD + const columns = useMemo(() => [ + // ... + ], []); + + //✅ GOOD (React Query provides stable references to data automatically) + const { data, isLoading } = useQuery({ + //... + }); + + const table = useReactTable({ + columns, + //❌ BAD: This will cause an infinite loop of re-renders because `data` is mutated in place (destroys stable reference) + data: data?.filter(d => d.isActive) ?? [], + }); + + return ...
; +} +``` + +### Solution 2: Memoize your data transformations + +To prevent infinite loops, you should always memoize your data transformations. This can be done with `useMemo` or similar. + +```js +export default function MyComponent() { + //✅ GOOD + const columns = useMemo(() => [ + // ... + ], []); + + //✅ GOOD + const { data, isLoading } = useQuery({ + //... + }); + + //✅ GOOD: This will not cause an infinite loop of re-renders because `filteredData` is memoized + const filteredData = useMemo(() => data?.filter(d => d.isActive) ?? [], [data]); + + const table = useReactTable({ + columns, + data: filteredData, // stable reference! + }); + + return ...
; +} +``` + +### The Ultimate Solution: React Forget + +When React Forget is released, these problems might be a thing of the past. Or use Solid.js... 🤓 + ## How do I stop my table state from automatically resetting when my data changes? Most plugins use state that _should_ normally reset when the data sources changes, but sometimes you need to suppress that from happening if you are filtering your data externally, or immutably editing your data while looking at it, or simply doing anything external with your data that you don't want to trigger a piece of table state to reset automatically. diff --git a/docs/installation.md b/docs/installation.md index c8e127eba7..20026a2630 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -41,3 +41,5 @@ npm install @tanstack/vue-table ```bash npm install @tanstack/table-core ``` + +Don't see your favorite framework (or favorite version of your framework) listed? You can always just use the `@tanstack/table-core` package and build your own adapter in your own codebase. Usually, only a thin wrapper is needed to manage state and rendering for your specific framework. Browse the [source code](https://github.com/TanStack/table/tree/main/packages) of all of the other adapters to see how they work. \ No newline at end of file