Search code examples
cssreactjsreact-window

React: Rendering Css cards with react-window and react-window-infinite-loader


I'm working on a React project where I'm using the libraries react-window and react-window-infinite-loader to render a large list of project cards. Each project card contains an image, which is stored as a list of strings. I want to display these images in a element using

  • items within each project card.

    I have defined a component called Item to render each project card. Within Item, I'm trying to render the list of images using a and

  • structure. However, I'm facing some issues with the layout and positioning of the images within the project cards. Additionally, when the list is long, some list items go under the project cards.

    The issues I'm facing are:

    The images are not being rendered properly within the project cards. Some project names are overlapping with the images, and the buttons are not positioned correctly. When the list of projects is long, some list items go under the project cards, causing them to be partially hidden. I've already tried adjusting the CSS styles and using flexbox for layout, but the issues persist. I suspect there might be some conflict between the react-window and flexbox styles.

    Has anyone faced a similar issue while using react-window and react-window-infinite-loader to render a list with images as a list of strings? How can I properly render the images as a list within each project card and ensure the correct positioning of the buttons? Additionally, how can I prevent the list items from going under the project cards when the list is long?

    I would greatly appreciate any guidance or suggestions to resolve these issues.

    here is code-sandbox's sample example

    Here is React component code:

    {filteredProjects.length === 0 && isSearchMode ? (
            <div className="d-flex justify-content-center align-items-center">
              <h3>No projects found or project is still loading</h3>
            </div>
          ) : (
            <InfiniteLoader
              isItemLoaded={isItemLoaded}
              itemCount={itemCount}
              loadMoreItems={fetchNextProjects}
            >
              {({ onItemsRendered, ref }) => (
                <FixedSizeList
                  height={600}
                  itemCount={itemCount}
                  itemSize={150}
                  onItemsRendered={onItemsRendered}
                  ref={ref}
                  width={1400}
                >
                  {Item}
                </FixedSizeList>
              )}
            </InfiniteLoader>
          )}
    
    /// This is the Item:
      const Item = memo(
        ({ index, style }: { index: number; style: React.CSSProperties }) => {
          if (!isItemLoaded(index)) {
            return (
              <div style={style} className="loader">
                <Spinner animation="border" variant="primary" />
              </div>
            );
          }
    
          const project = projects[index];
          return (
            <div style={style} className="list-group-item">
              <div className="details">
                <div className="info">
                  <p className="number">{project.projectName + 1}</p>
                  <p className="project-name">
                    {highlightSearchTerm(project.projectName, searchTerm)}
                  </p>{" "}
                  {/* Added class for project name */}
                  <p>Use image:</p>
                  <ul>
                    {project.images.map((image: any, i: any) => (
                      <li key={i}>
                        <p className="image">
                          {highlightSearchTerm(image.image, searchTerm)}
                        </p>{" "}
                        {/* Added class for image */}
                      </li>
                    ))}
                  </ul>
                  <div className="buttons">
                    {" "}
                    {/* Added wrapper div for buttons */}
                    {visibleImages < project.images.length && (
                      <Button
                        variant="secondary"
                        onClick={handleLoadMore}
                        className="load-button"
                      >
                        Load More
                      </Button>
                    )}
                    <Button
                      variant="primary"
                      onClick={() => handleGitLabButtonClick(project.gitlabUrl)}
                      className="repo-button"
                    >
                      Repo
                    </Button>
                  </div>
                </div>
              </div>
            </div>
          );
        },
        areEqual
      );

    This is the css

    .title {
      display: flex;
      margin-bottom: 20px;
    }
    
    .title h1 {
      color: white;
      font-weight: normal;
      font-size: 24px;
      font-family: avenir next, avenir, sans-serif;
    }
    
    .list-container {
      border: 2px solid #bababa;
      border-radius: 10px;
    }
    
    .details {
      display: flex;
      justify-content: space-between;
      font-family: 'Nunito Sans', sans-serif;
      margin-top: 15px;
    }
    
    .info {
      display: flex;
      flex-direction: column;
    }
    
    .info p {
      margin: 0;
    }
    
    .list-group-item {
      border-bottom: 1px solid #fff;
      display: flex;
      margin-bottom: 10px;
      background-color: #b9e6b3;
      padding: 10px;
    }
    
    .list-group-item .number {
      color: #fff;
      font-size: 20px;
      font-weight: 700;
    }
    
    .list-group-item .avatar img {
      width: 60px;
      height: 60px;
      border-radius: 30px;
      border: 1px solid #f0f0f0;
    }
    
    .loader {
      margin-top: 40px;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    
    .project-name {
      margin-top: 10px;
      font-weight: bold;
    }
    
    .image {
      margin: 0;
    }
    
    .buttons {
      display: flex;
      justify-content: space-between; /* Adjust button alignment */
      align-items: center; /* Adjust button alignment */
      margin-top: 10px;
    }
    
    .load-button {
      margin-right: 0.5rem;
    }


  • Solution

  • In your Item you need to put style within curly brackets when you're passing it to the style attribute. In the following manner:

    const Item = memo(
        ({ index, style }: { index: number; style: React.CSSProperties }) => {
          if (!isItemLoaded(index)) {
            return (
              <div style={{style}} className="loader">
                <Spinner animation="border" variant="primary" />
              </div>
            );
          } 
    

    Following is the edited codeSandbox: https://codesandbox.io/s/crazy-chihiro-c564m4 In any case, do let me know if the change worked if/when you make it. Good luck!