React-Redux:在子级中使用connect()时,子组件不会在父级状态更改时重新呈现

时间:2017-12-24 04:56:31

标签: reactjs react-native redux react-redux redux-thunk

我是Reactjs的新手,并一直在尝试使用React-Redux创建一个真实的网站。

在我的WebApp中,当主页(父组件)中的状态发生更改时,子组件 HomePage 不会重新呈现。我将在下面详细解释,

这是我的父组件,

import React, {Component} from 'react';
import NavBar from './../components/NavBar';
import HomePage from './../components/Home/HomePage';
import {connect} from 'react-redux';
import {loadCity} from './../actions/MainPage';
import Footer from './../components/Footer';
import {instanceOf} from 'prop-types';
import {withCookies, Cookies} from 'react-cookie';

class Home extends Component {

    constructor(props) {
        super(props);

        this.rerender = this
            .rerender
            .bind(this);

        this.state = {
            renderFlag: false
        }
    }

    static propTypes = {
        cookies: instanceOf(Cookies).isRequired
    };

    componentDidMount() {
        this
            .props
            .loadCity();

    }

    rerender() {
        this.setState({
            renderFlag: !this.state.renderFlag
        })
    }

    render() {

        return (
            <div>
                <NavBar placelist={this.props.result} rerenderfun={() => this.rerender()}/>
                <HomePage/>
                <Footer/>
            </div>
        );
    }
}

const mapStateToProps = (state) => {
    return {result: state.cityReducer}
};

const mapDispatchToProps = (dispatch) => {
    return {
        loadCity: () => {
            dispatch(loadCity());
        }
    };
};

export default withCookies(connect(mapStateToProps, mapDispatchToProps)(Home));

在我的 NavBar 组件中,我必须选择城市。当我点击城市时,在 HOME (父组件)中发生状态更改,这会导致子组件重新呈现。但所有子组件除了HomePage 重新渲染。

但如果我从主页中删除 connect(),则会重新呈现此页面。

这是我的HomePage代码,

import React, {Component} from 'react';
import SearchSection from './SearchSection';
import {connect} from 'react-redux';
import {loadInfoData} from './../../actions/MainPage';
import {instanceOf} from 'prop-types';
import {withCookies, Cookies} from 'react-cookie';
import InfoSection from './../../container/Home/InfoSection ';

class HomePage extends Component {

    static propTypes = {
        cookies: instanceOf(Cookies).isRequired
    };

    componentWillMount() {
        const {cookies} = this.props;
        this.setState({
            city: cookies.get('place')
        });
    }

    componentDidMount() {
        this
            .props
            .loadInfoData(this.state.city);
    }

    componentWillUpdate(nextProps, nextState) {
        console.log('Component WILL UPDATE!');
    }

    render() {

        return (
            <div><SearchSection/>  <InfoSection result={this.props.result}/> 

            </div>
        );
    }
}

const mapStateToProps = (state) => {
    return {data: state.cityReducer}
};

const mapDispatchToProps = (dispatch) => {
    return {
        loadInfoData: (selectedCity) => {
            dispatch(loadInfoData(selectedCity));
        }
    };
};

export default withCookies(connect(mapStateToProps, mapDispatchToProps)(HomePage));

请帮我找到问题并解决。我希望 HomePage 主页状态更改时重新呈现。

更新

减速

let defaultState = {
    result: "",
    info: "",
    recentlyadded: ""
}

const cityReducer = (state = defaultState, action) => {
    if (action.type === "GET_CITY") {
        return {
            ...state,
            result: action.result
        }
    } else if (action.type === "GET_INFO_DATA") {
        return {
            ...state,
            info: action.result
        }
    } else if (action.type === "GET_MAIN_RECENTLY_ADDED") {
        return {
            ...state,
            recentlyadded: action.result
        }
    } else {
        return {
            ...state
        }
    }
}

export default cityReducer;

动作

import axios from 'axios';

