Espandi / Comprimi tutti i dati
Sto facendo una fisarmonica e quando clicchiamo su ogni singolo elemento, si apre o si chiude bene.
Ora ho implementato l'opzione Espandi tutto o Comprimi tutto per far espandere / comprimere tutte le fisarmoniche.
Accordion.js
const accordionArray = [
{ heading: "Heading 1", text: "Text for Heading 1" },
{ heading: "Heading 2", text: "Text for Heading 2" },
{ heading: "Heading 3", text: "Text for Heading 3" }
];
.
.
.
{accordionArray.map((item, index) => (
<div key={index}>
<Accordion>
<Heading>
<div className="heading-box">
<h1 className="heading">{item.heading}</h1>
</div>
</Heading>
<Text expandAll={expandAll}>
<p className="text">{item.text}</p>
</Text>
</Accordion>
</div>
))}
E text.js è un file in cui sto eseguendo l'azione per aprire qualsiasi contenuto particolare della fisarmonica e del codice come segue,
import React from "react";
class Text extends React.Component {
render() {
return (
<div style={{ ...this.props.style }}>
{this.props.expandAll ? (
<div className={`content open`}>
{this.props.render && this.props.render(this.props.text)}
</div>
) : (
<div className={`content ${this.props.text ? "open" : ""}`}>
{this.props.text ? this.props.children : ""}
{this.props.text
? this.props.render && this.props.render(this.props.text)
: ""}
</div>
)}
</div>
);
}
}
export default Text;
Qui tramite this.props.expandAllottengo il valore se expandAll è vero o falso. Se è vero, tutte le fisarmoniche avranno la classe, className={`content open`}quindi verranno aperte tutte.
Problema:
La openclasse viene applicata ma il contenuto del testo interno non viene visualizzato.
Quindi questa linea non funziona,
{this.props.render && this.props.render(this.props.text)}
Requisiti:
Se si fa clic sul pulsante Espandi tutto / Comprimi tutto, tutte le fisarmoniche dovrebbero essere rispettivamente aperte / chiuse.
Questo dovrebbe funzionare indipendentemente dalla fisarmonica aperta / chiusa in precedenza. Quindi, se Espandi tutto, dovrebbe aprire tutta la fisarmonica oppure chiudere tutta la fisarmonica anche se era stata aperta / chiusa in precedenza.
Collegamenti:
Questo è il collegamento del file https://codesandbox.io/s/react-accordion-forked-sm5fw?file=/src/GetAccordion.js dove gli oggetti di scena sono effettivamente tramandati.
Modificare:
Se uso {this.props.children}allora ogni fisarmonica viene aperta .. Nessun problema.
Ma se apro manualmente una fisarmonica facendo clic su un elemento particolare, se faccio clic su espandi tutto, allora è espanso (previsto) ma se faccio clic su Indietro Comprimi tutto l'opzione allora non tutte le fisarmoniche sono chiuse .. Quelle che abbiamo aperto in precedenza sono ancora in stato aperto .. Ma il comportamento previsto qui è che tutto dovrebbe essere chiuso.
Risposte
Nel tuo file text.js
alla riga numero 9. sostituire il codice precedente con: {this.props.children}
Ho provato nella sandbox e ha funzionato per me.
/// non posso aggiungere un commento così modificando la risposta stessa. Accordian.js contiene il tuo hook expandAll e l'intestazione boolean sta già accadendo GetAccordian.js. Suggerisco di spostare l'espandi tutto su GetAccordian.js in modo da poter controllare entrambi i valori.
in questo caso this.props.render non è una funzione e this.props.text non è definito, prova a sostituire questa riga
<div className={`content open`}>
{this.props.render && this.props.render(this.props.text)}
</div>
da questo:
<div className={`content open`}>
{this.props.children}
</div>
EDIT: // Un'altra soluzione è passare la proprietà expandAll al componente Accordion
<Accordion expandAll={expandAll}>
<Heading>
<div className="heading-box">
<h1 className="heading">{item.heading}</h1>
</div>
</Heading>
<Text>
<p className="text">{item.text}</p>
</Text>
</Accordion>
quindi in getAccordion.js
onShow = (i) => {
this.setState({
active: this.props.expandAll ? -1: i,
reserve: this.props.expandAll ? -1: i
});
if (this.state.reserve === i) {
this.setState({
active: -1,
reserve: -1
});
}
};
render() {
const children = React.Children.map(this.props.children, (child, i) => {
return React.cloneElement(child, {
heading: this.props.expandAll || this.state.active === i,
text: this.props.expandAll || this.state.active + stage === i,
onShow: () => this.onShow(i)
});
});
return <div className="accordion">{children}</div>;
}
};
Partendo dalla risposta di @lissettdm, non mi è chiaro il motivo getAccordione accordionsono due entità separate. Potresti avere una ragione molto valida per la separazione, ma il fatto che gli stati dei due componenti siano interdipendenti suggerisce che potrebbero essere implementati meglio come un unico componente.
Accordion ora controlla direttamente lo stato dei suoi figli, come prima, ma senza usare getAccordion. La commutazione di expandAll ora ripristina anche gli stati dei singoli elementi.
const NormalAccordion = () => {
const accordionArray = [ //... your data ];
const [state, setState] = useState({
expandAll: false,
...accordionArray.map(item => false),
});
const handleExpandAll = () => {
setState((prevState) => ({
expandAll: !prevState.expandAll,
...accordionArray.map(item => !prevState.expandAll),
}));
};
const handleTextExpand = (id) => {
setState((prevState) => ({
...prevState,
[id]: !prevState[id]
}));
};
return (
<>
<div className="w-full text-right">
<button onClick={handleExpandAll}>
{state.expandAll ? `Collapse All` : `Expand All`}
</button>
</div>
<br />
{accordionArray.map((item, index) => (
<div key={index}>
<div className="accordion">
<Heading handleTextExpand={handleTextExpand} id={index}>
<div className="heading-box">
<h1 className="heading">{item.heading}</h1>
</div>
</Heading>
<Text shouldExpand={state[index]}>
<p className="text">{item.text}</p>
</Text>
</div>
</div>
))}
</>
);
};
L'intestazione restituisce l'indice in modo che il componente padre sappia quale elemento disattivare.
class Heading extends React.Component {
handleExpand = () => {
this.props.handleTextExpand(this.props.id);
};
render() {
return (
<div
style={ //... your styles}
onClick={this.handleExpand}
>
{this.props.children}
</div>
);
}
}
Al testo interessa solo un puntello per determinare se deve visualizzare il contenuto di espansione.
class Text extends React.Component {
render() {
return (
<div style={{ ...this.props.style }}>
<div
className={`content ${this.props.shouldExpand ? "open" : ""}`}
>
{this.props.shouldExpand ? this.props.children : ""}
</div>
</div>
);
}
}