Переключение определенного div (id) в компоненте react

#reactjs #react-component

#reactjs #реагирующий компонент

Вопрос:

У меня есть сайт, созданный с использованием посткомпонентов для показа статей в ленте. Внутри компонента у меня есть кнопка, которая открывает модальный onClick. Я использую useState для переключения на модальный, который работает отлично. Проблема в том, что, поскольку переключатель установлен на modal-div внутри компонента.. каждый отдельный постмодальный файл открывается всякий раз, когда я нажимаю одну из кнопок. Я хочу открывать только целевой постмодальный пост (с идентификатором sam post в качестве кнопки, на которую я нажимаю). Я не могу понять, как это сделать this…in реагируйте.

     const [toggle, setToggle] = useState (true);
      
    const toggler = () => {
        setToggle(prev => !prev)
    }


...

return (
        <section className="posts">
        {data.allMarkdownRemark.edges.map((edge) => {
            return (
                <div className="post">
                    <div className="postDescrip">
                        <h2 className="postTitle">{edge.node.frontmatter.title}</h2>
                        <h2 className="name">{edge.node.frontmatter.name}</h2>

                        <button className="readMoreBtn" onClick={toggler}>{toggle ? <h2 className="readMore">Read more</h2> : <h2 className="readMore">Read less</h2>} 
                        </button>
                    </div>
                
                    <Img className="postImg" fluid={edge.node.frontmatter.featuredImage.childImageSharp.fluid} /> 

                    <div className={toggle ? 'hide' : 'postCopy'} >
                        <Close close={toggler} />
                        <h3>{edge.node.frontmatter.details}</h3>
                        <div className="copy" dangerouslySetInnerHTML= {{__html: edge.node.html}}></div>
                        <h4>Read the full article in Issue One</h4>
                    </div>
                </div>
            )}
        )}
        </section>
    )
}
export default Posts;
 

После попытки предложенного решения используйте object вместо этого на bolean. Теперь я получаю это сообщение об ошибке
[Сообщение об ошибке] [1]для следующего кода:

     const [toggles, setToggles] = useState ({});
    
    let id;
    const createToggler = (id) = () => {
        setToggles(prev => { [id] : !prev[id] }) 
        // setToggle(prev => { ...prev, [id]: !prev[id] }) // or support multi modal at same time. but I think you don't want it.
    }
    
    const data = useStaticQuery(graphql`
        query {
            allMarkdownRemark (
                sort: { order: DESC, fields: [frontmatter___date] }
                ){
                edges {
                    node {
                        frontmatter {
                            id
                            title
                            name
                            details
                            featuredImage {
                                childImageSharp {
                                    fluid(maxWidth: 800) {
                                        ...GatsbyImageSharpFluid
                                    }
                                }
                            }
                        }
                        html
                        fields {
                            slug
                        }
                    }
                }
            }
        }
    `)

    
    
    return (
        <section className="posts">
        {data.allMarkdownRemark.edges.map((edge) => {
            const id = edge.node.frontmatter.id;
            const toggle = toggles[id];
            const toggler = createToggler(id);
            return (
                <div className="post" id={edge.node.frontmatter.id}>
                    <div className="postDescrip">
                        <h2 className="postTitle">{edge.node.frontmatter.title}</h2>
                        <h2 className="name">{edge.node.frontmatter.name}</h2>

                        <button className="readMoreBtn" onClick={toggler}>{toggle ? <h2 className="readMore">Read more</h2> : <h2 className="readMore">Read less</h2>} 
                        </button>
                    </div>
                
                    <Img className="postImg" fluid={edge.node.frontmatter.featuredImage.childImageSharp.fluid} /> 

                    <div className={toggle ? 'hide' : 'postCopy'} id={edge.node.frontmatter.id}>
                        <Close close={toggler} />
                        <h3>{edge.node.frontmatter.details}</h3>
                        <div className="copy" dangerouslySetInnerHTML= {{__html: edge.node.html}}></div>
                        <h4>Read the full article in Issue One</h4>
                    </div>
                </div>
            )}
        )}
        </section>
    )
}
export default Posts;
            


  [1]: https://i.stack.imgur.com/VhIYF.png
 

Комментарии:

1. Переместите все, что у вас есть внутри map , в отдельный компонент с его собственным индивидуальным состоянием, это будет лучше, чем отслеживать состояния переключения каждого элемента внутри родительского

Ответ №1:

вот так. используйте объект вместо одного логического значения.

     const [toggles, setToggles] = useState ({});
      
    const createToggler = (id) = () => {
        setToggle(prev => { [id]: !prev[id] }) // atmost one id is true. others is undefine or false.
        // setToggle(prev => { ...prev, [id]: !prev[id] }) // or support multi modal at same time. but I think you don't want it.
    }


...

return (
        <section className="posts">
        {data.allMarkdownRemark.edges.map((edge) => {
            const id = ... // get your id form edge.
            const toggle = toggles[id];
            const toggler = createToggler(id);
            return (
                <div className="post">
                    <div className="postDescrip">
                        <h2 className="postTitle">{edge.node.frontmatter.title}</h2>
                        <h2 className="name">{edge.node.frontmatter.name}</h2>

                        <button className="readMoreBtn" onClick={toggler}>{toggle ? <h2 className="readMore">Read more</h2> : <h2 className="readMore">Read less</h2>} 
                        </button>
                    </div>
                
                    <Img className="postImg" fluid={edge.node.frontmatter.featuredImage.childImageSharp.fluid} /> 

                    <div className={toggle ? 'hide' : 'postCopy'} >
                        <Close close={toggler} />
                        <h3>{edge.node.frontmatter.details}</h3>
                        <div className="copy" dangerouslySetInnerHTML= {{__html: edge.node.html}}></div>
                        <h4>Read the full article in Issue One</h4>
                    </div>
                </div>
            )}
        )}
        </section>
    )
}
export default Posts;
 

Ответ №2:

Я решил свою проблему следующим образом

 import React, {useState} from "react"
import Img from "gatsby-image"
import './posts.css';
import cancel from '../images/cancel.png'

const Post = ({title, name, id, image, details, html}) => {   
    const [toggle, setToggle] = useState (true);

    const toggler = () => {
        setToggle(prev => !prev)
    }

    const selectPost= (event) =>{
        let id = event.target.id,
            postCopy = document.getElementById('hide'   id);
            toggler.call(postCopy);
    } 

        
    return (
        <div className="post">
            <div className="postDescrip">
                <h2 className="postTitle">{title}</h2>
                <h2 className="name">{name}</h2>
                <button className="readMoreBtn" onClick={selectPost}>{toggle ? <h2 id={id} className="readMore">Read more</h2> : <h2 id={id} className="readMore">Read less</h2>} 
                </button>
            </div>
        
            <Img className="postImg" fluid={image} />
            <div id={'hide'   id} className={toggle ? 'hide' : 'postCopy'} >
                <button aria-label="Close" onClick={selectPost} className="closeBtn">
                    <img alt="close-button" src={cancel}/>
                </button>
                <h3>{details}</h3>
                <div className="copy" dangerouslySetInnerHTML= {html}></div>
                <a href="/#footer"><h4>Read the full article in Issue One</h4></a>
            </div>
        </div>
    )
}
export default Post;