简体   繁体   English

数组状态更新后反应不重新渲染

[英]React not re-rendering after array state update

I have a checkbox list UI that is rendered from an array.我有一个从数组呈现的复选框列表 UI。 After I update the array the checkbox list state does not update.更新数组后,复选框列表状态不会更新。

I moved the code where the list is mapped but it does not change the results.我移动了映射列表的代码,但它不会改变结果。 The DOM re-render does not happen, see gif below.不会发生 DOM 重新渲染,请参见下面的 gif。

I have been looking arround and I see that this issue is already reported however the solution about moving the list.map code out of the function it did not work for me.我一直在寻找,我发现这个问题已经被报告了,但是关于将list.map代码移出函数的解决方案对我不起作用。

Could you suggest me a solution?你能给我一个解决方案吗? What is the source of this problem?这个问题的根源是什么?

这是当前状态

import React,{ useState } from "react"

import
{
    Box,
    DropButton,
    Grid,
    Text,
    Calendar,
    RangeInput,
    CheckBox
} from "grommet"

const CalButton = ( { label,onSelect } ) =>
{
    return (
        <DropButton
            label={ label }
            dropAlign={ { top: 'bottom',left: 'left' } }
            margin="small"
            dropContent={
                <Box pad="medium" background="ligth-3" elevation="small">
                    <Calendar range size="medium" onSelect={ onSelect } />
                </Box>
            } />
    )
}

const RangeButton = ( { label,value,onChange,min,max,step,unit,header } ) =>
{

    return (
        <DropButton
            label={ value === null ? label : value + ' ' + unit }
            dropAlign={ { top: 'bottom',left: 'left' } }
            margin="small"
            dropContent={
                <Box pad="medium"
                    background="ligth-3"
                    elevation="small"
                    align="center"
                >
                    <Text size="small">{ header }</Text>
                    <RangeInput
                        value={ value }
                        min={ min } max={ max }
                        onChange={ onChange }
                        step={ step }
                    />
                    <Text weight="bold">{ value }</Text>
                    <Text weight="normal">{ unit }</Text>

                </Box>
            } />
    )
}

const FeaturesButton = ( { label,features,onChange } ) =>
{
    const FeaturesList = ( { features,onChange } ) => (
        <>
            { features.map( ( item,idx ) => (
            <CheckBox
                key={ item.name }
                label={ item.name }
                checked={ item.checked }
                onChange={ e => onChange( e,idx ) } />) 
                )
            }
        </>
    )



    return (
        <DropButton
            label={ label }
            dropAlign={ { top: 'bottom',left: 'left' } }
            margin="small"
            dropContent={
                <Box pad="medium"
                    background="ligth-3"
                    elevation="small"
                    align="start"
                    direction="column"
                    gap="small"
                >
                    <FeaturesList 
                        features={features} 
                        onChange={onChange} />

                </Box>
            } />
    )
}


const destApp = () =>
{

    const [ windStrength,setWindStrength ] = useState( null )
    const [ windFrequency,setWindFrequency ] = useState( null )
    const [ cost,setCost ] = useState( null )

    const date = new Date()
    const [ month,setMonth ] = useState( date.getMonth() )

    const [ listFeatures,setListFeatures ] = useState( [
        {
            name: 'Butter flat water',
            checked: false,
        },
        {
            name: 'Moderately flat water',
            checked: false,
        },
        {
            name: '1-2m Waves',
            checked: false,
        },
        {
            name: '2m+ Waves',
            checked: false,
        },
        {
            name: 'Beginer Friendly',
            checked: false,
        },
        {
            name: 'Kite-in-kite-out',
            checked: false,
        },
        {
            name: 'Nightlife',
            checked: false,
        }

    ] )
    const months = [ 'January','February','March','April','May','June','July','August','September','October','November','December' ];

    const updateFeaturesList = ( e,idx ) =>
    {

        listFeatures[ idx ].checked = e.target.checked
        const newFeatures = listFeatures
        setListFeatures( newFeatures )
        console.log( "Updated features list",newFeatures,e.target.checked )
    }

    return (
        <Grid rows={ [ "xsmall","fill" ] }
            areas={ [ [ "filterBar" ],[ "results" ] ] }
            gap="xxsmall">
            <Box gridArea="filterBar"
                direction="row-responsive"
                gap="xsmall"
                pad="xsmall"
                justify="center" >
                <CalButton label={ months[ month ].toLowerCase() } onSelect={ ( data ) => console.log( data ) } />
                <RangeButton
                    label="wind strength"
                    header="What's your wind preference?"
                    min="15"
                    max="45"
                    unit="kts"
                    value={ windStrength }
                    step={ 1 }
                    onChange={ ( e ) =>
                    {
                        setWindStrength( e.target.value )
                        console.log( windStrength )
                    } } />
                <RangeButton
                    label="wind frequency"
                    header="How often does your destination need to be windy?"
                    min="1"
                    max="7"
                    unit="days/week"
                    value={ windFrequency }
                    step={ 1 }
                    onChange={ ( e ) =>
                    {
                        setWindFrequency( e.target.value )
                        console.log( windFrequency )
                    } } />
                <RangeButton
                    label="cost"
                    header="Average daily cost: 1 lunch, diner and doubble room at a midrange hotel?"
                    min="10"
                    max="400"
                    unit="€"
                    value={ cost }
                    step={ 1 }
                    onChange={ ( e ) =>
                    {
                        setCost( e.target.value )
                        console.log( cost )
                    } } />

                <FeaturesButton
                    label="important features "
                    features={ listFeatures }
                    onChange={ updateFeaturesList }
                />
            </Box>
            <Box gridArea="results"
                margin="">
                Results go in here!

            </Box>


        </Grid>

    )
}

export default destApp 

The problem is in updateFeaturesList , you are mutating the state directly in this line listFeatures[ idx ].checked = e.target.checked , the state reference stay the same and so react does not know if it should re-render.问题出在updateFeaturesList ,您直接在这一行listFeatures[ idx ].checked = e.target.checked中改变状态,状态引用保持不变,因此 react 不知道是否应该重新渲染。

What you can do is copy the state, before changing it :您可以做的是在更改状态之前复制状态:

const updateFeaturesList = ( e,idx ) =>
{

    const newFeatures = [...listFeatures];
    newFeatures[ idx ].checked = e.target.checked
    setListFeatures( newFeatures )
    console.log( "Updated features list",newFeatures,e.target.checked )
}

You're mutating the original state in your updateFeaturesList function.您正在改变updateFeaturesList函数中的原始状态。 Use the functional form of setState to update your current feature list:使用setState的函数形式来更新您当前的功能列表:

const updateFeaturesList = (e, idx) => {
  const { checked } = e.target;
  setListFeatures(features => {
    return features.map((feature, index) => {
      if (id === index) {
        feature = { ...feature, checked };
      }

      return feature;
    });
  });
};

Also note that calling console.log("Updated features list", newFeatures,e.target.checked) immediately after setting the state won't show the updated state, since setting state is async.另请注意,在设置状态后立即调用console.log("Updated features list", newFeatures,e.target.checked)不会显示更新状态,因为设置状态是异步的。

React state will trigger render only if value changed in state.只有当状态中的值发生变化时,React 状态才会触发渲染。 Modifying or pushing values to array wont change the array reference, here react state uses array reference to decide whether to trigger render or not.so here array is modifying but reference is not changing修改或推送值到数组不会改变数组引用,这里反应状态使用数组引用来决定是否触发渲染。所以这里数组正在修改但引用没有改变

solution : copy your array to new array and set to state will solve issue解决方案:将您的数组复制到新数组并设置为状态将解决问题

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM