ReactJS asyn c ax ios в Context API - PullRequest
       65

ReactJS asyn c ax ios в Context API

0 голосов
/ 28 апреля 2020

Я получаю данные из rest-api в context.js. И я назначаю данные государству. Вот код из context.js:

export class AppProvider extends Component {
    state = {
        products: [],
        cart: [],
        dispatch: action => this.setState(state => reducer(state, action))
    }

    fetchData = async () => {
        const products = await axios.get('http://127.0.0.1:4000/product/all', { headers: { "Access-Control-Allow-Origin": "*" } })
        this.setState({
            products: products.data
        })
    }

    componentDidMount(){
        this.fetchData();
    }

    render() {
        return (
            <AppContext.Provider value={this.state}>
                {this.props.children}
            </AppContext.Provider>
        )
    }
}

Я использую данные в состоянии в других компонентах. Но когда страница обновляется, я не использую данные в состоянии в других компонентах. Когда страница обновляется, состояние в context.js становится исходным. Как я могу решить эту проблему? Вот код из другого компонента:

import React, { useState, useContext, useEffect } from 'react';
import { Button, Badge } from 'reactstrap';
import './ProductDetail.css';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faShoppingCart } from '@fortawesome/free-solid-svg-icons';
import { AppContext } from '../../../context/context';
import { toast, Slide } from 'react-toastify';
toast.configure();
const ProductDetail = props => {
    const [inputValue, setInputValue] = useState(1);


    const preventNegative = e => {
        const value = e.target.value.replace(/[^\d]/, '');
        if (parseInt(value) !== 0) {
            setInputValue(value);
        }
    }

    const addToCart = (e, productID) => {
        // eslint-disable-next-line array-callback-return
        const product = products.filter(p => {
            // eslint-disable-next-line eqeqeq
            if (p.id == productID) {
                return p
            }
        })
        console.log(product);
        dispatch({
            type: 'ADD_TO_CART',
            payload: [product, inputValue]
        })
        toast.success("The product has been added to cart successfully !", {
            position: toast.POSITION.TOP_CENTER,
            autoClose: 2500,
            transition: Slide
        })
    }



    const { products, dispatch } = useContext(AppContext)
    const filteredProduct = products.filter(p => p.id == props.match.params.id)

    return (
        <React.Fragment>
            <main className="mt-5 pt-4">
                <div className="container dark-grey-text mt-5">
                    <div className="row wow fadeIn">
                        <div className="col-md-6 mb-4">
                            <img src={filteredProduct[0].image} className="img-fluid" alt="" />
                        </div>
                        <div className="col-md-6 mb-4">
                            <div className="p-4">
                                <div className="mb-3">
                                    <Badge color="primary">New</Badge>
                                    <Badge color="success">Best seller</Badge>
                                    <Badge color="danger">{filteredProduct[0].discountRate}% OFF</Badge>
                                </div>
                                <h2 className="h2">{filteredProduct[0].title}</h2>
                                <p className="lead">
                                    <span className="mr-1">
                                        <del>${filteredProduct[0].prevPrice}</del>
                                    </span>
                                    <span>${filteredProduct[0].price}</span>
                                </p>
                                <p className="text-muted">
                                    {filteredProduct[0].detail}
                                </p>
                                <form className="d-flex justify-content-left">
                                    <input
                                        min="1"
                                        onChange={(e) => preventNegative(e)}
                                        value={inputValue}
                                        type="number"
                                        aria-label="Search"
                                        className="form-control"
                                        style={{ width: '100px' }} />

                                    <Button onClick={(e) => addToCart(e, filteredProduct[0].id)} color="primary">Add to Cart <FontAwesomeIcon icon={faShoppingCart} /> </Button>

                                </form>
                            </div>
                        </div>
                    </div>
                </div>
            </main>
        </React.Fragment>

    )
}
export default ProductDetail;

1 Ответ

1 голос
/ 28 апреля 2020

