【问题标题】:How to autoselect default option on re-render in React MaterialUI Select如何在 React Material UI Select 中重新渲染时自动选择默认选项
【发布时间】:2021-06-11 10:41:10
【问题描述】:

我正在开发一项功能,客户在购买包裹时可以获得折扣。

左边的项目是固定的,不会改变。它与右侧的物品一起包装,客户可以在其中选择滑雪板:

我所需要的只是当客户选择一个尺寸,然后滑动到下一个滑雪板时,从前一个滑雪板中选择的尺寸将被设置回默认的“选择尺寸选项”。

这是父组件的代码:

import React, { useState, useEffect } from 'react';

// Material UI
import { makeStyles } from '@material-ui/core/';

// React Responsive Carousel
import { Carousel } from 'react-responsive-carousel';
import '../../../../../../node_modules/react-responsive-carousel/lib/styles/carousel.min.css'

// Component
import PackageProd from './PackageProd/packageProd';

const useStyles = makeStyles({
    packageCard: {
        maxWidth: 345,
        height: '100%'
    },
});

const Packages = (props) => {
    const classes = useStyles();
    const [defaultProd, setDefaultProd] = useState({ size: 'CHOOSE THE SIZE', discount: '0', price: '0', barcode: 'default' });

    function handleSetProd(val){
        setDefaultProd(val)
    }

    return <Carousel className={classes.packageCard} showIndicators={false}
        renderItem={item => <div style={{ background: "white", height: '100%' }}>{item}</div>}
        onChange={(val)=>{
            setDefaultProd({ size: 'CHOOSE THE SIZE', discount: '0', price: '0', barcode: 'default' })
        }}
    >
        {props._packages.map((prod, i)=>{
            return <PackageProd 
                key={i} 
                _prod={prod} 
                _handleSetProd={handleSetProd}
                _defaultProd={defaultProd}
            ></PackageProd>
        })}
    </Carousel>
}

export default Packages;

我正在使用一个 npm 包 Carousel,它带有一个内置方法 onChange,每次滑动都会触发。因此,每次我滑动到下一个滑雪板或上一个滑雪板时,它都会设置 defaultProd。

这是子组件:

import React, { useEffect, useState } from 'react';

// Material UI
import { makeStyles, Grid, Card, CardContent, CardMedia,
    Typography, CardActions, Button, FormControl, InputLabel,
    Select,  } from '@material-ui/core/';
import { display } from '@material-ui/system';

// React Router
import { Link } from "react-router-dom";

// Knight Demon
import knightDemon from '../../../../../../assets/icons/knight_demon.png';

// Price Format
const { format } = require('number-currency-format');

const useStyles = makeStyles({
    sizes: {
        minWidth: '100%'
    },
    media: {
        height: '20rem',
        objectFit: 'contain'
    },
    text: {
        color: 'black'
    },
    price: {
        color: 'green'
    },
    redPrice: {
        color: 'red',
        textDecoration: 'line-through'
    },
});

const PackageProd = (props) => {
    const classes = useStyles();
    const [prodDetails, setProdDetails] = useState({});

    const handleChange = (event) => {
        var specificProd = JSON.parse(event.target.value)
        props._handleSetProd(specificProd)
    };

    const PriceWithDiscount = () => <Grid container direction='row' justify='center' spacing={2}>
        <Grid item>
            <span className={classes.redPrice}>
                {format(props._defaultProd.price, {
                    currency: 'isk',
                    showDecimals: 'NEVER',
                    thousandSeparator: ' '
                })}
            </span>
        </Grid>
        <Grid item>
            <span className={classes.price}>
                {format(Math.ceil(parseInt(props._defaultProd.price) - (parseInt(props._defaultProd.price) * props._defaultProd.discount / 100)), {
                    currency: 'isk',
                    showDecimals: 'NEVER',
                    thousandSeparator: ' '
                })}
            </span>
        </Grid>
    </Grid>

    const PriceWithoutDiscount = () => <span className={classes.price}>
        {format(props._defaultProd.price, {
            currency: 'isk',
            showDecimals: 'NEVER',
            thousandSeparator: ' '
        })}
    </span>

    return <div>
        <CardMedia
            className={classes.media}
            component='img'
            image={props._prod.images.length > 0 ? props._prod.images[0] : knightDemon}
        />
        <CardContent>
            <Link to={`/product?id=${props._prod._id}`}>
                <Typography className={classes.text} variant='h5' component='h2' onClick={()=>console.log(props._prod)}>
                    {props._prod.description}
                </Typography>
            </Link>
            {props._defaultProd.discount > 0 ?
                <PriceWithDiscount></PriceWithDiscount> :
                <PriceWithoutDiscount></PriceWithoutDiscount>
            }
        </CardContent>
        <CardActions>
        {props._prod.sizepricesdiscountqty.length >= 1 &&
            props._prod.sizepricesdiscountqty[0].size !== '' ?
            <FormControl className={classes.sizes}>
            <InputLabel htmlFor='age-native-simple'>SIZE</InputLabel>
                <Select
                    native
                    defaultValue={props._defaultProd.size}
                    onChange={handleChange}
                    inputProps={{
                        name: 'prodDetails',
                        id: 'age-native-simple',
                    }}
                >
                    <option value={JSON.stringify(props._defaultProd)}>CHOOSE SIZE</option>
                    {props._prod.sizepricesdiscountqty.map((item, i)=>{
                        if(parseInt(item.qty) > 0){
                            return <option key={i}
                                value={JSON.stringify(item)}
                            >
                                {item.size.toUpperCase()}
                            </option>
                        } 
                    })}
                </Select>
            </FormControl> :
            <Grid container justify='flex-start'>
            {props._prod.sizepricesdiscountqty[0].size === '' ?
                <Typography>SIZE: NO SIZE</Typography> :
                <Typography>SIZE: {props._prod.sizepricesdiscountqty[0].size.toUpperCase()}</Typography>
            }
            </Grid>
        }
        </CardActions>
    </div>
}

export default PackageProd;

我不明白的行为是材料选择组件的默认值。当我记录从父级传递的 defaultProd 时,它具有它假设的所有值。当我选择不同的大小时,它会更改它并将父级中的 defaultProd 设置为新对象。

问题是当我滑动到新产品时,它应该将 defaultValue 更改为 defaultProd 的大小值,但它没有。更改反映在价格中,控制台正确记录了 defaultProd,但大小没有更改为“选择大小”,我不知道为什么它没有反映应有的更改。这是视觉示例:

  1. 我选择了尺寸,变化反映在价格和尺寸上,我成功记录了变化:

然后我滑到下一个滑雪板并滑回上一个。价格反映变化。 defaultProd 被正确记录,但大小没有改变:

到目前为止,我尝试的是尝试强制重新渲染:

const [, updateState] = useState();
const forceUpdate = useCallback(() => updateState({}), []);  

并在子组件的 useEffect() 中触发它们。我尝试使用value 而不是defaultValue,但是在更改大小时它不起作用,只会一直显示CHOOSE SIZE。在这一点上,我不明白材料选择中的 defaultValue 是如何工作的,以及为什么它不反映变化。

【问题讨论】:

    标签: reactjs select react-hooks material-ui


    【解决方案1】:

    在子组件中,我将 Select 中的 defaultValue 更改为 value,删除了 native 并使用了 renderValue 函数。所以我在 Select 中的子组件代码是这样的:

    <Select
       value={JSON.stringify(props._defaultProd)}
       renderValue={(val)=>JSON.parse(val).size}
       onChange={handleChange}
    >
       <option disabled value={JSON.stringify(props._defaultProd)}>CHOOSE SIZE</option>
       {props._prod.sizepricesdiscountqty.map((item, i)=>{
          if(parseInt(item.qty) > 0){
             return <option key={i}
                value={JSON.stringify(item)}
             >
                {item.size.toUpperCase()}
             </option>
          } 
       })}
    </Select>
    

    现在值正在重新渲染中反映出来。

    【讨论】:

      猜你喜欢
      • 2021-09-05
      • 2020-04-16
      • 1970-01-01
      • 1970-01-01
      • 2020-07-26
      • 2021-11-26
      • 2016-12-31
      • 2021-11-17
      • 1970-01-01
      相关资源
      最近更新 更多