将新的商店状态放入道具后,反应不会重新渲染

Posted

技术标签:

【中文标题】将新的商店状态放入道具后,反应不会重新渲染【英文标题】:react doesn't get re-render after getting new store state into props 【发布时间】:2019-12-09 17:12:19 【问题描述】:

我正在使用 react 和 react-redux。 我使用 mapstatetoprops 和 mapdispatchtoprops 来更新我的反应组件的视图。 除了 redux 存储更改后重新渲染不起作用外,一切正常。动作调度工作正常,reducer 工作正常,我可以 console.log 存储状态并检查差异。 起初,我使用了 useDispatch 和 useSelector,一切正常。但是我将其更改为 mapdispatchtoprops 和 mapstatetoprops 以将我的代码合并到我的项目队友的代码中。

我试图将 this.props.(whatineed) 直接放在我的 render() 组件的 return 中。据我了解,通过 mapstatetoprops,应该将存储状态传递到我的组件的 props 中。

import React,  Component  from 'react';
import  ToggleButton, ToggleButtonGroup  from 'react-bootstrap';
import  useSelector, useDispatch  from 'react-redux';
import  checked, notchecked  from '../../../actions';
import  connect  from "react-redux";
import local from './address';
import './index.css';
const mapStateToProps = state => 
    return 
      localsel : state.selectedLocal.locals
    
  
let mapDispatchToProps = (dispatch) => 
    return 
        check: (btn) => dispatch(checked(btn)),
        uncheck: (btn) => dispatch(notchecked(btn))
    

class Seoul extends Component 
    constructor(props)
        super(props)
    
    render()
        var btnclicked = (e) => 
            let btnname = e.target.parentNode.getAttribute('id');
            if (e.target.checked) 
                console.log('checked');
                this.props.check(btnname);
            ;
            if (!e.target.checked) 
                console.log('not checked');
                this.props.uncheck(btnname);
            ;
            // HERE IS WHERE I CAN CHECK THE PASSED STORE STATE
            console.log(this.props.localsel);
            // -------------------------------------------------
        
        return (
            <div className='localdiv localdiv1'>
                // HERE IS WHERE I WANT TO SEE MY STORE STATE
                this.props.localsel.map(val=>
                    return <h1>val</h1>
                )
                // --------------------------------------------
                <ToggleButtonGroup className='togglebtngrp' type="checkbox">
                    <ToggleButton className='togglebtn0' onChange=btnclicked variant="outline-secondary" value=0 id="entireseoul">Entire Seoul</ToggleButton>
                    local.Seoul.map((value, index) => 
                        return (<ToggleButton key=index className='togglebtn' onChange=btnclicked variant="outline-primary" value=index + 1 id=value>value</ToggleButton>)
                    )
                </ToggleButtonGroup>
            </div>
        );
    

export default connect(mapStateToProps, mapDispatchToProps)(Seoul);

该组件在父组件中导出,即

import React,  Component  from 'react';
import  Jumbotron  from 'react-bootstrap';
import  Gyeongi, Incheon, Busan, Daegue, Daejeon, Sejong, Gwangju, Ulsan, Gangwon, Gyungnam, Gyungbuk, Jeonnam, Jeonbuk, Choongnam, Choongbuk, Jeju, Othercountry  from './Locals';
import Seoul from './Locals';
import './Detailsrch.css';

class Detailsrch extends Component
    render()
        var localselect = (e) => 
            let selector = document.getElementsByClassName('locals');
            let selector_local = document.getElementsByClassName('localdiv');
            let i = 0;
            for (let j = 0; j < selector_local.length; j++) 
                selector_local[j].style.display = 'none';
            
            let boxclass = e.target.getAttribute('name');
            if (boxclass) document.getElementsByClassName(boxclass)[0].style.display = 'block';
            while (selector[i]) 
                selector[i].className = 'locals';
                i++;
            
            if (e.target.className == 'localtext') 
                e.target.parentElement.className = 'locals localclick';
             else 
                e.target.className = 'locals localclick';
            
        
        return (
            <Jumbotron className='searchjumbo'>
                <p>Locals</p>
                <Seoul />
                <Gyeongi />
                <Incheon />
                <Busan />
                <Daegue />
                <Daejeon />
                <Sejong />
                <Gwangju />
                <Ulsan />
                <Gangwon />
                <Gyungnam />
                <Gyungbuk />
                <Jeonnam />
                <Jeonbuk />
                <Choongnam />
                <Choongbuk />
                <Jeju />
                <Othercountry />
                <hr className='firsthr' />
                <p>type</p><hr />

                <p>career</p><hr />

                <p>country</p><hr />

                <p>sex</p>
            </Jumbotron>
        );
    
