cách hiển thị các thành phần phản ứng bằng cách sử dụng bản đồ và nối


102

Tôi có một thành phần sẽ hiển thị Mảng chuỗi. Mã trông như thế này.

React.createClass({
  render() {
     <div>
        this.props.data.map(t => <span>t</span>)
     </div>
  }
})

Nó đang hoạt động hoàn toàn tốt. tức là nếu props.data = ['tom', 'jason', 'chris'] Kết quả hiển thị trong trang sẽ là tomjasonchris

Sau đó, tôi muốn nối tất cả các tên bằng cách sử dụng dấu phẩy, vì vậy tôi thay đổi mã thành

this.props.data.map(t => <span>t</span>).join(', ')

Tuy nhiên, kết quả trả về là [Đối tượng], [Đối tượng], [Đối tượng].

Tôi không biết cách diễn giải đối tượng để trở thành các thành phần phản ứng được hiển thị. Bất kì lời đề nghị nào ?

Câu trả lời:


147

Một giải pháp đơn giản là sử dụng reduce()mà không có đối số thứ hai và không lan truyền kết quả trước đó:

class List extends React.Component {
  render() {
     <div>
        {this.props.data
          .map(t => <span>{t}</span>)
          .reduce((prev, curr) => [prev, ', ', curr])}
     </div>
  }
}

Không có đối số thứ hai, reduce()sẽ bắt đầu ở chỉ mục 1 thay vì 0 và React hoàn toàn hài lòng với các mảng lồng nhau.

Như đã nói trong các nhận xét, bạn chỉ muốn sử dụng điều này cho các mảng có ít nhất một mục, bởi vì reduce()nếu không có đối số thứ hai sẽ ném với một mảng trống. Thông thường, điều này sẽ không thành vấn đề, vì dù sao thì bạn cũng muốn hiển thị một thông báo tùy chỉnh như 'cái này trống' cho các mảng trống.

Cập nhật cho bản ghi

Bạn có thể sử dụng điều này trong Typecript (không có kiểu không an toàn any) với React.ReactNodetham số kiểu trên .map():

class List extends React.Component {
  render() {
     <div>
        {this.props.data
          .map<React.ReactNode>(t => <span>{t}</span>)
          .reduce((prev, curr) => [prev, ', ', curr])}
     </div>
  }
}

3
If the array is empty and no initialValue was provided, TypeError would be thrown.. vì vậy nó sẽ không hoạt động đối với mảng trống.
Eugene Krevenets

6
Cũng lưu ý rằng điều này tổ chức một cách đệ quy prevmảng. ví dụ: [1, 2, 3, 4]trở thành [[[1,",",2],",",3],",",4].
Tamlyn 14/02/17

2
@Tamlyn: Bạn có nghĩ rằng đề cập ban đầu của tôi về quan điểm của bạn trong câu trả lời ('React hoàn toàn hài lòng với các mảng lồng nhau') là đủ rõ ràng hay tôi nên giải thích thêm về điều này?
Maarten ter Horst

1
Có ai đã làm cho giải pháp này hoạt động với React + TypeScript không?
Matt Dell

1
@Jstuff tôi đã ăn phải sử dụng bất kỳ. .reduce((prev: JSX.Element, current: JSX.Element): any => [prev, (<span>&nbsp;</span>), current])
Matt Dell

26

Bạn có thể sử dụng reduceđể kết hợp nhiều phần tử của một mảng:

React.createClass({
  render() {
     <div>
        this.props.data
        .map(t => <span>t</span>)
        .reduce((accu, elem) => {
            return accu === null ? [elem] : [...accu, ',', elem]
        }, null)
     </div>
  }
})

Thao tác này khởi tạo bộ tích lũy bằng null, vì vậy chúng ta có thể bọc mục đầu tiên trong một mảng. Đối với mỗi phần tử sau trong mảng, chúng tôi xây dựng một mảng mới chứa tất cả các phần tử trước đó bằng cách sử dụng ...-operator, thêm dấu phân tách và sau đó là phần tử tiếp theo.

Array.prototype.reduce ()