Ваш AppProvider компонент правильный, единственная проблема, с которой вы столкнулись, это то, что вы выбираете результаты из API и делаете это в componentDidMount, что должно быть правильным.

Однако, когда вы обновляете sh страницу, установленную в AppProvider, сбрасывается до начального значения, и, поскольку вы уже используете компонент ProductDetail, вы пытаетесь получить доступ к значениям продукта даже до того, как он станет доступен из Запрос API и, следовательно,

const filteredProduct = products.filter(p => p.id == props.match.params.id)

вернет вам пустой массив.

Проблема возникает из-за попытки доступа к filteredProduct[0].image и аналогичным другим свойствам.

Решение здесь используется loadState и визуализировать загрузчик до тех пор, пока данные не станут доступны

Также убедитесь, что при наличии данных filteredProduct никогда не будет пустым

export class AppProvider extends Component {
    state = {
        isLoading: true,
        products: [],
        cart: [],
        dispatch: action => this.setState(state => reducer(state, action))
    }

    fetchData = async () => {
        const products = await axios.get('http://127.0.0.1:4000/product/all', { headers: { "Access-Control-Allow-Origin": "*" } })
        this.setState({
            isLoading: false,
            products: products.data
        })
    }

    componentDidMount(){
        this.fetchData();
    }

    render() {
        return (
            <AppContext.Provider value={this.state}>
                {this.props.children}
            </AppContext.Provider>
        )
    }
}

и в productDetails

const ProductDetail = props => {
    const [inputValue, setInputValue] = useState(1);


    const preventNegative = e => {
        const value = e.target.value.replace(/[^\d]/, '');
        if (parseInt(value) !== 0) {
            setInputValue(value);
        }
    }

    const addToCart = (e, productID) => {
        // eslint-disable-next-line array-callback-return
        const product = products.filter(p => {
            // eslint-disable-next-line eqeqeq
            if (p.id == productID) {
                return p
            }
        })
        console.log(product);
        dispatch({
            type: 'ADD_TO_CART',
            payload: [product, inputValue]
        })
        toast.success("The product has been added to cart successfully !", {
            position: toast.POSITION.TOP_CENTER,
            autoClose: 2500,
            transition: Slide
        })
    }



    const { products, dispatch, isLoading } = useContext(AppContext)
    if(isLoading) return <div>Loading...</div>

    const filteredProduct = products.filter(p => p.id == props.match.params.id)

    return (
        <React.Fragment>
            <main className="mt-5 pt-4">
                <div className="container dark-grey-text mt-5">
                    <div className="row wow fadeIn">
                        <div className="col-md-6 mb-4">
                            <img src={filteredProduct[0].image} className="img-fluid" alt="" />
                        </div>
                        <div className="col-md-6 mb-4">
                            <div className="p-4">
                                <div className="mb-3">
                                    <Badge color="primary">New</Badge>
                                    <Badge color="success">Best seller</Badge>
                                    <Badge color="danger">{filteredProduct[0].discountRate}% OFF</Badge>
                                </div>
                                <h2 className="h2">{filteredProduct[0].title}</h2>
                                <p className="lead">
                                    <span className="mr-1">
                                        <del>${filteredProduct[0].prevPrice}</del>
                                    </span>
                                    <span>${filteredProduct[0].price}</span>
                                </p>
                                <p className="text-muted">
                                    {filteredProduct[0].detail}
                                </p>
                                <form className="d-flex justify-content-left">
                                    <input
                                        min="1"
                                        onChange={(e) => preventNegative(e)}
                                        value={inputValue}
                                        type="number"
                                        aria-label="Search"
                                        className="form-control"
                                        style={{ width: '100px' }} />

                                    <Button onClick={(e) => addToCart(e, filteredProduct[0].id)} color="primary">Add to Cart <FontAwesomeIcon icon={faShoppingCart} /> </Button>

                                </form>
                            </div>
                        </div>
                    </div>
                </div>
            </main>
        </React.Fragment>

    )
}
export default ProductDetail;
...