React-bootstrap table axios 调用后未刷新

React-bootstrap table not refreshed after axios call

我有一个 table 可以在页面加载时完美运行,该页面还包含一个模态组件来更新 table 的一行,我正在使用自定义挂钩来制作 API 调用并且效果很好。

问题是修改了一行后,没找到刷新table的方法。 DB 得到更新,Toast 显示更新成功,对新列表的调用被触发并在 DBHook 中使用 console.log('Result', result) 获得新列表但是,具有加热依赖性的 useEffect 从不被解雇。

如果我转到另一个页面然后返回或按 F5,列表会正确刷新。

dbHook :

export const useAxios = (url, method, data) => {
    const [responseData, setResponseData] = useState(undefined)
    const [status, setStatus] = useState(undefined)
    const [error, setError] = useState('')
    const [loading, setLoading] = useState(true)

    const params = {
        method: method,
        url: url,
        headers: { accept: '*/*' },
        data: data,
    }

    const fetchData = async (params) => {
        //console.log('in axios', method, url, data)
        try {
            const result = await axios.request(params)
            //console.log('Result', result)
            setResponseData(result.data)
            setStatus(result.status)
            //console.log('resultStatus', result.status)
        } catch (error) {
            setError(error.response)
            //console.log('Error', error)
        } finally {
            setLoading(false)
            //console.log('axios finished', url)
        }
    }

    useEffect(() => {
        fetchData(params)
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [url])
    return { responseData, error, loading, status }
}

模态:

export default function HeatModal({
    isShow,
    triggerModal,
    activeHeat,
    setActiveHeat,
    saveHeat,
}) {
    function save() {
        saveHeat()
        triggerModal()
    }

    return (
        <>
            <Modal show={isShow} onHide={triggerModal}>
                <Modal.Header>
                    <Modal.Title>{activeHeat.name}</Modal.Title>
                </Modal.Header>
                <Modal.Body>
                    <Form>
                        <FormGroup as={Row}>
                            <FormLabel column>
                                Nouvelle date de chaleur
                            </FormLabel>
                            <Col>
                                <MyDatePicker
                                    field={'date'}
                                    value={activeHeat.date}
                                    setter={setActiveHeat}
                                />
                            </Col>
                        </FormGroup>
                    </Form>
                </Modal.Body>
                <Modal.Footer as={NavContainer}>
                    <Button variant="outline-success" onClick={triggerModal}>
                        Annuler
                    </Button>

                    <Button variant="success" onClick={save}>
                        Confirmer
                    </Button>
                </Modal.Footer>
            </Modal>
        </>
    )
}

列表:

const HeatList = () => {
const [heatsUrl, setHeatsUrl] = useState('/heats')
    const heat = {
        idHeat: -1,
        name: '',
        date: new Date(),
        idDog: -1,
    }
    const [heatsWithName, setHeatsWithNames] = useState([])
    const [heatPerPage, setHeatPerPage] = useState(10)
    const [firstHeat, setFirstHeat] = useState(0)
    const [showModal, setShowModal] = useState(false)
    const [activeHeat, setActiveHeat] = useState(heat)
    const [editUrl, setEditUrl] = useState('')
    const {
        responseData: heats,
        loading: heatsIsLoading,
        error: heatsIsError,
    } = useAxios(heatsUrl, 'GET', null)
    const { responseData: dogs, loading: dogsIsLoading } = useAxios(
        '/dogs',
        'GET',
        null
    )
    const { responseData: editResponse, loading: editLoading } = useAxios(
        editUrl,
        'PUT',
        activeHeat
    )
    useEffect(() => {
        console.log('activeHeat', activeHeat.idHeat)
        editResponse && console.log('editResponse', editResponse.idHeat)
        if (editResponse && activeHeat.idHeat === editResponse.idHeat) {
            console.log('in use Effect2')
            setActiveHeat(editResponse)
            toastSuccess(SAVE_SUCCESS)
            setHeatsUrl('/heats')
        } else if (editResponse === '') {
            console.log('in use Effect3')
            toastError(SAVE_ERROR)
        }
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [editResponse])
    useEffect(() => {
        console.log('in new heats array')
        let newHeatsArr = []
        if (heats && dogs) {
            console.log('in if', heats)
            heats.map((h) => newHeatsArr.push(buildHeat(h, true)))
        }
        setHeatsWithNames(newHeatsArr)
        console.log('newHeatsArray', newHeatsArr)
        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [heats, dogs])
    const { items, requestSort, sortConfig } = useSortableData(heatsWithName)
    const getClassNamesFor = (name) => {
        if (!sortConfig) {
            return
        }
        return sortConfig.key === name ? sortConfig.direction : undefined
    }
    const triggerModal = () => {
        setShowModal((prev) => !prev)
    }
    function buildHeat(origHeat, withName) {
        const newHeat = {
            idHeat: origHeat.idHeat,
            date: origHeat.date,
            idDog: origHeat.idDog,
        }
        if (withName) {
            let dogName =
                dogs && dogs.find((d) => d.idDog === origHeat.idDog).name
            Object.assign(newHeat, dogName && { name: dogName })
        }
        return newHeat
    }

    const modifyHeat = (h) => {
        setActiveHeat(h)
        triggerModal()
    }
    const saveHeat = () => {
        console.log('aH', activeHeat)
        setEditUrl('/heat/' + activeHeat.idHeat)
    }
    return (
        <Container>
            {heatsIsLoading || dogsIsLoading ? (
                <Spinner animation="border" variant="primary" />
            ) : (
                <div className="table-container">
                    <h1 className="text-center">Liste des chaleurs</h1>
                    <HeatModal
                        isShow={showModal}
                        triggerModal={triggerModal}
                        activeHeat={activeHeat}
                        setActiveHeat={setActiveHeat}
                        saveHeat={saveHeat}
                    />
                    <Paginator
                        items={items}
                        dogPerPage={heatPerPage}
                        setDogPerPage={setHeatPerPage}
                        firstDog={firstHeat}
                        setFirstDog={setFirstHeat}
                    />
                    <Table
                        striped
                        bordered
                        hover
                        responsive
                        className="table-fixed"
                    >
                        <thead>
                            <tr>
                                <th>
                                    <button
                                        type="buttton"
                                        onClick={() => requestSort('name')}
                                        className={getClassNamesFor('name')}
                                    >
                                        Nom
                                    </button>
                                </th>
                                <th>
                                    <button
                                        type="buttton"
                                        onClick={() => requestSort('date')}
                                        className={getClassNamesFor('date')}
                                    >
                                        Date dernière chaleur
                                    </button>
                                </th>
                                <th>Actions</th>
                            </tr>
                        </thead>
                        <tbody>
                            {items &&
                                items
                                    .slice(
                                        firstHeat,
                                        Number(firstHeat) + Number(heatPerPage)
                                    )
                                    .map((heat) => (
                                        <tr key={heat.idHeat}>
                                            <td>{heat.name}</td>
                                            <td>
                                                {formatDate().format(
                                                    new Date(heat.date)
                                                )}
                                            </td>
                                            <td>
                                                <Button className="noBackground noBorder">
                                                    <ActionLogoStyle
                                                        src={AddLogo}
                                                    />
                                                </Button>
                                                <Button
                                                    className="noBackground noBorder"
                                                    onClick={() =>
                                                        modifyHeat(heat)
                                                    }
                                                >
                                                    <ActionLogoStyle
                                                        src={ModifyLogo}
                                                    />
                                                </Button>
                                            </td>
                                        </tr>
                                    ))}
                        </tbody>
                    </Table>
                </div>
            )}
        </Container>
    )
}

export default HeatList

感谢您的帮助。

谢谢布伦丹,

这真是个好东西post,帮助我找到了解决方案。

最终挂钩:

const dataFetchReducer = (state, action) => {
    switch (action.type) {
        case 'FETCH_INIT':
            return {
                ...state,
                isLoading: true,
                isError: false,
            }
        case 'FETCH_SUCCESS':
            return {
                ...state,
                isLoading: false,
                isError: false,
                data: action.payload,
            }
        case 'FETCH_FAILURE':
            return {
                ...state,
                isLoading: false,
                isError: true,
            }
        default:
            throw new Error()
    }
}

export const useAxios = (initialUrl, method, initialData) => {
    const [url, setUrl] = useState(initialUrl)
    const [state, dispatch] = useReducer(dataFetchReducer, {
        isLoading: false,
        isError: false,
        data: initialData,
    })

    useEffect(() => {
        let didCancel = false
        const params = {
            method: method,
            url: url,
            headers: { accept: '*/*' },
            data: initialData,
        }

        const fetchData = async (params) => {
            dispatch({ type: 'FETCH_INIT' })
            console.log('in axios', method, url, params)
            try {
                const result = await axios(params)
                // console.log('result', result)
                if (!didCancel) {
                    dispatch({ type: 'FETCH_SUCCESS', payload: result.data })
                }
            } catch (error) {
                if (!didCancel) {
                    dispatch({ type: 'FETCH_FAILURE' })
                }
            }
        }
        url && fetchData(params)
        return () => {
            didCancel = true
        }
    }, [initialData, method, url])
    return [state, setUrl]
}

最终模态:

export default function HeatModal({
    isShow,
    triggerModal,
    activeHeat,
    setActiveHeat,
    saveHeat,
}) {
    function save() {
        saveHeat()
        triggerModal()
    }

    return (
        <>
            <Modal show={isShow} onHide={triggerModal}>
                <Modal.Header>
                    <Modal.Title>{activeHeat.name}</Modal.Title>
                </Modal.Header>
                <Modal.Body>
                    <Form>
                        <FormGroup as={Row}>
                            <FormLabel column>
                                Nouvelle date de chaleur
                            </FormLabel>
                            <Col>
                                <MyDatePicker
                                    field={'date'}
                                    value={activeHeat.date}
                                    setter={setActiveHeat}
                                />
                            </Col>
                        </FormGroup>
                    </Form>
                </Modal.Body>
                <Modal.Footer as={NavContainer}>
                    <Button variant="outline-success" onClick={triggerModal}>
                        Annuler
                    </Button>

                    <Button variant="success" onClick={save}>
                        Confirmer
                    </Button>
                </Modal.Footer>
            </Modal>
        </>
    )
}

最终名单:

const HeatList = () => {
    const heat = {
        idHeat: -1,
        name: '',
        date: new Date(),
        idDog: -1,
    }
    const [heatsWithName, setHeatsWithNames] = useState([])
    const [heatPerPage, setHeatPerPage] = useState(10)
    const [firstHeat, setFirstHeat] = useState(0)
    const [showModal, setShowModal] = useState(false)
    const [activeHeat, setActiveHeat] = useState(heat)
    const [heatToSave, setHeatToSave] = useState(undefined)
    const [{ data: dogs, isLoading: dogsIsLoading }] = useAxios(
        '/dogs',
        'GET',
        null
    )
    const [{ data: editResponse, isLoading: editLoading }, setEditUrl] =
        useAxios('', 'PUT', heatToSave)
    const [{ data: heats, isLoading: heatsIsLoading }] = useAxios(
        '/heats',
        'GET',
        editResponse
    )
    useEffect(() => {
        if (
            editResponse &&
            activeHeat.idHeat === editResponse.idHeat &&
            editResponse > 0
        ) {
            console.log('in use Effect2')
            toastSuccess(SAVE_SUCCESS)
        } else if (editResponse === '') {
            console.log('in use Effect3')
            toastError(SAVE_ERROR)
        }
    }, [activeHeat.idHeat, editResponse])
    useEffect(() => {
        console.log('in new heats array', heats, 'dogs', dogs)
        let newHeatsArr = []
        let dogList = []
        if (heats && dogs && Array.isArray(heats)) {
            dogList = dogs
                .filter(({ gender }) => gender === 'Femelle')
                .filter(({ status }) => status === 'Élevage')
                .filter(({ state }) => state === 'Vivant')

            // console.log('in if', dogList)
            dogList.map((d) =>
                newHeatsArr.push({
                    idDog: d.idDog,
                    name: d.name,
                    idHeat: heats.find((h) => d.idDog === h.idDog)
                        ? heats.find((h) => d.idDog === h.idDog).idHeat
                        : -1,
                    date: heats.find((h) => d.idDog === h.idDog)
                        ? heats.find((h) => d.idDog === h.idDog).date
                        : 'Jamais',
                })
            )
        }
        setHeatsWithNames(newHeatsArr)
        console.log('newHeatsArray', newHeatsArr)
    }, [heats, dogs])
    const { items, requestSort, sortConfig } = useSortableData(heatsWithName)
    const getClassNamesFor = (name) => {
        if (!sortConfig) {
            return
        }
        return sortConfig.key === name ? sortConfig.direction : undefined
    }
    const triggerModal = () => {
        setShowModal((prev) => !prev)
    }
    const modifyHeat = (h) => {
        setActiveHeat(h)
        triggerModal()
    }
    const saveHeat = () => {
        console.log('aH', activeHeat)
        setEditUrl('/heat/' + activeHeat.idHeat)
        setHeatToSave(activeHeat)
    }
    return (
        <Container>
            {heatsIsLoading || dogsIsLoading ? (
                <Spinner animation="border" variant="primary" />
            ) : (
                <div className="table-container">
                    <h1 className="text-center">Liste des chaleurs</h1>
                    <HeatModal
                        isShow={showModal}
                        triggerModal={triggerModal}
                        activeHeat={activeHeat}
                        setActiveHeat={setActiveHeat}
                        saveHeat={saveHeat}
                    />
                    <Paginator
                        items={items}
                        dogPerPage={heatPerPage}
                        setDogPerPage={setHeatPerPage}
                        firstDog={firstHeat}
                        setFirstDog={setFirstHeat}
                    />
                    <Table
                        striped
                        bordered
                        hover
                        responsive
                        className="table-fixed"
                    >
                        <thead>
                            <tr>
                                <th>
                                    <button
                                        type="buttton"
                                        onClick={() => requestSort('name')}
                                        className={getClassNamesFor('name')}
                                    >
                                        Nom
                                    </button>
                                </th>
                                <th>
                                    <button
                                        type="buttton"
                                        onClick={() => requestSort('date')}
                                        className={getClassNamesFor('date')}
                                    >
                                        Date dernière chaleur
                                    </button>
                                </th>
                                <th>Actions</th>
                            </tr>
                        </thead>
                        <tbody>
                            {items &&
                                items
                                    .slice(
                                        firstHeat,
                                        Number(firstHeat) + Number(heatPerPage)
                                    )
                                    .map((heat) => (
                                        <tr key={heat.idDog}>
                                            <td>{heat.name}</td>
                                            <td>
                                                {heat.date === 'Jamais'
                                                    ? 'Jamais'
                                                    : formatDate().format(
                                                          new Date(heat.date)
                                                      )}
                                            </td>
                                            <td>
                                                <Button className="noBackground noBorder">
                                                    <ActionLogoStyle
                                                        src={AddLogo}
                                                    />
                                                </Button>
                                                <Button
                                                    className="noBackground noBorder"
                                                    disabled={
                                                        heat.date === 'Jamais'
                                                    }
                                                    onClick={() =>
                                                        modifyHeat(heat)
                                                    }
                                                >
                                                    <ActionLogoStyle
                                                        src={ModifyLogo}
                                                    />
                                                </Button>
                                            </td>
                                        </tr>
                                    ))}
                        </tbody>
                    </Table>
                </div>
            )}
        </Container>
    )
}

export default HeatList