更新 React 组件共享状态

Update React components sharing state

我有两个相互关联的项目列表。 B 列表有复选框。当我取消选中 B 列表的一个元素时,A 列表的相应元素应该被隐藏,但是 不幸的是渲染函数没有被调用。我使用减速器更改应用程序的状态。

我的问题是在更改状态后未调用渲染。由于点击事件由 B 列表处理,我可以 forceUpdate 该列表,但 A 列表将保持不变。

我该如何处理这个连接?

可选Links部分(列表)

import React, { PropTypes, Component } from 'react'
import Link from '../components/Link'
import LinkSelector from '../components/LinkSelector'
import {Popover, OverlayTrigger} from "react-bootstrap"

class SelectableLinksSection extends Component {

    renderPopup() {
        return (
            <Popover id="idea-popup" className="imageP">
                <LinkSelector links={this.props.links} showLink={this.props.showLink} hideLink={this.props.hideLink}/>
            </Popover>
        )
    }

    render() {
        if (this.props.links.length) {
            return (
                <div>
                    <div className="col-sm-7">
                        <div className={this.props.cssClass}>
                            {
                                this.props.links.map(link => {
                                    return <Link url={link.url} label={link.label} icon={link.icon} hide={link.hide}
                                                 text={link.text} widthClass="col-sm-4" dividerClass="divider"/>
                                })
                            }
                        </div>
                    </div>
                    <section className="col-sm-2 no-padding">
                        <div className="col-sm-12">
                            <div className="text-center add">
                                <OverlayTrigger trigger="click" rootClose placement="bottom"
                                                overlay={this.renderPopup()}>
                                    <a role="button" tabIndex="0" title="#i18n:dashboard.ideas.addLink#">
                                    </a>
                                </OverlayTrigger>
                            </div>
                        </div>
                    </section>
                </div>
            )
        } else {
            return <div></div>
        }
    }
}

SelectableLinksSection.propTypes = {
    cdn: PropTypes.string.isRequired,
    title: PropTypes.string.isRequired,
    text: PropTypes.string.isRequired,
    links: PropTypes.array.isRequired,
    cssClass: PropTypes.string.isRequired,
    showLink: PropTypes.func.isRequired,
    hideLink: PropTypes.func.isRequired
}

export default SelectableLinksSection;

Link选择器(B 列表)

import React, { PropTypes, Component } from 'react'
import Links from '../components/Links'

class LinkSelector extends Component {

    constructor(props) {
        super(props);
        this.handleChange = this.handleChange.bind(this)
    }

    handleChange(link) {
        if (link.hide) {
            this.props.showLink(link.id)
        } else {
            this.props.hideLink(link.id)
        }
        this.forceUpdate()
    }

    render() {
        return (
            <div className="row">
                {this.props.links.map((link)=> {
                    return (
                        <div key={"link-"+link.id} className="col-sm-3 text-center">
                            <div className="imgContainer"
                                 style={{background:"url('"+link.icon+"') no-repeat center"}}>
                                <input type="checkbox" checked={!link.hide}
                                       onChange={() => {this.handleChange(link)}}/>
                            </div>

                            <div className="title">{link.label}</div>
                        </div>
                    )
                })}
            </div>
        )
    }
}

LinkSelector.propTypes = {
    links: PropTypes.array.isRequired,
    showLink: PropTypes.func.isRequired,
    hideLink: PropTypes.func.isRequired
}

export default LinkSelector;

Link(列表项)

import React, { PropTypes, Component } from 'react'

class Link extends Component {
    render() {
        return (
            <div className={this.props.widthClass} style={this.props.hide ? {display: "none"} : {}}>
                <div className="text-center">
                    <a href={this.props.url}><img alt={this.props.label} src={this.props.icon} /></a>
                </div>
                <div className={this.props.dividerClass}></div>
                <div className="title"><a href={this.props.url}>{this.props.label}</a></div>
                <div className="text" dangerouslySetInnerHTML={{__html: this.props.text}}></div>
            </div>
        )
    }
}

Link.propTypes = {
    label: PropTypes.string.isRequired,
    icon: PropTypes.string.isRequired,
    url: PropTypes.string.isRequired,
    text: PropTypes.string.isRequired,
    hide: PropTypes.bool.isRequired,
    widthClass: PropTypes.string.isRequired,
    dividerClass: PropTypes.string.isRequired
}

export default Link;

[编辑] SectionIdeas(列表容器)

import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux';

import SelectableLinksSection from '../components/SelectableLinksSection'