export function loadCity() {
    return (dispatch) => {
        return axios
            .get("**RESTAPILINK**")
            .then((response) => {
                dispatch(getPlace(response.data.result));
            })
    }
}

export function getPlace(result) {
    return {type: "GET_CITY", result}
}

export function loadInfoData(selectedCity) {
    var url = "**RESTAPILINK**" + selectedCity;
    return (dispatch) => {
        return axios
            .get(url)
            .then((response) => {
                dispatch(getInfoData(response.data));
            })
    }
}

export function getInfoData(result) {
    return {type: "GET_INFO_DATA", result}
}

export function loadRecentlyAdded(selectedCity) {
    var url = "**RESTAPILINK**" + selectedCity;
    return (dispatch) => {
        return axios
            .get(url)
            .then((response) => {
                dispatch(getRecentlyAdded(response.data));
            })
    }
}

export function getRecentlyAdded(result) {
    return {type: "GET_MAIN_RECENTLY_ADDED", result}
}

我的NavBar组件

import React, {Component} from 'react';
import {
    Collapse,
    Navbar,
    NavbarToggler,
    NavbarBrand,
    Nav,
    NavItem
} from 'reactstrap';
import {NavLink} from 'react-router-dom';
import cityicon from '../assets/images/city/Bangalore.png';
import {instanceOf} from 'prop-types';
import {withCookies, Cookies} from 'react-cookie';

class NavBar extends Component {

    constructor(props) {
        super(props);

        this.toggle = this
            .toggle
            .bind(this);

        this.toggleHidden = this
            .toggleHidden
            .bind(this);

        this.closeOverlay = this
            .closeOverlay
            .bind(this);

        this.escFunction = this
            .escFunction
            .bind(this);

        this.handleClick = this
            .handleClick
            .bind(this);

        this.state = {
            isOpen: false,
            isHidden: true,
            isLocation: false,
            city: "Select your city",
            classname: "city-section"
        };
    }

    static propTypes = {
        cookies: instanceOf(Cookies).isRequired
    };

    componentWillMount() {

        const {cookies} = this.props;
        let newstate = (cookies.get('place') != null)
            ? true
            : false;
        if (newstate) {
            this.setState({
                city: cookies.get('place')
            });
        }
        this.setState({isLocation: newstate, isHidden: newstate});

    }

    toggle() {
        this.setState({
            isOpen: !this.state.isOpen
        });
    }

    toggleHidden() {
        this.setState({
            isHidden: !this.state.isHidden
        });
    }

    closeOverlay() {
        this.setState({
            isHidden: !this.state.isHidden
        });
    }

    escFunction(event) {
        if (event.keyCode === 27) {
            if (this.state.isHidden === false) {
                this.closeOverlay();
            }

        }
    }

    handleClick(selectedCity) {
        const {cookies} = this.props;
        cookies.set("place", selectedCity);
        this
            .props
            .rerenderfun();

        if (this.state.isLocation) {
            this.setState({
                isHidden: !this.state.isHidden,
                city: cookies.get('place')
            })
        } else {
            this.setState({
                isLocation: !this.state.isLocation,
                isHidden: !this.state.isHidden,
                city: cookies.get('place')
            })
        }
    }

