1

In the categories component, I render a random image from each category. I also added a onClick event to each image. When the image is clicked, it will dispatch the action getCategory(target.alt) and the DOM will render the products from the clicked category. The problem I got is that every time I clicked a random category image, the DOM will re-render and new random images will appear on the DOM. How do I prevent this re-render? Below is my codes.

const Categories = ({selectedCategory}) => {
    const isLoading = useSelector(state => state.productsReducer.isLoading);
    const productsByCategory = useSelector(state => 
        state.productsReducer.productsByCategories);

    const getRandomProductsByCategory = () => {
        const randomProducts = []
        for(let categories in productsByCategory) {
            const randomCategory = productsByCategory[categories][getRandomIndex(productsByCategory[categories].length)];
            productsByCategory[categories].map(category => {
                if(category === randomCategory) {
                    randomProducts.push(category)
                }
            })
        }
        return randomProducts;
    }

    return (
        <div class='categories-container'>
           {getRandomProductsByCategory().map(randomProduct => (
                <img onClick={selectedCategory} src={randomProduct.image} />}
        </div>
    )
}


function App() {
  const dispatch = useDispatch();
  const category = useSelector(state => state.productsReducer.category)

  useEffect(() => {
    dispatch(getProducts())
  }, [dispatch])

  const handleCategoryClick = ({target}) => {
    return dispatch(getCategory(target.alt))
  }

  return (
    <>
      {/* <ProductsList /> */}
      <Categories selectedCategory={handleCategoryClick} />
      {category.map(product => <img src={product.image} />)}
    </>
  )
}

const populateProductsStarted = () => ({
    type: 'POPULATE_PRODUCTS/fetchStarted'
})

const populateProductsSuccess = products => ({
    type: 'POPULATE_PRODUCTS/fetchSuccess',
    payload: products
})

const populateProductsFailed = error => ({
    type: 'POPULATE_PRODUCTS/fetchFailed',
    error
})

export const getCategory = (category) => ({
    type: 'GET_CATEGORY',
    category
})

const getProducts = () => async dispatch => {
    dispatch(populateProductsStarted())
    try {
        const response = await fetch(url)
        if(response.ok) {
            let jsonResponse = await response.json();
           return dispatch(populateProductsSuccess(jsonResponse))
        }
    } catch (err) {
        dispatch(populateProductsFailed(err.toString()))
    }
}

const initialState = {
    isLoading: false,
    isError: null,
    allProducts: [],
    productsByCategories: {},
    category: []
}

const productsReducer = (state=initialState, action) => {
    switch(action.type) {
        case 'POPULATE_PRODUCTS/fetchStarted':
            return {
                ...state,
                isLoading: true
            }
        case 'POPULATE_PRODUCTS/fetchSuccess':
            return {
                ...state,
                isLoading: false,
                allProducts: action.payload,
                productsByCategories: action.payload.reduce((accumulatedProduct, currentProduct) => {
                    accumulatedProduct[currentProduct.category] = accumulatedProduct[currentProduct.category] || [];
                    accumulatedProduct[currentProduct.category].push(currentProduct);
                    return accumulatedProduct;
                }, {}) 
            }
        case 'POPULATE_PRODUCTS/fetchFailed':
            return {
                ...state,
                isError: action.error
            }
        case 'GET_CATEGORY':
            return {
                ...state,
                category: state.allProducts.filter(product => product.category === action.category)
            }
        default:
            return state
    }
}

1 Answer 1

3

One way to achieve this is through memoization provided by React's useMemo.

const images = React.useMemo(getRandomProductsByCategory().map(randomProduct => (
                <img onClick={selectedCategory} src={randomProduct.image} />, [productsByCategory])
return (
        <div class='categories-container'>
           {images}
        </div>
    )

This will keep the srcs consistent across re-renders.

Sign up to request clarification or add additional context in comments.

1 Comment

Hi @Slava! Thank you for your response. I tried this method out, but the random images are not rendering when I reload the page.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.