2
cảm ơn, chính xác những gì tôi cần. Bạn có thể loại bỏ kiểm tra null và kiểm tra bậc ba bằng cách khởi tạo bộ tích lũy dưới dạng một mảng trống
Larry

7
@Larry nếu bạn loại bỏ dấu kiểm null và dấu bậc ba và chuyển [] làm trình khởi tạo, làm cách nào để bạn tránh dấu phẩy ở đầu? ['a'].reduce((a, e) => [...a, ',', e],[])lợi tức [",", "a"]. Việc chuyển không có trình khởi tạo nào hoạt động trừ khi mảng trống, trong trường hợp đó, nó trả về một TypeError.
Guy

@Guy bạn hoàn toàn chính xác - lỗi của tôi là tôi đã nối các giá trị với dấu cách và thiếu khoảng trống trong đầu ra được kết xuất
Larry

12

Cập nhật với React 16: Giờ đây, có thể hiển thị các chuỗi trực tiếp, vì vậy bạn có thể đơn giản hóa mã của mình bằng cách xóa tất cả các <span>thẻ vô dụng .

const list = ({ data }) => data.reduce((prev, curr) => [ prev, ', ', curr ]);

8

Câu trả lời được chấp nhận thực sự trả về một mảng của mảng vì prevmỗi lần sẽ là một mảng. React đủ thông minh để thực hiện điều này, nhưng nó có dễ gây ra các vấn đề trong tương lai như phá vỡ thuật toán khác biệt của React khi đưa ra các khóa cho mỗi kết quả của bản đồ.

React.FragmentTính năng mới cho phép chúng tôi thực hiện điều này một cách dễ hiểu mà không gặp các vấn đề cơ bản.

class List extends React.Component {
  render() {
     <div>
        {this.props.data
          .map((t, index) => 
            <React.Fragment key={index}>
              <span> {t}</span> ,
            </React.Fragment>
          )
      </div>
  }
}

Với việc React.Fragmentchúng ta chỉ cần đặt dấu phân tách ,bên ngoài HTML được trả về và React sẽ không phàn nàn.


2
Giải pháp tuyệt vời, nhưng bạn cần phải loại bỏ dấu phẩy cho các phần tử cuối cùng của mảng
indapublic

Bạn luôn có thể sử dụng sơ đồ và chỉ mục để giải quyết điều đó.
Jstuff

2
Bạn có thể xóa dấu phẩy cuối cùng với sửa đổi này: <React.Fragment key = {index}> <span> {t} </span> {index === this.props.data.length - 1? '': ','} </React.Fragment>
JohnP

7

các <span>{t}</span>bạn đang quay trở lại là một đối tượng, không phải là một chuỗi. Kiểm tra tài liệu phản ứng về nó https://facebook.github.io/react/docs/jsx-in-depth.html#the-transform

Bằng cách sử dụng .join()trên mảng trả về từ map, bạn đang nối mảng đối tượng.[object Object], ...

Bạn có thể đặt dấu phẩy bên trong <span></span>để nó được hiển thị theo cách bạn muốn.

  render() {
    return (
      <div>
        { this.props.data.map(
            (t,i) => <span>{t}{ this.props.data.length - 1 === i ? '' : ','} </span>
          )
        }
      </div>
    )
  }

mẫu: https://jsbin.com/xomopahalo/edit?html,js,output


3

Biến thể của tôi:

{this.props.data
    .map(item => <span>{item}</span>)
    .map((item, index) => [index > 0 && ', ', item ])}

2

Nếu tôi chỉ muốn hiển thị một mảng các thành phần được phân tách bằng dấu phẩy, tôi thường thấy reducequá dài dòng. Một giải pháp ngắn hơn trong những trường hợp như vậy là

{arr.map((item, index) => (
  <Fragment key={item.id}>
    {index > 0 && ', '}
    <Item {...item} />
  </Fragment>
))}

{index > 0 && ', '} sẽ hiển thị một dấu phẩy theo sau bởi một khoảng trắng ở phía trước của tất cả các mục mảng ngoại trừ mục đầu tiên.

Nếu bạn muốn tách mục thứ hai đến mục cuối cùng và mục cuối cùng bằng một thứ khác, hãy nói chuỗi ' and ', bạn có thể thay thế {index > 0 && ', '}bằng

{index > 0 && index !== arr.length - 1 && ', '}
{index === arr.length - 1 && ' and '}

2

Sử dụng es6, bạn có thể làm điều gì đó như:

const joinComponents = (accumulator, current) => [
  ...accumulator, 
  accumulator.length ? ', ' : '', 
  current
]

Và sau đó chạy:

listComponents
  .map(item => <span key={item.id}> {item.t} </span>)
  .reduce(joinComponents, [])

1

Sử dụng mảng lồng nhau để giữ "," bên ngoài.