    render() {

        var overlayClass = 'city-section';
        if (!this.state.isLocation) {
            overlayClass += " city-section visible"
        } else if (this.state.isHidden) {
            overlayClass += " city-section hidden";
        } else {
            overlayClass += " city-section visible"
        }

        return (
            <div>
                <Navbar className="navbar navbar-expand-md navbar-dark" light expand="md">
                    <NavbarBrand href="/">
                        <i className="fa fa-graduation-cap mr-2" aria-hidden="true"></i>Company
                    </NavbarBrand>
                    <NavbarToggler onClick={this.toggle}/>
                    <Collapse isOpen={this.state.isOpen} navbar>
                        <Nav className="ml-auto" navbar>
                            <NavItem>
                                <NavLink className="nav-link" to="/">Favourites</NavLink>
                            </NavItem>
                            <NavItem>
                                <NavLink to="/" className="nav-link">Login</NavLink>
                            </NavItem>
                            <NavItem>
                                <a className="nav-link" onClick={() => this.toggleHidden()}>
                                    <i className="fa fa-map-marker mr-2" aria-hidden="true"></i>{this.state.city}</a>
                            </NavItem>
                        </Nav>
                    </Collapse>
                </Navbar>

                <div className={overlayClass} onClick={this.closeOverlay}>
                    <div
                        className="city-content py-5"
                        onClick={(e) => {
                        e.stopPropagation();
                    }}>
                        <div className="container">
                            <div className="row text-center">
                                <div className="col-12">
                                    <h4 className="text-secondary">Select your City</h4>
                                </div>
                            </div>

                            {Object
                                .entries(this.props.placelist.result)
                                .map(([k, value]) => {
                                    return (
                                        <div className="row text-center mt-5" key={k}>
                                            <div className="col-md-2 offset-md-5">
                                                <div
                                                    className="card border-0 location-card"
                                                    onClick={() => {
                                                    this.handleClick(value.city)
                                                }}>
                                                    <div className="card-body">
                                                        <img className="location-img" src={cityicon} alt="bangalore"/>
                                                        <p className="font-weight-bold mt-3 mb-0">{value.city}</p>
                                                    </div>
                                                </div>
                                            </div>
                                        </div>
                                    )
                                })}

                            <div className="row text-center pt-5">
                                <div className="col-12">
                                    <h6 className="text-secondary mt-3 font-italic">Currently we are only in Bangalore</h6>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        );
    }
}

export default withCookies(NavBar);

2 个答案:

答案 0 :(得分:0)

您需要将loadInfoData bind传递给dispatch到navbar组件。您也可以为导航栏编写mapdispatchtoProps。

handleClick(selectedCity) {
        const {cookies, loadInfoData} = this.props;
        loadInfoData(selectedCity)
        cookies.set("place", selectedCity);
        this
            .props
            .rerenderfun();

        if (this.state.isLocation) {
            this.setState({
                isHidden: !this.state.isHidden,
                city: cookies.get('place')
            })
        } else {
            this.setState({
                isLocation: !this.state.isLocation,
                isHidden: !this.state.isHidden,
                city: cookies.get('place')
            })
        }
    }
// add below code for navbar component
const mapDispatchToProps = (dispatch) => {
    return {
        loadInfoData: (selectedCity) => {
            dispatch(loadInfoData(selectedCity));
        }
    };
};

export default connect(null, mapDispatchToProps)(NavBar)

答案 1 :(得分:0)

首先,很难用你所分享的代码提供一个确凿的答案,我的意思是你如何编写减速器和所有代码。

尽管如此,我可以建议一些有关react和redux架构的最佳实践。就像你的情况一样,父组件和子组件都需要连接到redux商店吗?当父组件(Home)连接并重新渲染时,子组件(HomePage)可以只接收带有props的值。

对于进一步的研究,我建议进行以下操作。

https://github.com/reactjs/redux/issues/585

https://redux.js.org/docs/Troubleshooting.html

个人建议或说明:

事情是有很多最佳实践和标准,没有一个是绝对的或一个统治它们所有类型。它完全取决于您的需求和应用程序架构。但你需要的是掌握一些概念,如智能和愚蠢的组件或容器和表示组件,提升状态(反应文档最好地解释它)。试着让你了解这些概念。 React是一个非常精心设计的库,如果你巧妙地构建你的应用程序,这些问题甚至都不会出现。

了解整个应用程序并将其划分为尽可能小的占用空间的许多组件是我认为的关键。

首先,您可以首先设计应用程序的整个状态树(对象结构),如果不可能,那么在某种程度上通过查看该状态树来设计组件层次结构,您会发现一切都落入了地方。 / p>