Home > OS >  React not re-rendering on change
React not re-rendering on change

Time:06-10

So I've been beating my head against a wall for a couple days now scouring through Google and questions on here. I've done quite a bit of changes to the code I've written and re-written. The problem I'm getting is that the POST request to the DB works fine. If I refresh the page, the new entry is there. Somewhere in my code, I know I'm not properly updating the state. From what I've gathered, an issue like mine happens when you're not passing a new array into state in order for a re-render to trigger.

At this point I'm lost, and any insight on to what I'm doing wrong would be appreciated.

Here's the code:

export default function Form () {

const [chatData, setChatData] = useState([{
    chat: Number,
    isConverted: Boolean,
}])

console.log(chatData)

const changeHandler = name => (e) => {
    setChatData({...chatData, [name]: e.target.value})
}

const createChat = (e) =>  {
    e.preventDefault()

    const data = { ...chatData }

    const postData = () => {
        try {
            axios.post("/api/create", data)
        } catch (error) {
            console.error(error)
        }
    }
    postData()
}

return (
    <div>
        <Box sx={{maxWidth: 200}} display="flex" alignItems="center" justifyContent="center" margin="auto">
            <form onSubmit={createChat}> 
                    <FormControl fullWidth sx={{ m: 1, minWidth: 120 }}>
                        <InputLabel id="chat_number_label">Chat Number</InputLabel>
                        <Select
                        labelId="chat_number_label"
                        id="chat_input_select"
                        defaultValue=""
                        value={chatData.chat}
                        onChange={changeHandler("chat")}
                        name="chat">
                            <MenuItem value={1}>1</MenuItem>
                            <MenuItem value={2}>2</MenuItem>
                            <MenuItem value={3}>3</MenuItem>
                            <MenuItem value={4}>4</MenuItem>
                            <MenuItem value={5}>5</MenuItem>
                            <MenuItem value={6}>6</MenuItem>
                            <MenuItem value={7}>7</MenuItem>
                            <MenuItem value={8}>8</MenuItem>
                            <MenuItem value={9}>9</MenuItem>
                            <MenuItem value={10}>10</MenuItem>
                        </Select>
                        </FormControl>
                        <FormControl fullWidth sx={{ m: 1, minWidth: 120 }}>
                        <InputLabel id="is_converted_label">Converted to Ticket</InputLabel>
                        <Select
                        labelId="is_converted_label"
                        id="is_converted_select"
                        defaultValue=""
                        value={chatData.isConverted}
                        onChange={changeHandler("isConverted")}
                        name="isConverted">
                            <MenuItem value={true}>True</MenuItem>
                            <MenuItem value={false}>False</MenuItem>
                        </Select>
                    </FormControl>
                <Button color="primary" variant="contained" type="submit" value="Submit">Submit</Button>
            </form>
        </Box>
    </div>
)
}

EDIT: Per request, this is where all my chats are displayed on the page

export default function AllChats () {

const [chatList, setChatList] = useState([])

useEffect(() => {
    const fetchAllChats = async () => {
        try {
            const res = await axios.get("/api")
            setChatList(res.data)
        } catch (error) {
            console.error(error)
        }
    }
    fetchAllChats()
}, [])

const headers = [
    {
        label: "Chat Number", key: "chat"
    },
    {
        label: "Date", key: "createdAt"
    },
    {
        label: "Converted into Ticket", key:"isConverted"
    }
]

const csvLink = {
    filename: "chat_data.csv",
    headers: headers,
    data: chatList
}

return (
    <Container  maxWidth="md">
        <TableContainer component={Paper}>
            <Table sx={{ minWidth: 650 }}>
                <TableHead>
                    <TableRow>
                        <TableCell>Chats Number</TableCell>
                        <TableCell align="right">Date</TableCell>
                        <TableCell align="right">Converted into ticket</TableCell>
                    </TableRow>
                </TableHead>
                <TableBody>
                { chatList.map((val, _key) => {
                    return (
                    <TableRow key={val.chat}
                                sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
                        >
                            <TableCell omponent="th" scope="row">{val.chat}</TableCell>
                            <TableCell align="right">{val.createdAt}</TableCell>
                            <TableCell align="right"> {String(val.isConverted)} </TableCell>
                    </TableRow>
                )})}
                </TableBody>
            </Table>
        </TableContainer>
        <CSVLink {...csvLink}>Export to CSV</CSVLink>
    </Container>
)
}

Here's my App.js

    function App() {
  return (
    <>
    <AllChats />
    <Form />
    </>
  );
}

export default App;

CodePudding user response:

You are initiating chatData State with an OBJECT, but with-in an array like this:

const [chatData, setChatData] = useState([{
    chat: Number,
    isConverted: Boolean,
}])

So when you are retrieving the chatData, it is returning the array, not the object.

You need to remove the array while initiating it, and directly initiate it with an OBJECT like this:

const [chatData, setChatData] = useState({
    chat: Number,
    isConverted: Boolean,
})

It will solve the problem.

And if don't, then you can contact me through my profile.

CodePudding user response:

setChatList is only called on first render in fetchAllChats. Your chatList is never being updated after the post request in createChat. You are updating your database but your frontend has no knowledge that your chats were updated. You need to update your chatList with setChatList upon a successful post. For instance:

axios.post("/api/create", data).then(response => {
    setChatList([...chatList, response.data]);
})

For the above to work, your backend should return the chat in the response in the same format as in the get request in fetchAllChats(). Or the response could contain the entire updated chat list. If you don't have control over the backend, you could run fetchAllChats() after a successful POST, but this might not be ideal as it is additional load in terms of network and database operations.

In either case, you would need access to functions from <AllChats /> like setChatList in your <Form /> component, so you should lift state up to a common ancestor (<App /> in this case)

  • Related