enoch
enoch

Reputation: 3123

Error when converting a class component to a functional component

i am trying to convert my class component to a functionnal component but my filters are not working anymore.

the main error is when I want to convert listProducts because I don't know how to define the prevState with useState for that case

how can update the state for case?

this is my code

class component

class ListShopping extends Component{
    constructor(props) {
        super(props);
        this.state = {
            size: "",
            sort: "",
            cartItems: [],
            products: [],
            filteredProducts: []
        };
    }
    componentDidMount() {
        fetch("http://localhost:8000/products")
            .then(res => res.json())
            .catch(err =>
                fetch("db.json")
                    .then(res => res.json())
                    .then(data => data.products)
            )
            .then(data => {
                this.setState({ products: data });
                this.listProducts();
            });
    }

    listProducts = () => {
        this.setState(state => {
            if (state.sort !== "") {
                state.products.sort((a, b) =>
                    state.sort === "lowestprice"
                        ? a.price < b.price
                        ? 1
                        : -1
                        : a.price > b.price
                        ? 1
                        : -1
                );
            } else {
                state.products.sort((a, b) => (a.id > b.id ? 1 : -1));
            }

            if(state.size !== ""){
                return {
                    filteredProducts: state.products.filter(
                        a => a.availableSizes.indexOf(state.size.toUpperCase()) >= 0
                    )
                }
            }
            return { filteredProducts: state.products };
        });
    };
    handleSortChange = e => {
        this.setState({ sort: e.target.value });
        this.listProducts();
    };
    handleSizeChange = e => {
        this.setState({ size: e.target.value });
        this.listProducts();
    };
    render() {
        return (
            <div className="container">
                <h1>E-commerce Shopping Cart Application</h1>
                <hr />
                <div className="row">
                    <div className="col-md-9">
                        <Filter
                            count={this.state.filteredProducts.length}
                            handleSortChange={this.handleSortChange}
                            handleSizeChange={this.handleSizeChange}
                        />
                        <hr />
                        <Products
                            products={this.state.filteredProducts}
                        />
                    </div>
                </div>
            </div>
        );
    }
} 

functionnal component

const ListShopping = () => {
    const [data, setData] = useState({
        products : [],
        filteredProducts : [],
        sort : '',
        size : ''
    })

    const {products, filteredProducts, sort, size} = data;
    const fetchApi = () => {
        axios.get(`http://localhost:8000/products`)
            .then(response => response.data)
            .then(data => {
                setData({...data, products: data});
            })
    }

    const listProducts = () => {

    };
    const handleSortChange = e => {
        setData({...e, sort: e.target.value})
        listProducts();
    };
    const handleSizeChange = e => {
        setData({...e, size: e.target.value})
        listProducts();
    };

    useEffect(()=> {
        fetchApi()
    }, [])
    return(
        <div className="container">
            <h1>E-commerce Shopping Cart Application</h1>
            <hr />
            <div className="row">
                <div className="col-md-9">
                    <Filter
                        count={filteredProducts.length}
                        handleSortChange={handleSortChange}
                        handleSizeChange={handleSizeChange}
                    />
                    <hr />
                    <Products
                        products={filteredProducts}
                    />
                </div>
            </div>
        </div>
    )
}

Upvotes: 0

Views: 63

Answers (2)

moshfiqrony
moshfiqrony

Reputation: 4723

Suppose you have a state like this

const [todos, setTodos] = useState([1,2,3,4,5,6,7]);

Method 1:

Now if you want to get prevState from this hook try this way

setTodos(oldTodo => {
                    oldTodo.push(1000);
                    setTodos(oldTodo)
                })

this oldTodo work the same way the prevState works.

Method 2:

const listProducts = () => {
        let oldState = todos
        //Now do what you want to do with it. modify the oldTodo
        setTodos(oldTodo);
    }

I prefer the second method because it gives me more flexibility to change modify the state and if you cannot manage the state properly in the first method or if it finds any bug then it will return undefined so I prefer to work taking the whole state in a temporary variable and after work is done set it

Upvotes: 1

iamhuynq
iamhuynq

Reputation: 5529

Try this

const listProducts = () => {
    setData(data => {
        if (data.sort !== '') {
            data.products.sort((a, b) =>
                data.sort === 'lowestprice'
                    ? a.price < b.price
                        ? 1
                        : -1
                    : a.price > b.price
                    ? 1
                    : -1,
            );
        } else {
            data.products.sort((a, b) => (a.id > b.id ? 1 : -1));
        }

        if (data.size !== '') {
            return {
                ...data,
                filteredProducts: data.products.filter(
                    a => a.availableSizes.indexOf(data.size.toUpperCase()) >= 0,
                ),
            };
        }

        return { ...data, filteredProducts: data.products };
    });
};

Upvotes: 2

Related Questions