class SectionIdeas extends Component {
    render() {
        return <SelectableLinksSection title={this.props.title} text={this.props.text} links={this.props.links}
                                       cdn={this.props.cdn} cssClass="row virtual list" showLink={this.props.show} hideLink={this.props.hide} />
    }
}

SectionIdeas.propTypes = {
    cdn: PropTypes.string.isRequired,
    title: PropTypes.string.isRequired,
    text: PropTypes.string.isRequired,
    links: PropTypes.array.isRequired,
    show: PropTypes.func.isRequired,
    hide: PropTypes.func.isRequired
};

function mapStateToProps(state) {
    return {
        cdn: state.data.cdn,
        title: state.data.ideas.title,
        text: state.data.ideas.text,
        links: state.data.ideas.links
    };
}

export default connect(mapStateToProps)(SectionIdeas)

[编辑] 主容器

import React, { Component, PropTypes } from 'react'
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';

import SectionIdeas from './SectionIdeas'

import { readStateFromServer, setStateToServer } from '../actions/api';
import * as ideasActions from '../actions/ideas';

class App extends Component {
    constructor(props) {
        super(props);
        props.dispatch(readStateFromServer());
    }

    render() {
        const {dispatch} = this.props
        const ideasBoundActions = bindActionCreators(ideasActions, dispatch)

        dispatch(setStateToServer())

        if (this.props.loaded) {
            return (
                <div>
                    <SectionIdeas {...ideasBoundActions} />
                </div>
            )
        } else {
            return (
                <div className="loading">
                    #i18n:Loading#
                </div>
            )
        }
    }
}

App.propTypes = {
    loaded: PropTypes.bool.isRequired,
    dispatch: PropTypes.func.isRequired
};

function mapStateToProps(state) {
    return {
        loaded: state.data.loaded
    };
}

export default connect(mapStateToProps)(App);

[编辑] 动作

export const SHOW = 'SHOW_LINKS'
export const HIDE = 'HIDE_LINKS'

export function show(id) {
    return {
        type: SHOW,
        id: id
    }
}

export function hide(id) {
    return {
        type: HIDE,
        id: id
    }
}

减速器

import { SHOW, HIDE } from '../actions/ideas'
import { RECEIVE_STATE } from '../actions/api'

export default function ideas(state = {}, action = "") {
    switch (action.type) {
        case RECEIVE_STATE:
            return action.json && action.json.data && action.json.data.ideas ? action.json.data.ideas : state
        case SHOW:
            return toggle(state, action.id, true)
        case HIDE:
            return toggle(state, action.id, false)
        default:
            return state
    }
}

function toggle(state, id, show) {
    let newState = Object.assign({}, state)
    newState.links.forEach((link)=> {
        if (link.id === id) {
            link.hide = !show
        }
    })
    return newState
}

状态样本

 {
    "title":"Idee per insegnare in digitale",
    "text":"Da qui puoi consultare siti di approfondimento",
    "links":[
        {
            "id":1,
            "icon":"http:\/\/cdn-my.zanichelli.local\/fe\/images\/dashboard\/spazio-clil.png",
            "url":"http:\/\/online.scuola.zanichelli.it\/spazioclil\/",
            "label":"Spazio CLIL",
            "text":"",
            "hide":false
        },
        {
            "id":2,
            "icon":"http:\/\/cdn-my.zanichelli.local\/fe\/images\/dashboard\/invalsi-myzanichelli.png",
            "url":"http:\/\/online.scuola.zanichelli.it\/quartaprova\/",
            "label":"Invalsi",
            "text":"",
            "hide":false
        },
        {
            "id":3,
            "icon":"http:\/\/cdn-my.zanichelli.local\/fe\/images\/dashboard\/aula-scienze-myzanichelli.png",
            "url":"http:\/\/aulascienze.scuola.zanichelli.it\/",
            "label":"Aula di Scienze",
            "text":"",
            "hide":false
        },
        {
            "id":4,
            "icon":"http:\/\/cdn-my.zanichelli.local\/fe\/images\/dashboard\/guida-myzanichelli.png",
            "url":"http:\/\/altro.scuola.zanichelli.it",
            "label":"Altro",
            "text":"",
            "hide":true
        }
    ]
}

一种可能的解决方案(希望不是最好的)是将 handleChange 函数从 LinkSelector 组件(B 列表)移动到其父组件 SelectableLinksSection(也包含 A 列表)并通过作为 属性 到 LinkSelector 的函数。这样 forceUpdate 函数强制两个列表的渲染方法