;

export default Detailsrch;

这是我的减速器

import  combineReducers  from 'redux';
const initialstate = 
    locals: []

const localSelector = (state = initialstate, action) => 
    switch(action.type)
        case 'CHECKED':
            if(action.payload)
                var arr = state.locals;
                arr.push(action.payload);
                return 
                    ...state,
                    locals: arr
                ;
             else 
                return state;
            
        case 'NOTCHECKED':
            if(action.payload)
                var arrnum = state.locals.indexOf(action.payload);
                var arr = state.locals;
                arr.splice(arrnum, 1);
                return 
                    ...state,
                    locals: arr
                ;
             else 
                return state;
            
        default:
            return state;
    
;

const rootReducer = combineReducers(
    selectedLocal: localSelector
);
export default rootReducer;

我希望当 props 值发生变化时,组件会重新渲染,我会在浏览器中看到变化。 props 值发生了变化,但在浏览器中没有任何反应。

【问题讨论】:

也发布你的减速器。 @ravibagul91 我在底部添加了它 【参考方案1】:

我看不到 DetailsrchSeoul 中导入,但反之亦然 SeoulDetailsrch 中导入,根据代码和 cmets,我可以看到 this.props.localsel 正在发生变化,这在 @ 中使用987654326@ 所以会调用Seoul 的r​​ender 方法,由于Detailsrch 中没有this.props.localsel 的映射或使用,所以不会调用render 方法。

因此,如果您想重新渲染Detailsrch,您需要将this.props.localsel 的映射从Seoul 更改为Detailsrch,并将此值作为道具传递给Seoul,它应该可以工作。 如果仍然存在问题,请将您的代码发布到 sandbox/codepen/jsfiddle,以便我们重现。

【讨论】:

是的,Seoul是在Detailsrch中导入的,我写错了。我想重新渲染Seoul 所以我把 this.props.localsel.map(val=>return

val) 作为回报在渲染中,但它仍然没有工作。

请你把你的代码放在 jsfiddle/codepen 的某个地方,以便我们可以重现【参考方案2】:

您可能正在遭受类似this 帖子提到的问题。使您的组件从存储中获取更改的一个技巧(当从存储中传递状态值作为道具时)是对道具(或您想要获取更改的道具)进行深度克隆,为此你可以使用 JSON:

JSON.parse(JSON.stringify(propToClone));

希望这会有所帮助。

P.S.:不要克隆作为函数的 props,因为 JSON 会擦除/忽略它们。

【讨论】:

【参考方案3】:

你正在改变 redux 状态,如下所示

var arr = state.locals;
arr.push(action.payload);

redux 状态应该是不可变的。您可以在此处查看有关如何在 reducer 中更新 redux 存储的一些提示。

https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns

【讨论】:

我看了文档,我真的不明白。我在做顶层的浅拷贝吗?你能给我一个例子,我应该用什么来代替var arr = state.locals;arr.push(action.payload);return ...state, locals: arr ;? ;( 当然,让我解释一下。假设您有这样的状态 locals: [] 。如果您使用您的方法var arr = state.locals; arr.push('something');,现在状态变为 locals: ['something'] 。现在状态发生了变异。当您改变状态时,组件可能不知道状态已更改。您可以使用不可变的更新模式,例如 const newLocals = state.locals.slice(); newLocals.push(action.payload); return ...state, locals: newlocals 非常感谢!最后我明白了。如果我使用var arr = state.locals;,那么 arr 将成为 state.locals 的引用变量,因为它们是数组!

以上是关于将新的商店状态放入道具后,反应不会重新渲染的主要内容,如果未能解决你的问题,请参考以下文章

防止子级在更新父级状态时重新渲染,使用父级方法作为本机反应的道具

即使道具没有改变,为啥还要对重新渲染组件做出反应?

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

高阶组件状态正在正确更新,但接收道具的包装组件不会在状态更改时重新渲染

反应:孩子点击改变父母的状态以重新渲染

在我滚动之前,react-virtualized 列表项不会使用更改的道具重新渲染