【问题标题】:Mixing loops and React JSX混合循环和 React JSX
【发布时间】:2019-01-13 21:54:27
【问题描述】:

我是 Javascript 和 React 新手,有一个与 this one 类似的问题:

我正在学习井字游戏game 的教程,并试图了解 JSX 与循环和数组的混合。

我有这个正在工作:

class Board extends React.Component {
  renderSquare(i) {
    return (
      <Square
       value={this.props.squares[i]}
       onClick={() => this.props.onClick(i)}
      />
    )
  }
  renderRow(row) {
    let ss = [];
    for(let i=0; i<3; i++)
    {
      ss.push(this.renderSquare(i+row));
    }
    return (
      <div className="board-row">
      {ss}
      </div>
    )
  }
  renderBoard() {
    let board = []
    for(let i=0; i<3; i++)
    {
      board.push(this.renderRow(i*3));
    }
    return board;

  }
  render() {
    return (
      <div>
        {this.renderBoard()}
      </div>
    )
  }
}

基本上这个想法是替换用这个完成的硬编码板:

render() {
  return (
    <div>
      <div className="board-row">
        {this.renderSquare(0)}
        {this.renderSquare(1)}
        {this.renderSquare(2)}
      </div>
      <div className="board-row">
        {this.renderSquare(3)}
        {this.renderSquare(4)}
        {this.renderSquare(5)}
      </div>
      <div className="board-row">
        {this.renderSquare(6)}
        {this.renderSquare(7)}
        {this.renderSquare(8)}
      </div>
    </div>
  );
}

我想做的是在某种嵌套的 for 循环结构中有效地将 renderRow 和 renderBoard 组合在一起。我只有不同的功能,因为我不知道如何将元素的嵌套结构内联到我正在制作的数组中。这可能吗?

【问题讨论】:

  • 在这个特定的示例中,我不会尝试将其强制为一些花哨的for 循环。 for 循环也不能很好地与 jsx 配合使用,而且大多数时候我发现它们很难阅读。我尽量避免它们。实际上,几个月来,在一个中型项目中整天创建 React 组件时,我什至没有使用过一次。我尽可能使用map()(在 99% 的情况下)。

标签: javascript reactjs


【解决方案1】:

您经常看到人们做的是map 在某些结构上以动态创建元素数组。在您的情况下,由于您只有一个平面列表而不是适当的层次结构,您可以通过简单地临时创建一个来解决。然后您实际上并不需要所有这些功能。

class Board extends React.Component {
    render() {
        const indexes = [[0, 1, 2], [3, 4, 5], [6, 7, 8]];

        return (<div>
            {indexes.map(row =>
                <div class="board-row">
                    {row.map(index =>
                        <Square key={index}
                            value={this.props.squares[index]}
                            onClick={() => this.props.onClick(i)}
                            />
                    )}
                </div>
            )}
        </div>);
    }
}

这是顺便说一句。一个很好的例子,与 JSX 相比,使用超脚本使这看起来更容易一些。我不想在这里让你远离 JSX,但我认为所有的嵌套和花括号让你很难理解。有兴趣的可以看看下面这个使用超标的sn-p:

const h = React.createElement;

function Square(props) {
  return h('div', { className: 'square', onClick: props.onClick }, props.value);
}

function Board(props) {
    const indexes = [[0, 1, 2], [3, 4, 5], [6, 7, 8]];
    return h('div', null, indexes.map((row, k) =>
        h('div', { key: k, className: 'board-row' }, row.map(index =>
            h(Square, { key: index, value: props.squares[index], onClick: props.onClick.bind(null, index) } )
        ))
    ));
}


