My strong recommendation is two-fold:
- Use the
findmethod to find the correct topic. - Don't use state to redundantly store information you already have from props. Storing information you already have can result in divergence. In this case, we can just
findthe appropriate topic in the render method:
import React, { Component } from "react";
import topics from "./topics.json";
class Tutorial extends Component {
constructor(props) {
super(props);
}
render() {
const selectedTopic = topics.find(
(topic, index) => page.url === this.props.match.params.url
);
return (
<div className="Tutorial">
<div className="ca-nav-spacer w3-hide-small"></div>
{selectedTopic.name}
{this.props.match.params.url}
</div>
);
}
}
export default Tutorial;
Answer from Nick on Stack OverflowMy strong recommendation is two-fold:
- Use the
findmethod to find the correct topic. - Don't use state to redundantly store information you already have from props. Storing information you already have can result in divergence. In this case, we can just
findthe appropriate topic in the render method:
import React, { Component } from "react";
import topics from "./topics.json";
class Tutorial extends Component {
constructor(props) {
super(props);
}
render() {
const selectedTopic = topics.find(
(topic, index) => page.url === this.props.match.params.url
);
return (
<div className="Tutorial">
<div className="ca-nav-spacer w3-hide-small"></div>
{selectedTopic.name}
{this.props.match.params.url}
</div>
);
}
}
export default Tutorial;
Although find would work in most situations, it won't for reasons I did not include. I ended up using a forEach loop in the constructor.
constructor(props){
super(props);
topics.forEach((topic) => {
if(topic.url === this.props.match.params.url)
{
this.state = {
url: topic.url,
title: topic.title
}
}
})
}
Thanks everyone!
You can use map to simplify it. The tricky bit will be the calling of Update with different number of parameters, but that too can be achieved using another map.
const columns = ['Title', 'Author', 'Rating'];
export const BookshelfListRow = (props) => {
return (
<tr className="table-row">
{
columns.map((column, i) => (
<td>
<input onChange={ e =>
props.Update(...[ // the parameters to Update consist of
...columns.slice(0, i).map(column => props.book[column]), // the column values from the start until the current column, map is used here to get the values for those columns
e.target.value // and the input value
])
}
placeholder={ props.book[column] } />
</td>
))
}
</tr>
)
}
Another approach:
The Update function is a mess. It can be a lot simpler if it just takes the column that was changed and the value as there is no need for it to send all those props back to the server if only one was changed, like so (this uses computed property names):
const Update = (column, value) => // takes the column that was changed and the value
axios.put('http://localhost:4001/books/update', { [column]: value }); // update only that column
Then the rendering will be much simpler also, like so:
const columns = ['Title', 'Author', 'Rating'];
export const BookshelfListRow = (props) => {
return (
<tr className="table-row">
{
columns.map((column, i) => (
<td>
<input onChange={ e => props.Update(column, e.target.value) } placeholder={ props.book[column] } />
</td>
))
}
</tr>
)
}
If you're using the keys of props.book, you can try something like this:
import React from "react";
const BookshelfListRow = props => {
const args = [];
return (
<tr className="table-row">
{Object.keys(props.book).map((key, idx) => {
if(idx > 0) {
args.unshift(key);
}
const argsCopy = [...args];
return (
<td>
<input
onChange={e => {
props.Update(...argsCopy, e.target.value);
}}
placeholder={props.book[key]}
/>
</td>
);
})}
</tr>
);
};
export default BookshelfListRow;
Otherwise, you can use an array like the one you suggested (const columns = ['Title', 'Author', 'Rating']) and take each value and add it to a copy with each map loop.