  <div>
      {this.props.data.map((element, index) => index == this.props.data.length - 1 ? <span key={index}>{element}</span> : [<span key={index}>{element}</span>, ", "])}
  </div>

Tối ưu hóa nó bằng cách lưu dữ liệu vào mảng và sửa đổi phần tử cuối cùng thay vì kiểm tra xem phần tử cuối cùng của nó luôn luôn.

  let processedData = this.props.data.map((element, index) => [<span key={index}>{element}</span>, ", "])
  processedData [this.props.data.length - 1].pop()
  <div>
      {processedData}
  </div>

0

Điều này đã làm việc cho tôi:

    {data.map( ( item, i ) => {
                  return (
                      <span key={i}>{item.propery}</span>
                    )
                  } ).reduce( ( prev, curr ) => [ prev, ', ', curr ] )}

0

Như đã đề cập bởi Pith , React 16 cho phép bạn sử dụng các chuỗi trực tiếp nên việc gói các chuỗi trong thẻ span không còn cần thiết nữa. Dựa trên câu trả lời của Maarten , nếu bạn cũng muốn xử lý một thông báo tùy chỉnh ngay lập tức (và tránh gây ra lỗi trên mảng trống), bạn có thể dẫn đầu hoạt động với câu lệnh ternary if trên thuộc tính length trên mảng. Nó có thể trông giống như sau:

class List extends React.Component {
  const { data } = this.props;

  render() {
     <div>
        {data.length
          ? data.reduce((prev, curr) => [prev, ', ', curr])
          : 'No data in the array'
        }
     </div>
  }
}

0

Điều này sẽ trả về một mảng phẳng. Xử lý trường hợp s với đối tượng đầu tiên không thể lặp lại bằng cách cung cấp một mảng trống ban đầu và lọc dấu phẩy đầu tiên không cần thiết khỏi kết quả

[{}, 'b', 'c'].reduce((prev, curr) => [...prev, ', ', curr], []).splice(1) // => [{}, 'b', 'c']

0

Tôi có phải là người duy nhất nghĩ rằng có rất nhiều sự lan truyền và làm tổ không cần thiết đang diễn ra trong các câu trả lời ở đây? Điểm cho sự ngắn gọn, chắc chắn, nhưng nó mở ra cánh cửa cho các vấn đề về quy mô hoặc React thay đổi cách chúng xử lý với các mảng / mảnh lồng nhau.

const joinJsxArray = (arr, joinWith) => {
  if (!arr || arr.length < 2) { return arr; }

  const out = [arr[0]];
  for (let i = 1; i < arr.length; i += 1) {
    out.push(joinWith, arr[i]);
  }
  return out;
};

// render()
<div>
  {joinJsxArray(this.props.data.map(t => <span>t</span>), ', ')}
</div>

Một mảng, không lồng nhau. Không có chuỗi phương thức sexy nào cả, nhưng nếu bạn thấy mình làm điều này thường xuyên, bạn luôn có thể thêm nó vào nguyên mẫu mảng hoặc bọc nó trong một hàm sử dụng lệnh gọi lại ánh xạ để thực hiện tất cả trong một lần.


0
function YourComponent(props) {

  const criteria = [];

  if (something) {
    criteria.push(<strong>{ something }</strong>);
  }

  // join the jsx elements with `, `
  const elements = criteria.reduce((accu, elem) => {
    return accu === null ? [elem] : [...accu, ', ', elem]
  }, null);

  // render in a jsx friendly way
  return elements.map((el, index) => <React.Fragment key={ index }>{ el }</React.Fragment> );

}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.