简体   繁体   中英

Update React components sharing state

I have two list of items which are connected. The B list has checkboxes. When I uncheck an element of the B list, the corresponding element of A list should be hidden, but unfortunately the render function is not invoked . I change state of the application using reducers.

My problem is that render is not invoked after changing the state. Since click event is handled by B list I can forceUpdate that list, but A list will remain unchanged.

How can I handle this connection?

SelectableLinksSection (A list)

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;

LinkSelector (B List)

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 (A List item)

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;

[EDIT] SectionIdeas (A List container)

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)

[EDIT] Main container

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);

[EDIT] Actions

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
    }
}

Reducer

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
}

State sample

 {
    "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
        }
    ]
}

A possible solution (hopefully not the best one) is to move the handleChange function from LinkSelector component (B List) to its parent SelectableLinksSection (wich also contains A List) and to pass the function as a property to LinkSelector . In this way the forceUpdate function force the render methods of both lists

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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