Hello friendly people of stackoverflow, I'm having a map function issue and I just can't put my finger on what the problem could be. Context is just for user data and its not needed to replicate the issue, context/state can be removed or changed so I'm not so sure it's even a state issue. Strict mode changes do nothing. Id/key changes aren't helping. I've tried every iteration from good to bad of map key/id practices (using the best answers on the website for map functions duplicating) and it's always the same issue, it even happens when no errors are thrown like with the code example below (using index) and when errors/warnings for duplicate id's throw (by giving bad keys/id) it still happens - it always adds two more posts to the feed on refresh. Maybe I'm misunderstanding or missing something about the map function or even Next/react (this is using NextJS) so I thought I'd reach out. I just need to figure out why its duplicating but I can't for the life of me. It works as it should until I refresh. If I refresh again, it just displays the same post(s) with the same two duplicates that were added to the feed on the first refresh. Can anyone explain to me why this is happening?

import Gun from "gun/gun";
import { useReducer, useState, useEffect, useContext } from "react";
import {
  Box,
  Center,
  Container,
  VStack,
  Avatar,
  useColorModeValue,
  Textarea,
  Input
} from '@chakra-ui/react';
import { UserContext } from "../../../lib/context";

// initialize gun locally
const gun = Gun({
  peers: [
    'http://localhost:4000/gun'
  ]
})

// create the initial state to hold the messages
const initialState = {
  messages: []
}



// Create a reducer that will update the messages array
function reducer(state, message) {
  return {
    messages: [message, ...state.messages]
  }
}

export default function Feed() {
  // the form state manages the form input for creating a new message
    const [formState, setForm] = useState({
    name: '', message: ''
  })

  const [user] = useContext(UserContext);

  // initialize the reducer & state for holding the messages array
  const [state, dispatch] = useReducer(reducer, initialState)
  

  // when the app loads, fetch the current messages and load them into the state
  // this also subscribes to new data as it changes and updates the local state
  useEffect(() => {
    const messages = gun.get('messages')
    messages.map().on(m => {
      dispatch({
        userid: m.user?.issuer,
        name: m.name,
        message: m.message,
        createdAt: m.createdAt
      })
    })
  }, [])

  // set a new message in gun, update the local state to reset the form field
  function saveMessage() {
    const messages = gun.get('messages')
    messages.set({
      userid: user?.issuer,
      name: formState.name,
      message: formState.message,
      createdAt: Date.now()
    })
    setForm({
      name: '', message: ''
    })
  }

  // update the form state as the user types
  function onChange(e) {
    setForm({ ...formState, [e.target.name]: e.target.value  })
  }

  return (
    <div style={{ padding: 30 }}>
      <input
        onChange={onChange}
        placeholder="Name"
        name="name"
        value={formState.name}
      />
      <input
        onChange={onChange}
        placeholder="Message"
        name="message"
        value={formState.message}
      />
      <button onClick={saveMessage}>Send Message</button>
      {
        state.messages.map((message, index) => (
          
          <div key={index}>
            <h4>{message.userid}</h4>
            <h2>{message.message}</h2>
            <h3>From: {message.name}</h3>
            <p>Date: {message.createdAt}</p>
          </div>
        ))
      }
    </div>
  );
}

Thanks in advance for anyone who takes the time to explain this issue. I can add more pictures if needed but I'm probably going to start working on a solution without reducers and maps and then compare for now. Will update when that's done if no replies!

link to duplicate example without errors

1

There are 1 answers

1
AudioBubble On

pretty sure TJ pointed me in the right direction

it looks solved by changing .on back to .once. not sure why I changed that in the first place. spent 6 hours messing with map thinking that was the issue lol. mfw :O