How to create a matrix input component with React
May 08, 2020
Prerequisites
For this example, we’ll be handling state with hooks. If you’re not familiar with hooks, check React’s Docs. It’s the future of React and it’ll make your life easier so don’t think twice about it.
If you want to display your matrix in LaTex, you’ll also have to install katex
and react-katex
.
Parent component
In the parent component, we’ll be declaring different states for
- The size of the matrix (object with ‘columns’ and ‘rows’ attributes).
- The matrix itself (a two dimensional array == an array of array(s)).
- The KaTeX string associated with the matrix.
const [matrixSize, setMatrixSize] = useState({
rows: 2,
columns: 2,
})
// or, if it's a square matrix :
// const [squareMatrixSize, setSquareMatrixSize] = useState(2);
const [matrix, setMatrix] = useState([[0, 0], [0, 0]])
const [latexMatrix, setLatexMatrix] = useState(
"\\begin{pmatrix}\n 0 & 0\\\\\n 0 & 0\n \\end{pmatrix}",
)
Matrix Size Input Component
The first step is to let the user choose the size of the matrix. We thus create a component that takes as props
the setMatrixSize
function.
<MatrixInputSize setMatrixSize={object => setMatrixSize(object)} />
Here’s what the MatrixInputSize
input should look like :
<input
type="number"
defaultValue={2}
onChange={e => {
const rows = parseInt(e.target.value)
// if we only want matrix of size between 2 and 8
if (2 <= rows && rows <= 8) {
setMatrixSize(prevSize => ({
...prevSize,
rows: rows,
}))
}
}}
/>
We use object destructuring
to set the new value of the matrixSize
state while keeping the value we are not changing (here we change the number of
rows without changing the number of columns).
Matrix Input Component
Now for the real deal, how do we dynamically create the different inputs and get their values to update the matrix
state ?
We create a new component : MatrixInput
.
<MatrixInput matrixSize={matrixSize} setMatrix={matrix => setMatrix(matrix)} />
Inside this component, we’ll be creating a new variable called matrix
, which we’ll use to build the inputs.
let matrix = Array(matrixSize.rows)
for (let i = 0; i < matrixSize.rows; i++) {
matrix[i] = new Array(matrixSize.columns).fill(0)
}
We then build our form with the map()
function o
<form onSubmit={handleSubmit}>
{matrix.map((row, indexRow = 1) => {
return (
<MatrixRow key={indexRow}>
{row.map((item, indexColumn = 1) => {
return (
<input
key={indexRow + " " + indexColumn}
type="text"
defaultValue={0}
name={indexRow + "," + indexColumn}
/>
)
})}
</MatrixRow>
)
})}
<button>{"Save A"}</button>
</form>
You just have to define <MatrixRow>
as a flex-container (css : display: flex;
) and you’ll have n
rows of m
elements.
The last step is to get the values from the form and update the matrix
state with those values. We’re actually able to
loop through the inputs of the form with event.target[count]
, which is really useful in this case.
const handleSubmit = event => {
event.preventDefault();
let count = 0;
for (let i = 0; i < matrixSize.rows; i++) {
for (let j = 0; j < matrixSize.columns; j++) {
// If the floating point number cannot be parsed, we set 0 for this value
matrix[i][j] = !isNaN(parseFloat(event.target[count].value)) ? parseFloat(event.target[count].value) : 0;
count += 1;
}
}
setMatrix(matrix);
}
Matrix rendering with react-katex
In the parent component, let’s set a useEffect
with matrix
as a dependency so that when the matrix
state is modified,
we update the latexMatrix
with the right values.
useEffect(() => {
setLatexMatrix(renderLatexMatrix(matrix))
// + do any action you want on the matrix
}, [matrix])
As you can see, we’re using a function called renderLatexMatrix
. This function transforms a matrix into a string
readable by react-katex
to be displayed in the browser in a LaTeX manner.
Here is this function :
const renderLatexMatrix = matrix => {
return (
"\\begin{pmatrix}\n" +
matrix
.map((row, index) => {
if (index === matrix.length) return row.join(" & ") + "\n"
else return row.join(" & ") + "\\\\\n"
})
.join("") +
"\\end{pmatrix}"
)
}
export default renderLatexMatrix
Then, we render the matrix :
<BlockMath math={"A = " + latexMatrix} />
Isn’t that beautiful ?