0

I am using use reducer inside a ReactContext to handle some signatures display. On Messages and messagesForm components the dispatch function works fine. But inside prevMessages it doesn't. I tried console log (json) but it is also not working inside the handleDeleteMes() function. I have tried everything I could think of for solving this problem.

Here I provide my code:

This is the parent component -Messages.jsx:

function Messages() {
  const {dispatch, signing, signatures} = useSignatureContext();
  const { user } = useUserContext()

  const thisPalRef = useRef([]);

  useEffect(()=>{

    const fetchSignatures = async()=>{
        const response = await fetch('/api/signatures/sent', {
            method: 'GET',
            headers: {
                'Authorization': `Bearer ${user.token}`
            }
        })
    
        const json = await response.json();

        if (response.ok) {
            json.forEach((mes)=>{
                mes.recipient_id === signing._id && thisPalRef.current.push(mes)
            })
        }
        dispatch({type:'SET_SIGNATURES', payload: thisPalRef.current}) //It works fine
    }
        fetchSignatures()

  },[signing._id, user.token, dispatch])


  return (
    <motion.div 
      initial= {{opacity: 0, height:'10%',width:'85%', x:0}}
      animate= {{opacity: 1, height:'100%', width:'95%', x:0}}
      exit= {{opacity:0, height:'10%',width:'85%', x:0}}
      transition={{ duration: 0.2 }}>
      <div className="d-flex flex-column align-items-start p-1 pt-4">
          <MessageForm />
          <div className='prevMsg_container d-flex flex-column align-items-end p-3 mb-5'>
              <header className='my-5 align-self-center'>
                  <h2>Previous Messages</h2>
                  <hr />
              </header>
              {
                signatures ? signatures.map(mes => {
                  console.log(signatures)
                  return <PrevMessages key={mes._id} mes={mes}/>
                }) : <small className="text-muted">No messages sent</small>
              }
             
          </div>
      </div>
    </motion.div>
  )
}

one child component -PrevMessages.jsx

function PrevMessages({mes}) {

    const {dispatch} = useSignatureContext();
    const {user} = useUserContext();

    const handleDeleteMes = async() => {
        if (!user) {
            return
        }

        const response = await fetch('/api/signatures/' + mes._id, {
            method:'DELETE',
            headers: {
                'Authorization': `Bearer ${user.token}`
            }
        })

        const json = await response.json();

        console.log(json) //it doesn't work

        if (response.ok) {
            dispatch({type: 'DELETE_SIGNATURE', payload: json}) //it doesnt work
        }
    }


    return  <figure className="text-end">
                <blockquote className="blockquote">
                    <p className='prevMsg_text mt-3 text-end'>{mes.message}</p>
                </blockquote>
                <figcaption className="blockquote-footer text-end">
                    <cite title="Sent date">{formatRelative (new Date(mes.updatedAt), new Date())}</cite>
                </figcaption>
                <IconContext.Provider value={{ color: '#ea1537', className: "delete_icon" }}>
                    <span className="text-end delete_msg_btn" onClick={() => handleDeleteMes(mes._id)}><RiChatDeleteFill/></span>
                </IconContext.Provider>
            </figure>
    

}

and another child component -MessageForm.jsx

