Is there a better way to display content depending on the route? - javascript

There is a component responsible for rendering items in the e-commerce. I want to have a single component, which will render different content depending on the category clicked by user.
My solution looks something like this:
function ProductsPage({collections}) {
return (
<Route path="/products/type_one" render={() =>
<div>
<h1>Type One</h1>
<div className="products">
{
collections.category.typeOne.items.map((item) => {
return (
<ProductCard key={item.id} item={item} />
)
})
}
</div>
</div>
}/>
<Route path="/products/type_two" render={() =>
<div>
<h1>Type Two</h1>
<div className="products">
{
collections.category.typeTwo.items.map((item) => {
return (
<ProductCard key={item.id} item={item} />
)
})
}
</div>
</div>
}/>
)
}
So far I have only four subcategories, that`s why it looks pretty harmless. But if there are dozens of them, then the component code will be clogged with an endless copy-paste. Is there a more elegant method for solving the problem?

You can use route props to dynamically get the category parameter from the route URL and display the items using so. Your code might look similar to this:
<Route path="/products/:category" component={Category} />
function Category(props) {
const category = collections.category[props.match.params.category]
if (category) {
return (
<div>
<h1>category.name</h1>
<div className="products">
{
category.items.map((item) => {
return (
<ProductCard key={item.id} item={item} />
)
})
}
</div>
</div>
)
} else {
// category does not exist
}
}

Related

nested array maps not rendering in react return

I have a react return like so:
return (
<div className="App">
{data && (
<div>
{data.map((d) => {
return (
<div key={d.id}>
<div>{d.string}</div>
<div> {d.array.map((el) => {
<div>{el}</div>
})}
</div>
</div>
);
</div>
)}
</div>
);
Each {el} doesn't render but the array exists, if I try and render {d.array[0]} the first index of the array is rendered, so I'm not the issue. I don't get an error and nothing breaks. Is it a react issue or a javascript issue, or is my syntax wrong.
You need to add a key to each children of your second map so React knows each one is different:
return (
<div className="App">
{data && (
<div>
{data.map((d) => {
return (
<div key={d.id}>
<div>{d.string}</div>
<div> {d.array.map((el, index) => {
return <div key={index}>{el}</div>
})}
</div>
</div>
);
</div>
)}
</div>
);
before the "=>" of second map, will have use "()" and not "{}", because all that be in {is js}, and in (jsx).

React - component is not rendered

After receiving data from the DB server, you try to render it, but the console log shows the data, but the component is not rendered. What's the reason?
useEffect(() => {
readRequest().then(setTodos);
console.log()
}, []);
return (
<div className="App">
{todos.map((todo) => {
console.log(todo);
console.log(todo.text);
<div key={todo._id}>
{todo.text}
{`${todo.completed}`}
</div>
})}
<p>dfdf</p>
</div>
);
The picture is a screen capture.
Your .map callback does not return anything.
Change the { to (:
return (
<div className="App">
{todos.map((todo) => ( // <-- here
<div key={todo._id}>
{todo.text}
{`${todo.completed}`}
</div>
))}
<p>dfdf</p>
</div>
);
Or use the return keyword.
return (
<div className="App">
{todos.map((todo) => {
return (<div key={todo._id}>
{todo.text}
{`${todo.completed}`}
</div>);
})}
<p>dfdf</p>
</div>
);

Trying to wrap second map method in a row DIV but i keep getting unexpected token error

{middleMenu.map((column) => {
return (
<div className="row">
column.map((item) => {
const { title, image, path } = item;
return (
<ul className="footer-collections">
<MenuLinks title={title} image={image} path={path} />
</ul>
);
})
</div>
);
})}
Does anyone know what the solution is? My first time using 2D Arrays
use :
<div className="row">
{column.map((item) => {
const { title, image, path } = item;
return (
<ul className="footer-collections">
<MenuLinks title={title} image={image} path={path} />
</ul>
);
})}
</div>

How do I pass a filter value up as part of the state and return it down as props, thereby filtering out certain items?

As of right now I have a list of 40 or so Menu items that are grocery stores, and I only have the option to display all of the 40 stores at one time. I am trying to implement a filter by using a drop-down menu where you select the name of a store and only display stores with that name. Here is my code for the filter in a file called 'MenuComponent.js':
class FilterForm extends Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(values) {
console.log(values.filter);
}
render() {
return(
<LocalForm onSubmit={(values) => this.handleSubmit(values)}>
<Control.select model=".filter" id="filter" name="filter" className="form-control">
<option value="Safeway">Safeway</option>
<option value="Whole Foods">Whole Foods</option>
<option value="Luckys">Luckys</option>
<option value="Sprouts">Sprouts</option>
</Control.select>
<Row className="form-group">
<Col md={{size: 10, offset: 2}}>
<Button type="submit" color="primary">
Filter
</Button>
</Col>
</Row>
</LocalForm>
);
};
}
As part of this file I have the actual menu component as such, also located in 'MenuComponent.js':
const Menu = (props) => {
const menu = props.dishes.dishes.map((dish) => {
return (
<div key={dish.id} className="col-12 col-md-5 m-1">
<RenderMenuItem dish={dish} comments={props.comments.comments.filter((comments) => comments.dishId === dish.id)} />
</div>
);
});
if (props.dishes.isLoading) {
return(
<div className="container">
<div className="row">
<Loading />
</div>
</div>
);
}
else if (props.dishes.errMess) {
return(
<div className="container">
<div className="row">
<h4>{props.dishes.errMess}</h4>
</div>
</div>
);
}
else
return (
<div className="container">
<div className="row">
<div className="col-12">
<h3>STORES</h3>
<FilterForm />
<hr />
</div>
</div>
<div className="row">
{menu}
</div>
</div>
);
}
export default Menu;
I am getting the correct value for values.filter. My question is how do I pass up this values.filter and re-render my Menu item so that I am only displaying the items where dish.name === values.filter.
The parent component is in a file called 'MainComponent.js' and looks like this:
return (
<div id="MainDiv">
<Header />
<TransitionGroup>
<CSSTransition key={this.props.location.key} classNames="page" timeout={300}>
<Switch>
<Route path="/home" component={HomePage} />
<Route exact path="/aboutus" component={AboutPage} />
<Route exact path="/menu" component={() => <Menu dishes={this.props.dishes} comments={this.props.comments} />} />
<Route path="/menu/:dishId" component={DishWithId} />
<Route exact path="/contactus" component={() => <Contact resetFeedbackForm={this.props.resetFeedbackForm}
postFeedback={this.props.postFeedback} />} />
<Redirect to="/home" />
</Switch>
</CSSTransition>
</TransitionGroup>
<Footer />
</div>
);
This is where I am mapping the state and dispatch to props:
const mapStateToProps = state => {
return {
dishes: state.dishes,
comments: state.comments,
promotions: state.promotions,
leaders: state.leaders
}
}
const mapDispatchToProps = (dispatch) => ({
postComment: (dishId, rating, author, comment, masks, carts, sanitizer, monitor, oneway, register, card, numcust, gloves, curb, delivery) => dispatch(postComment(dishId, rating, author, comment, masks, carts, sanitizer, monitor, oneway, register, card, numcust, gloves, curb, delivery)),
fetchDishes: () => {dispatch(fetchDishes())},
resetFeedbackForm: () => { dispatch(actions.reset('feedback'))},
fetchComments: () => {dispatch(fetchComments())},
fetchPromos: () => {dispatch(fetchPromos())},
fetchLeaders: () => {dispatch(fetchLeaders())},
postFeedback: (firstname, lastname, telnum, email, agree, contactType, message) => dispatch(postFeedback(firstname, lastname, telnum, email, agree, contactType, message))
});
You can pass down a function from Menu that will be used in FilterForm to set a state variable in Menu. When FilterForm updates the state variable in Menu, the Menu will re-render with the new filter.
Since Menu is a functional component you can use the useState hook.
const Menu = (props) => {
const [filter, setFilter] = useState(null);
...
// then pass the function to FilterForm
<h3>STORES</h3>
<FilterForm setFilter={setFilter} />
<hr />
Add the setFilter to FilterForm onSubmit
handleSubmit(values) {
console.log(values.filter);
this.props.setFilter(values.filter); // calls Menu's setFilter
}
Now we have that value back in Menu
const Menu = (props) => {
const [filter, setFilter] = useState(null);
let dishes = props.dishes.dishes;
if(filter !== null){
// not sure if you call it storeName or something else, but filter the list
dishes = dishes.filter((dish) => dish.storeName === filter);
}
// and then do map with the filtered list
const menu = dishes.map((dish) => {
return (
<div key={dish.id} className="col-12 col-md-5 m-1">
<RenderMenuItem dish={dish} comments={props.comments.comments.filter((comments) => comments.dishId === dish.id)} />
</div>
);
});
...
I hope this helps!

Why I can't call useRef inside callback?

When I write this code I have an error:
React Hook "useRef" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function
What should I do with this code?
return ITEMS.map((item, i) => {
const elementRef = useRef(null);
return (
<div
ref={elementRef}
key={i}
>
<p>{item.name}</p>
<Wrapper>
{item.name === visibleItem && (
<Item
parentRef={elementRef}
/>
)}
</Wrapper>
</div>
);
}
Here are two possibilities, Either using useRef with an object/array, or using createRef as suggested by Yevgen Gorbunkov.
I'm not entirely sure as to the viability of these as the createRef option will create entirely new refs on each render, and the useRef option you'll need to make sure your keys/indexes are always the same.
const ITEMS = [{ name: "test" }, { name: "test2" }];
export default function App() {
const ref = useRef({});
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
{ITEMS.map((item, idx) => {
return (
<div key={idx} ref={element => (ref.current[idx] = element)}>
<p>{item.name}</p>
<Wrapper>
{item.name === visibleItem && (
<Item parentRef={ref.current[idx]} />
)}
</Wrapper>
</div>
);
})}
{ITEMS.map((item, idx) => {
const ref = createRef();
return (
<div key={idx} ref={ref}>
<p>{item.name}</p>
<Wrapper>
{item.name === visibleItem && <Item parentRef={ref} />}
</Wrapper>
</div>
);
})}
</div>
);
}

Categories

Resources