Why isn't React.useMemo(…) working in my React function?

五迷三道 提交于 2020-06-29 03:20:32

问题


I'm trying to implement a react-table using this guide:

https://github.com/tannerlinsley/react-table/blob/master/docs/quickstart.md

At one point in the guide, it says to create your data using React.useMemo:

const columns = React.useMemo(
  () => [
    {
      Header: 'Column 1',
      accessor: 'col1', // accessor is the "key" in the data
    },
    {
      Header: 'Column 2',
      accessor: 'col2',
    },
  ],
  []
)

When I do this, I copy and paste this line into my code (replacing the data with my own data):

class Blog extends Component {

...

createTable() {

    const cols = React.useMemo(
        // copy and paste here
    );

    // more table creation code

    return (
        // jsx for table
    );
}

...

}

But when I run this, it tells me:

Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app

So after googling this problem, I gathered that I need to call useMemo in a React funcion. So I created this:

import React from 'react';

const getCols = () => {
    return React.useMemo(() => [
        {
            Header: 'title',
            accessor: 'titleCol'
        },
        {
            Header: 'body',
            accessor: 'bodyCol'
        },
        {
            Header: 'last updated',
            accessor: 'updatedAtCol'
        }
    ], []);
};

export default getCols;

And in my Blog class:

class Blog extends Component {

...

createTable() {

    const cols = getCols();

    // more table creation code

    return (
        // jsx for table
    );
}

...

}

But now it tells me:

React Hook "React.useMemo" is called in function "getCols" which is neither a React function component or a custom React Hook function

Why isn't it a React function?

More importantly, what is the proper way to call useMemo(...)?

Thanks.


回答1:


The error messages aren't asking you to put the code in a "react function", they're asking you to put it into a "function component". Your code snippets are all class components (ie, they start with class XYZ extends Component), and hooks (including useMemo) do not work in class components.

Assuming you want to follow the steps in that tutorial, you'll need to write your code as function components, not class components.

const Blog = (props) => {
  const cols = React.useMemo(() => [
    {
      Header: "title",
      accessor: "titleCol",
    },
    {
      Header: "body",
      accessor: "bodyCol",
    },
    {
      Header: "last updated",
      accessor: "updatedAtCol",
    },
  ], []);

  // more table creation code

  return (
    // jsx for table
  );
};




回答2:


Why isn't it a React function?

It depends on the way you are using it. getCols() is just a simple function call returning jsx and React interprets it as so. You have to render it to get React to interpret it as a functional component

 const cols = <getCols />; // this would be a React functional component

More importantly, what is the proper way to call useMemo(...)?

First thing to understand is that there are two ways to define components in React - functional components or class components.

useMemo is a hook. Hooks are used to add stateful logic to functional components. Class components, already have that with their lifecycle methods (like componentDidMount, componentDidUpdate). So hooks can only be used inside a functional component or another custom hook, as mentioned in the two warnings you got:

Hooks can only be called inside of the body of a function component.

React Hook "React.useMemo" is called in function "getCols" which is neither a React function component or a custom React Hook function

useMemo is used to memoize the value of something, based on the dependencies. Means once you assign something using useMemo, the value/reference won't change till your dependencies update.

   // `cols` will have the same value - `[ { Header: 'title ... ]`
   // TILL the dependency array changes
   const cols = React.useMemo(() => [
        {
            Header: 'title',
            accessor: 'titleCol'
        },
        {
            Header: 'body',
            accessor: 'bodyCol'
        },
        {
            Header: 'last updated',
            accessor: 'updatedAtCol'
        }
    ], 
    [] // <-- this is the dependency array, 
    // since it's empty, `cols` will only be initialized once

    // if you had something like this instead
    // [numberOfCols] 
    // i.e a variable in the dependency array, 
    // `cols` would update everytime `numberOfCols` updated
   )

Since your component Blog is a class component, you won't be able to use hooks inside it directly.

There are couple of ways to do so:

Method 1: Make Blog a functional component

Convert the whole Blog component as a functional component. The docs that you've linked also seem to be using it this way

const Blog = () => {
    // now you can use `cols` somewhere in the body of your component
    const cols = React.useMemo(() => { 
      ...
    }, [])


    // this will return whatever you were returning from the 
    // `render` function in class component
    return (
      // jsx for table
    )
}

Method 2: Extract cols out into a functional component

Use it as part of another functional component

class Blog extends Component {


 createTable() {
    // `cols` will be a React component now, not an array
    // so probably not what you need,
    // unless you want to use cols within that function
    // and return the `jsx` to be rendered directly
    const cols = <getCols />;

    // more table creation code

    return (
        // jsx for table
    );
  }

}

Method 3: Use it as a custom hook

Unnecessary, but just to demonstrate a custom hook (you'll still need to convert Blog into a functional component)

For the docs

A custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks.

const useTableCols = () => {
   const cols = React.useMemo(() => {
     ...
   }, [])

   return cols
}


const Blog = () => {
   const cols = useTableCols()
   
   // do something with `cols`
}



回答3:


First make sure you've actually installed it.

npm install react-table

Second, make sure you are importing it:

import { useTable } from 'react-table'

Third, why not just look at the Examples provided(too much code here to copy/paste):

https://github.com/tannerlinsley/react-table/tree/master/examples/basic/src



来源:https://stackoverflow.com/questions/62462611/why-isnt-react-usememo-working-in-my-react-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!