const props = {
  squares: [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i' ],
  onClick: (i) => console.log(i),
};
ReactDOM.render(h(Board, props), document.getElementById('target'));
.board-row {
  display: flex;
}
.square {
  width: 40px;
  height: 40px;
  border: 1px solid gray;
}
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="target"></div>

【讨论】:

  • 这其实很干净。我喜欢。但无论如何我会选择原版。它牺牲了一点“不要重复自己”来大幅提高可读性。它甚至可能更快。但是,如果我为了使用循环而决定做不同的事情,那将是这样做的方式。
【解决方案2】:

当然,试试这个:

renderBoard() {
  let board = []
  for (let i = 0; i < 3; i++) {
    let inner = []
    for (let j = 0; j < 3; j++) {
      inner.push(this.renderSquare(j + i));
    }
    let row = (
      <div className="board-row">
        {inner}
      </div>
    )
    board.push(row);
  }
  return board;
}

注意我必须做的事情,我必须创建一个变量来存储row,而不是推送函数调用,我只是将row 推送到board。此外,内部循环使用不同的变量 j 替换 renderRow(row)row 输入

【讨论】:

  • 别忘了给数组中的每个元素添加key属性,像这样&lt;Square key={i} value={this.props.squares[i]} onClick={() =&gt; this.props.onClick(i)} /&gt;
【解决方案3】:

Board 组件的较短版本

  class Board extends React.Component {
    render() {
      let row = [];
      const squaresNumbers = Array.apply(null, Array(9)).map((x, i) => i);

      return (
        <div>
          {squaresNumbers.reduce((result, i) => {
            row.push(
              <Square key={i} value={this.props.squares[i]} onClick={() => this.props.onClick(i)} />
            );
            if (row.length === 3) {
              result.push(<div className="board-row">{row}</div>);
              row = [];
            }
            return result;
          })}
        </div>
      );
    }
  }

【讨论】:

    【解决方案4】:

    考虑一下:您有一个从 0 到 9 的 范围,不包括上边界。您想将其 重塑 为 3 x 3 矩阵。之后,您希望将值 映射 到 React 组件实例中。让我们编写函数。

    首先,一个简单的range 函数,它只接受一个参数,即上边界,并输出从 0 到该边界的范围,不包括后者:

    const range = j => [...Array(j).keys()];
    

    这样

    > range(9)
    [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
    

    您可以查看 MDN 为什么会这样,但现在让我们考虑一下“为什么”超出了这个问题的范围。

    接下来,一个很简单的reshape函数:

    const reshape = (mx, my) => array => range(mx).map(x => range(my).map(y => array[mx * x + y]));
    

    它的工作原理是这样的:

    > reshape(3, 3)([1, 2, 3, 4, 5, 6, 7, 8, 9])
    [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ]
    

    最后,让它渲染:

    class Board extends React.Component {
      constructor(props, ...args) {
        super(props, ...args);
    
        const {
          rows = 3,
          cols = 3,
        } = props;
        const board = reshape(rows, cols)(range(rows * cols));
    
        this.state = {
          rows,
          cols,
          board,
        };
      }
    
      render() {
        const { board } = this.state;
    
        return (
          <div>
            {board.map((row, i) => (
              <div key={i} className="board-row">
                {row.map(cell => (
                  <Square
                    key={cell}
                    value={cell}
                    onClick={() => this.props.onClick(cell)}
                  />
                ))}
              </div>
            ))}
          </div>
        );
      }
    }
    

    那里发生了什么?

    我们正在做一些函数式编程。我们编写了几个不与业务逻辑耦合的小函数。我们使用了“范围”和“重塑”之类的词,并相应地编写了函数,这样几乎每个程序员都无需阅读相应的源代码就可以理解这些函数的作用。

    我们在reshape 函数中做了一些currying。因为 Javascript 对可变参数函数声明不太友好,所以我们使用这种奇怪的 fn()() 语法。我认为习惯它需要时间,因为好处是很棒的:你开始编写小的可重用函数,这些函数可以组合成非常简单的块创建很酷的业务逻辑。

    我们在这里做了一些假设:

    • range 函数只接受一个参数,
    • reshape 函数只接受两个参数,
    • reshape 返回的 lambda 函数需要一个数组,并且语义规定它是平面的,
    • Board 组件被实例化(其构造函数已执行)后,我们从不更改电路板配置。

    其中任何一个都可以单独调整。您可能想使用来自 lodash 的 _.range,也许让 reshape 在极端情况下的功能更安全,等等。由您来优化。

    因此,虽然这个答案有很多值得思考的地方,但我希望你会发现它很有用。干杯!

    【讨论】:

      猜你喜欢
      • 2018-05-17
      • 2020-10-05
      • 2017-07-24
      • 2016-10-16
      • 2010-10-24
      • 1970-01-01
      • 2017-06-30
      相关资源
      最近更新 更多