function MessageForm() {

    const {signing, dispatch} = useSignatureContext();
    const { user } = useUserContext()
  
    const [myMessage, setMyMessage] = useState('');

    const [error, setError] = useState(null);
    const [emptyFields, setEmptyFields] = useState([]);
  
    const handleSubmitMessage = async (e) => {
      e.preventDefault()
  
      if (!user) {
        setError('You must be logged in')
        return
      }
      
      const mssg = {
        message: myMessage,
        recipient: `${signing.name} ${signing.last_name}`,
        recipient_id: signing._id,
        sender: `${user.name} ${user.last_name}`,
        sender_id: user._id,
        sender_signature: user.signature
      }
  
      const response = await fetch('/api/signatures', {
        method: 'POST',
        body: JSON.stringify(mssg),
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${user.token}`
        }
      })
      
      const json = await response.json()
  
      if(!response.ok){
        setError(json.error)
        setEmptyFields(json.emptyFields)
      }
      if (response.ok) {
        setMyMessage('');
        setError(null);
        setEmptyFields([]);
        console.log('new message sent!', json);
        dispatch({type: 'CREATE_SIGNATURE', payload: json}); //works fine
      }
    }
  return (
    <>
        <h1 className='align-self-center bk_owner_title'>{signing.name}<small className="text-muted">'s Book</small></h1>
        <div className="input-group">
            <textarea value={myMessage} className={"form-control txtArea pt-4 ps-2 msg_txt_area " + (emptyFields.includes('message') ? ' message_error' : '')} autoFocus onChange={(e)=>setMyMessage(e.target.value)} placeholder={'Dear ' + signing.name + ' ' + signing.last_name + '...'}></textarea>
        </div>
        <figcaption className='message_footer mx-3 mt-1'>From: {user.signature}</figcaption>
        <button type='submit' className='btn btn-success align-self-end' onClick={(e)=>handleSubmitMessage(e)}>Send</button>
        {error && <Alert variant='danger' className='mt-3 align-self-center alert_message'>{error}</Alert>}
    </>
  )
}

And this is the context -SignatureContext.jsx

const SignatureContext = createContext();


const signaturesReducer = (mesState, action) => {
    switch (action.type) {
        case 'SET_SIGNATURES':
            return {
                signatures: action.payload
            }
        case 'CREATE_SIGNATURE':
            return {
                signatures: [action.payload, ...mesState.signatures]
            }
        case 'DELETE_SIGNATURE':
            return {
                signatures: mesState.signatures.filter((s) => s._id !== action.payload._id)
            }
        default:
            return mesState
    }
}


const SignatureContextProvider = ({children})=> {

    const [signing, setSigning] = useState({});
    const [mesState, dispatch] = useReducer(signaturesReducer, {
        signatures: null
    });

    return (
        <SignatureContext.Provider value={{signing, setSigning, ...mesState, dispatch}}>
            {children}
        </SignatureContext.Provider>
        )
}

export {SignatureContextProvider, SignatureContext, signaturesReducer};

Thank you in advance, any ideas will be appreciated.

E.K.Garcia
  • 49
  • 2
  • 7
  • I guess the response is not json ... try `console.log(await response.text())` – KcH Sep 29 '22 at 17:24
  • I just did, but it doesn't work either. I think the problem is the dispatch function on that component. Because the database gets updated, when I reload the page it shows that it's been deleted, but that is supposed to happen without rerendering. – E.K.Garcia Sep 30 '22 at 13:28

1 Answers1

0

Found the problem in the backend. The controller function wasn't returning JSON response so the frontend function had nothing to work with. For that reason it was stopping after the fetch, I'm using Express and Mongoose.

This was my controller function before:

const deleteSignature = async (req, res)=>{
    const {id} = req.params

    if(!mongoose.Types.ObjectId.isValid(id)) {
        return res.status(404).json({error: 'No such signature'})
    }

    const signature = await Signature.findOneAndDelete({_id: id})

    if(!signature) {
        return res.status(404).json({error: 'No such signature'})
    }

    //nothing returning on success
}

and this is how I fixed it

const deleteSignature = async (req, res)=>{
    const {id} = req.params

    if(!mongoose.Types.ObjectId.isValid(id)) {
        return res.status(404).json({error: 'No such signature'})
    }

    const signature = await Signature.findOneAndDelete({_id: id})

    if(!signature) {
        return res.status(404).json({error: 'No such signature'})
    }

    res.status(200).json(signature) //returning json on success
}

It works perfectly.

E.K.Garcia
  • 49
  • 2
  • 7