0
function handleBlur(event) {
  if (event.target.value !== props.value) {
    // a hook function that ends up rerendering the whole playlist
  }
}

i'm experimenting with React hooks and i have a playlist that holds a bunch of components that have input textboxes. modifying an item in the list unfortunately seems to render the whole playlist component despite memoizing so I'm trying to move the save playlist function to onBlur instead of onChange. but when I'm on a textbox and I tab over, once the re-render happens I lose the tab focus of the textbox I'm on. Is there any way I can prevent this? Any tips on how to prevent rerendering the whole playlist when i want to modify just an object in a list would be nice as well

playerList.map((player, index) => (
  <DraftPlayer
     arrayPosition={index}
     key={index + (Math.random()).toString()}
     modifyPlayer={modifyPlayer}  //hook function
     player={player}
   />
 ))

const [playerList, setPlayerList] = React.useState(
    initializePlayerListArray([ { firstName: "", lastName: "" },{ firstName: "", lastName: "" },{ firstName: "", lastName: "" },{ firstName: "", lastName: "" },{ firstName: "", lastName: "" },etc ])
  );
user1189352
  • 3,628
  • 12
  • 50
  • 90
  • 1
    What does initializePlayerListArray look like? Not sure if there's enough code here to help deduce what's wrong. – Chris Ngo Jul 08 '19 at 06:19
  • an array of objects that look like { firstname: ..., lastname: ... }. i'm pretty sure it's the react re-rendering of the whole playlist component is losing the focus of the original textbox that had the onBlur – user1189352 Jul 08 '19 at 06:20
  • What does modifyPlayer do and when it is called? – Jitesh Manglani Jul 08 '19 at 06:21
  • 1
    Why are you defining your keys this way? `key={index + (Math.random()).toString()}` This is probably what is causing the issue, react may be unmounting and remounting the component. which would cause it to lose focus. You should try to keep keys constant as much as possible for rendering performance. `key={player.id}` – John Ruddell Jul 08 '19 at 06:21
  • @JohnRuddell I read an article here on stack overflow that if you set the key to the index, it makes the whole list re-render when you make a change. i did some testing and it turned out to be true. – user1189352 Jul 08 '19 at 06:22
  • @JohnRuddell https://stackoverflow.com/questions/35308928/how-to-avoid-re-rendering-the-whole-list-instead-of-adding-the-new-item-to-the-d – user1189352 Jul 08 '19 at 06:23
  • @JohnRuddell ah okay let me try. maybe random isn't good but a consistent key. you're probably right. i'll let you know in a few minutes – user1189352 Jul 08 '19 at 06:24
  • yea, your random number in there is worse than an array index. shouldn't do them like that :) – John Ruddell Jul 08 '19 at 06:24
  • Quote from the answer you mentioned. "If you give each item in the list a unique (and deterministic) key=uniqueValue prop, then React will preserve list items where the key has not changed, thus avoiding a re-render of the entire list." So theoretically, if you were to give each item a key using `Math.random()`, then it is highly unlikely those keys will be reused again in the next execution of your list-generating function, so the initial items would not be preserved. I'm very interested to hear about your results with using consistent keys. – Chris Ngo Jul 08 '19 at 06:33
  • @ChristopherNgo will let you guys know. give me a bit.. i'm trying uuid() but it keeps giving all of the items the same uuid lol. promise to get back tomorrow if not tonight as it's late here. i appreciate you both. – user1189352 Jul 08 '19 at 06:37
  • loool, I would honestly just give `key={index}`, but if you want to use `uuid()` you'll want to give your array of objects their own id key-value pair before doing your your list-generator function. Best of luck :) – Chris Ngo Jul 08 '19 at 06:45
  • 2
    @JohnRuddell so it fixed the Tab issue, you were correct. Please write an answer and i'll give you credit – user1189352 Jul 08 '19 at 06:46
  • 1
    @ChristopherNgo so I gave each object a random uuid and assigned that as the key but unfortunately when the onBlur activates it still renders the whole list. it fixed the tab problem though which was at least what i was looking for. – user1189352 Jul 08 '19 at 06:47
  • @user1189352 where are you integrating the `uuid()`, inside the `.map()`? – Chris Ngo Jul 08 '19 at 06:48
  • @ChristopherNgo i just have a simple for-loop that pushes a model-object to a new array and i assign each object i push a new uuid and use it to initialize the playerList – user1189352 Jul 08 '19 at 06:50
  • Makes sense to me, can you verify that the same id's are persisted after triggering the onBlur? Maybe try console.log(playerList) in useEffect() or call-back. – Chris Ngo Jul 08 '19 at 06:53
  • 1
    define `new uuid` :) – John Ruddell Jul 08 '19 at 06:56
  • @ChristopherNgo just checked and the ids definitely didn't change and they are all different uuids. I'm using npm whyDidYouRender and it's saying "props.modifyPlayer --> 'different functions with the same name.' even though I used React.useCallback and the only dependency is on playerList. i guess that's a whole other discussion unfortunately.. but if you have any ideas i'd love to hear it but i'm pretty sure that's the other reason the whole playlist is is rendering. Hooks are frustrating.. =T – user1189352 Jul 08 '19 at 06:56
  • 2
    Honestly, I'm not a huge fan of hooks. The whole functional movement feels off from the direction the es standards have been going. I keep seeing huge functions that are hundreds of lines long that are hooks :/ – John Ruddell Jul 08 '19 at 07:00
  • @JohnRuddell same... i never had this problem using the standard React and Redux but I feel forced to learn this.. – user1189352 Jul 08 '19 at 07:01
  • 2
    Hooks hurt my soul. Only thing I found remotely positive was destructuring props from redux. – Chris Ngo Jul 08 '19 at 07:05

1 Answers1

4

The issue you are experiencing is because you are setting a random key on every render cycle. React uses the key for rendering performance, however when that key is randomized its telling react a new item is being rendered, or that it should be. In turn the input is essentially unmounted then remounted in the DOM. This is why you end up losing focus. Instead you should try to keep the key as constant as possible (relevant to the item that is being rendered).

I don't know the player model you are working with, but so were on the same page I'm going to write a typescript interface for the Player :)

interface Player {
  id: number
  firstname: string
  lastname: string
  /* ... etc */
}

Then I would create your items this way

playerList.map((player, index) => (
  <DraftPlayer
     arrayPosition={index}
     key={player.id}
     modifyPlayer={modifyPlayer}  //hook function
     player={player}
   />
))
John Ruddell
  • 25,283
  • 6
  • 57
  • 86