最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - React hooks map is not a function - Stack Overflow

programmeradmin4浏览0评论

what I'm trying to do is simple. I have two buttons, one that says toggle on and one that says toggle off. When I click one of them, I want to set my state to true or false (depending on which button was clicked) and re render, using the newly set true false values. But for some reason, rows.map is not a function. Here is what I have so far:

function Calculator() {
    const [rows, setRows] = useState([

        {   
            rowVal: 0,
            editing: false,
            someVal: 20
        },
        {   
            rowVal: 1,
            editing: true,
            someVal: 20
        },

    ])
    return(
        <div className="container">
            <table className="table">
                <thead>
                    <tr>
                        <th className="text-center" scope="col">SomeVal</th>
                    </tr>
                </thead>
                <tbody>
                        {
                            const togOff = <button onClick={setRows({editing: false})}>Toggle Off</button>
                            const togOn = <button onClick={setRows({editing: true})}>Toggle On</button>
                            rows.map(val => {
                                if (val.editing === true){
                                    togOff
                                } else {
                                    togOn
                                }
                            })
                        }

                </tbody>
            </table>
        </div>
    )
}

If anyone can point me in the right direction, that would be great.

what I'm trying to do is simple. I have two buttons, one that says toggle on and one that says toggle off. When I click one of them, I want to set my state to true or false (depending on which button was clicked) and re render, using the newly set true false values. But for some reason, rows.map is not a function. Here is what I have so far:

function Calculator() {
    const [rows, setRows] = useState([

        {   
            rowVal: 0,
            editing: false,
            someVal: 20
        },
        {   
            rowVal: 1,
            editing: true,
            someVal: 20
        },

    ])
    return(
        <div className="container">
            <table className="table">
                <thead>
                    <tr>
                        <th className="text-center" scope="col">SomeVal</th>
                    </tr>
                </thead>
                <tbody>
                        {
                            const togOff = <button onClick={setRows({editing: false})}>Toggle Off</button>
                            const togOn = <button onClick={setRows({editing: true})}>Toggle On</button>
                            rows.map(val => {
                                if (val.editing === true){
                                    togOff
                                } else {
                                    togOn
                                }
                            })
                        }

                </tbody>
            </table>
        </div>
    )
}

If anyone can point me in the right direction, that would be great.

Share Improve this question asked Apr 25, 2019 at 14:06 Ext-Ext- 5413 gold badges9 silver badges16 bronze badges 1
  • rows.map(({ editing }) => editing ? togOff : togOn) saves you space. best to move your const declerations out of your JSX too. also your onClick methods are being immediately invoked. wrap your calls in a function onClick={() => setRows({editing: false})} – Francis Leigh Commented Apr 25, 2019 at 14:38
Add a ment  | 

6 Answers 6

Reset to default 2

Your code have several problems.

1. You can't declare a variable inside the return

You need to declare the variable before the return to use it inside { }

Wrong

{
    const togOff = <button onClick={setRows({editing: false})}>Toggle Off</button> // wrong
    const togOn = <button onClick={setRows({editing: true})}>Toggle On</button> //wrong
    rows.map(val => {
        if (val.editing === true){
            togOff
        } else {
            togOn
        }
    })
}

Right

...
const togOff = <button onClick={setRows({editing: false})}>Toggle Off</button> // outside the return
const togOn = <button onClick={setRows({editing: true})}>Toggle On</button> // outside the return
return (
    ...
)

2. You can't just call setRows({editing: true}), this will rerender the ponent infinitely

Wrong

    const togOff = <button onClick={setRows({editing: false})}>Toggle Off</button> // wrong
    const togOn = <button onClick={setRows({editing: true})}>Toggle On</button> //wrong

Right

    const togOff = <button onClick={ () => setRows({editing: false})}>Toggle Off</button> // Added arrow function
    const togOn = <button onClick={ () => setRows({editing: true})}>Toggle On</button> //Added arrow function

3. You need to return something in the .map function

Wrong

rows.map(val => {
    if (val.editing === true){
        togOff //wrong
    } else {
        togOn //wrong
    }
})

Right

rows.map(val => {
    if (val.editing === true){
        return togOff // use return 
    } else {
        return togOn // use return
    }
})

OBSERVATION

Doing all of this, you still have problems because you can't do setRows({editing: true}) because rows are an array, not an object like {editing: true}

Please clarify what you want to do in setRows so I can e up with the answer, but until this, your erros are partially solved.

I can't be 100% sure on what are the expected result after the error is gone, but here is a working demo on what I think is what you want.

What is happening here is that you are using setRows just like the setState method of a class ponent (which is fine for updating the rows), but it won't work for setting anything other than rows. As stated in the React documentation:

Unlike the setState method found in class ponents, useState does not automatically merge update objects. You can replicate this behavior by bining the function updater form with object spread syntax.

[...]

Another option is useReducer, which is more suited for managing state objects that contain multiple sub-values.

So, what you can do if you want two keys in your state is something like:

const [state, setState] = useState({
  rows: [

    {
      rowVal: 0,
      editing: false,
      someVal: 20
    },
    {
      rowVal: 1,
      editing: true,
      someVal: 20
    },

  ],
  editing: false,
});

And then

setState(prevState => {...prevState, editing: true|false})

You are doing multiple wrong things.

  1. onClick should be given a callback/function instead of function execution output
  2. You are overriding list/array value with an object

You need to update editing value on a specific index so a your toggle function will take index and updated value.

const toggle = (index, value) => {
   const newRows = [...rows];
   newRows[index].editing = !value;
   setRows(rows);
}


rows.map((row, index) => {
   if (row.editing === true){
      return <button onClick={() => toggle(index, row.editing)}>Toggle Off</button>
   } else {
     return <button onClick={() => toggle(index, row.editing)}>Toggle ON</button>
   }
})

As your toggle's aren't massively different you could use the editing flag to conditionally apply your setRows arguments and the Off/On text. This keeps you from using your const toggles... best to keep them out of your JSX anyway, keep your variable declarations above your JSX.

Also don't forget, if you need to pass arguments to callback Functions that aren't implicitly passed you will need to wrap your callback in a function otherwise your callback is invoked straight away.

return (
  <div>
    {rows.map(({ editing }) => (
      <button onClick={() => setRows({editing: !editing})}>Toggle {editing ? 'Off' : 'On'}</button>
    )}
  </div>
)

I believe that this is what you need, try change the useState to useReducer as shown bellow.

"The useReducer is usually preferable to useState when you have plex state logic that involves multiple sub-values. It also lets you optimize performance for ponents that trigger deep updates because you can pass dispatch down instead of callbacks."

function Calculator() {
    const [rows, setRows] = useReducer(
    (state, newState) => ([{ ...state, ...newState }]),
    [

        {   
            rowVal: 0,
            editing: false,
            someVal: 20
        },
        {   
            rowVal: 1,
            editing: true,
            someVal: 20
        },

    ]);
rows.length>0 && rows.map((val) => {
if (val.editing === true) {
  togOff;
} else {
 togOn;
}
});

Just check the length before going for the map. If it is more than 0 then go for it.

发布评论

评论列表(0)

  1. 暂无评论