I have my homepage where I received the products with the redux method but I did not want to render them on the home page so I did it with a single product component, but again I wanted to display the products in react-Alice-carousel I sent the products in the homepage through props and destrusctured it in the Single product and tried to create the items props of react-alice-carousel through jsx but got an error product.map is not a function.
My Single Product Component.
import React from "react";
import AliceCarousel from "react-alice-carousel";
// import { Button, Card, Container } from "react-bootstrap";
// import { FaShoppingCart } from "react-icons/fa";
// import { useDispatch, useSelector } from "react-redux";
import { Link } from "react-router-dom";
// import { AddToCartAction, RemoveFromCart } from "../../actions/cartActions";
import UICARD from "../../interface/UICARD";
// import classes from "./home.module.css";
export function numberWithComas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
const SingleProduct = ({ product }) => {
const items = product.map((p) => {
return (
<Link to={`/product/${p._id}`}>
<UICARD>
<img src={p.image} alt={p.name} />
<span>
{p.name}
<span>{p.description}</span>
</span>
<span>{p.price}</span>
</UICARD>
</Link>
);
});
const responsive = {
0: {
items: 4,
},
512: {
items: 6,
},
};
return (
<div>
<AliceCarousel
items={items}
disableDotsControls
infinite
mouseTracking
responsive={responsive}
/>
</div>
);
};
export default SingleProduct;
My Home Page
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Products } from "../../actions/productsActions";
import ErrorMessage from "../../helpers/ErrorMessage";
import Loading from "../../helpers/Loading";
import SideBar from "../Home/SideBar";
import SingleProduct from "../Home/SingleProduct";
import classes from '../Home/home.module.css'
const Home = () => {
const dispatch = useDispatch();
const productList = useSelector((state) => state.productList);
const { products, error, loading } = productList
console.log(products);
useEffect(() => {
dispatch(Products());
},[dispatch])
return (
<div className ={classes.home}>
{error && <ErrorMessage variant={error.info?"info":"danger"}>{error}</ErrorMessage>}
{loading && <Loading />}
<SideBar />
<div className={classes.productContainer}>
{
products.map((product) => {
return <SingleProduct product={product} key={product._id} />
})
}
</div>
</div>
);
};
export default Home;
You need to change you home compoent like that since singleProduct need to have a props peroduct as an array , i recommand to use prop-types to avoid this kind of problems
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Products } from "../../actions/productsActions";
import ErrorMessage from "../../helpers/ErrorMessage";
import Loading from "../../helpers/Loading";
import SideBar from "../Home/SideBar";
import SingleProduct from "../Home/SingleProduct";
import classes from '../Home/home.module.css'
const Home = () => {
const dispatch = useDispatch();
const productList = useSelector((state) => state.productList);
const { products, error, loading } = productList
console.log(products);
useEffect(() => {
dispatch(Products());
},[dispatch])
return (
<div className ={classes.home}>
{error && <ErrorMessage variant={error.info?"info":"danger"}>{error}</ErrorMessage>}
{loading && <Loading />}
<SideBar />
<div className={classes.productContainer}>
<SingleProduct product={products} />
</div>
</div>
);
};
export default Home;
Related
Home.jsx:
import React, { useEffect } from "react";
import Slider from "../Slider/Slider";
import "./Home.css";
import "react-toastify/dist/ReactToastify.css";
import Product from "./Product.js";
import MetaData from "../layout/MetaData";
import { getProduct } from "../../actions/productAction";
import { useSelector, useDispatch } from "react-redux";
import Loader from "../Loader/Loader";
import { useAlert } from "react-alert";
const Home = () => {
const alert = useAlert();
const dispatch = useDispatch();
const { loading, products, productsCount, error } = useSelector(
(state) => state.products
);
useEffect(() => {
if (error) {
return console.log("err:" + error);
}
dispatch(getProduct());
}, [dispatch, error]);
return (
<div>
{loading ? (
<Loader />
) : (
<div>
<MetaData title="FNPASSION." />
<Slider />
<h2 className="homeHeading">Featured Products.</h2>
<div className="container_home" id="container_home">
{products &&
products.map((product) => <Product product={product} />)}
</div>
</div>
)}
</div>
);
};
export default Home;
productReducer.js:
*Why it was not logging the error in console.log?? after declaring an error in my productController.js ,can anyone please help ? Iam a beginner,so please answer this one.if you think this is a stupid question, please forgive me :)
Store.js:
I am using React Material TreeView, i am trying to create a route link that when i press on a TreeItem i can see it's specific details in the Outlet, but i dont know how to pass that ID of That specific item to the Outlet.
VesselComponents.js :
import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { fetchComponents } from "../../../features/componentsSlice";
import TreeItem from "#mui/lab/TreeItem";
import TreeView from "#mui/lab/TreeView";
import ExpandMoreIcon from "#mui/icons-material/ExpandMore";
import ChevronRightIcon from "#mui/icons-material/ChevronRight";
import { Link, Outlet } from "react-router-dom";
function VesselComponents() {
const { components, error, loading } = useSelector(
(state) => state.components
);
console.log(components);
const dispatch = useDispatch();
useEffect(() => {
fetchComponents()(dispatch);
}, [dispatch]);
const getTreeItemsFromData = (treeItems) => {
return treeItems.map((treeItemData) => {
let children = undefined;
if (treeItemData.children && treeItemData.children.length > 0) {
children = getTreeItemsFromData(treeItemData.children);
}
return (
<TreeItem
key={treeItemData.id}
nodeId={String(treeItemData.id)}
label={treeItemData.name}
children={children}
/>
);
});
};
const DataTreeView = ({ treeItems }) => {
return (
<TreeView
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
>
{getTreeItemsFromData(treeItems)}
</TreeView>
);
};
return (
<div className="components-container">
<div className="components-items">
{/* this line */}
<Link to={`./info/${components.id}`}>
<DataTreeView treeItems={components} />
</Link>
</div>
<div className="component-detail">
<Outlet />
</div>
</div>
);
}
export default VesselComponents;
I'm currently coding a React -typescript App for practising FluentUI (a.k.a Fabric). Issue appears
with my App.tsx component.
import React, { useContext, useState } from 'react';
import logo from './logo.svg';
import './App.css';
import Search from './components/Search';
//import CategoriasProvider from './components/Context/CategoriasContext';
import Title from './components/Title';
import { ListGhostingExample } from '../src/components/DrinkList';
import { PrimaryButton } from 'office-ui-fabric-react';
import { CategoriasContext, ICategoriasContextInterface } from './components/Context/CategoriasContext';
import CategoriasProvider from './components/Context/CategoriasContext';
import axios from 'axios';
import './components/DrinkList.css'
import './components/Search.css'
interface IApp{
items:ICategoriasContextInterface[],
renderList:boolean
}
const App =()=> {
const contextValues=useContext(CategoriasContext);
return(
<CategoriasProvider>
<div className="App">
<div className="search">
<Search name={contextValues?.name} image={contextValues?.image} thumbnail={contextValues?.thumbnail} />
</div>
</div>
</CategoriasProvider>
);
}
export default App;
CategoriasProvider comes from a Context (CategoriasContext.tsx ). CategoriasProvider has the mentioned error Inside of CategoriasProvider there's a Search.tsx Component.Search's works as a "wrapper". Code is:
import React, { useEffect, useState } from 'react';
import { SearchBox,ISearchBoxStyles } from 'office-ui-fabric-react/lib/SearchBox';
import { PrimaryButton, IContextualMenuProps, Stack, IStackTokens, StackItem, initializeIcons } from 'office-ui-fabric-react';
import { ComboBox, DefaultPalette, Dropdown, DropdownMenuItemType, IComboBoxOption, IDropdownOption, IDropdownStyles, IStackItemStyles, SelectableOptionMenuItemType, Toggle } from '#fluentui/react';
import { getGlassesOriginal } from './Utils/Utils';
import axios from 'axios';
import '../Search.css';
import { CategoriasContext, ICategoriasContextInterface } from './Context/CategoriasContext';
initializeIcons();
const Search = (props:ICategoriasContextInterface) => {
//State
const [textContent, setTextContent] = useState("");
const [textBoxDisabled,disableTextBox]=useState(false);
const [comboBoxDisabled,disableComboBox]=useState(true);
const CategoriasContextInSearch=React.useContext(CategoriasContext);
const setTextContentInstate = (e: any) =>{
console.log("Contenido de e" + e.target.value);
setTextContent(e.target.value);
}
const showMessageInConsole = ():void => {
console.log(textContent);
setTextContent("");
}
// Example formatting
const stackTokens: IStackTokens = { childrenGap: 20 };
const searchBoxStyles: Partial<ISearchBoxStyles> = { root: { width: 200 } };
const dropdownStyles: Partial<IDropdownStyles> = {
dropdown: { width: 200 },
};
const options: IDropdownOption[] = [
{ key: 'glasses', text: 'Glasses', itemType: DropdownMenuItemType.Header },
];
function getGlasses () {
let outputArray:string[] = [];
console.log("getGlasses");
axios
.get("https://www.thecocktaildb.com/api/json/v1/1/list.php?g=list")
.then((response)=>{
let responseDataJson=response.data.drinks;
for (let element in responseDataJson) {
options.push({key:responseDataJson[element].strGlass,text:responseDataJson[element].strGlass});
}
}
)
return outputArray;
}
function selectSearch(){
if(textBoxDisabled){
disableTextBox(false);
disableComboBox(true);
} else {
disableTextBox(true);
disableComboBox(false);
};
}
useEffect(() => {
//TODO: No se debería llamar siempre a esta función. Solamente cuando se activa el sistmea de búsqueda (y además, cachearlo)
getGlasses()
});
return(
<div className="wrapper">
<div className="one"> <Toggle onClick={selectSearch}/></div>
<div className="two">
{
<SearchBox
name="searchBox"
className="searchBox"
styles={searchBoxStyles}
placeholder="Cheers!"
onChange={setTextContentInstate}
value={textContent}
disabled={textBoxDisabled}
/>
}
</div>
<div className="three">
<Dropdown
placeholder="Select a glass"
options={options}
styles={dropdownStyles}
disabled={comboBoxDisabled}
/>
</div>
<div className="four">
<div className="primaryButton">
<PrimaryButton text="Search" onClick={showMessageInConsole}/>
</div>
</div>
</div>
);
}
export default Search;
Hope you can help me!!! Thanks in advance!
The code which is causing the error in your title is in your comment. It's this line:
export const CategoriasProvider = () => {
You are defining CategoriasProvider as a component which takes no props. It can only accept IntrinsicAttributes which is basically just the key property.
But when you use CategoriasProvider in App you are calling it with JSX element children. You get an error because you have not said that the CategoriasProvider component can accept a children prop.
Any of the following types will solve your problem:
export const CategoriasProvider: React.FC = ({children}) => {
export const CategoriasProvider = ({children}: {children: React.ReactNode}) => {
export const CategoriasProvider = ({children}: React.PropsWithChildren<{}>) => {
Regardless, you'll want to pass the children down as children of the inner Provider component.
return (
<CategoriasContext.Provider value={hola}>
{children}
</CategoriasContext.Provider>
);
Your App component is not going to work as expected because the useContext hook which accesses the CategoriasContext is located outside of the CategoriasProvider. It will just get the default value for the context -- not the value from the provider.
You need to rearrange your components such that the hook call occurs in a component that is rendered inside of the CategoriasProvider.
Try this:
const Search = () => {
const contextValues = useContext(CategoriasContext);
return (
<div className="search">
<Search
name={contextValues?.name}
image={contextValues?.image}
thumbnail={contextValues?.thumbnail}
/>
</div>
);
};
const App = () => {
return (
<CategoriasProvider>
<div className="App">
<Search />
</div>
</CategoriasProvider>
);
};
export default App;
I'm building a practice app that uses Unsplash to render users photos. I'm using React and Redux. With react-router-dom, I'm trying to follow the docs but I find it very confusing to set up. Here's what I have so far. When I click on a result out of a returned list of results from a search, I want it to render a user page profile.
index.js (make sure I have react-router-do set up correctly):
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import './index.css';
import App from './App';
// import store from './app/store';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import reducers from "./app/reducers/rootReducer";
import * as serviceWorker from './serviceWorker';
const storeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducers, storeEnhancers(applyMiddleware(thunk)));
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
</React.StrictMode>,
document.getElementById("root")
);
Top component App
import React from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
import Images from "./app/components/Images";
import Search from "./app/components/Search";
import UserProfile from "./app/components/UserProfile";
import "./App.css";
function App() {
return (
<>
<Search />
<Images />
<Router>
<Route link="/userProfile">
<UserProfile />
</Route>
</Router>
</>
);
}
export default App;
search (parent component to searchResults where exists):
import React, { useState, useEffect } from "react";
import { connect } from "react-redux";
import { queryAction } from "../actions/queryAction";
import SearchResults from "./SearchResults";
const Search = (props) => {
const [query, setQuery] = useState("");
console.log(props.searches);
const searchPhotos = async (e) => {
e.preventDefault();
console.log("submitting form");
props.queryAction(query);
};
const showUsers = (user, e) => {
e.preventDefault()
console.log(user)
};
return (
<>
<form className="form" onSubmit={searchPhotos}>
<label className="label" htmlFor="query">
{" "}
</label>
<input
type="text"
name="query"
className="input"
placeholder={`Try "dog" or "apple"`}
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<button type="submit" className="button">
Search
</button>
</form>
<SearchResults results={props.searches} showUsers={showUsers} />
</>
);
};
const mapStateToProps = (state) => {
return {
searches: state.searches,
};
};
const mapDispatchToProps = (dispatch) => {
return {
queryAction: (entry) => dispatch(queryAction(entry)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Search);
searchResults:
import React from "react";
import { BrowserRouter as Router, Link } from "react-router-dom";
import { getUserAction } from "../actions/getUserAction";
import { connect } from "react-redux";
const SearchResults = (props) => {
const { results } = props.results.searches;
const handleClick = (result, e) => {
e.preventDefault();
props.getUser(result.username);
};
return (
<>
{results &&
results.map((result, id) => {
return (
<div key={id}>
<Router>
<Link to="/userProfile" onClick={(e) => handleClick(result, e)}>
{result.username}
</Link>
</Router>
</div>
);
})}
</>
);
};
const mapDispatchToProps = (dispatch) => {
return {
getUser: (query) => dispatch(getUserAction(query)),
};
};
export default connect(null, mapDispatchToProps)(SearchResults);
and finally the UserProfile component:
import React from 'react';
import { connect } from 'react-redux';
const UserProfile = props => {
console.log(props)
return (
<div>
</div>
);
}
const mapStateToProps = state => {
return {
user: state.users
}
}
export default connect(mapStateToProps, null)(UserProfile);
app component
import React from "react";
import { Switch, Route } from "react-router-dom";
import Images from "./app/components/Images";
import Search from "./app/components/Search";
import UserProfile from "./app/components/UserProfile";
import "./App.css";
function App() {
return (
<>
<Search />
<Images />
<Switch>
<Route path="/userProfile/:username">
<UserProfile />
</Route>
</Switch>
</>
);
}
export default App;
SearchResults component
import React from "react";
import { Link } from "react-router-dom";
const SearchResults = (props) => {
const { results } = props.results.searches;
const handleClick = (result, e) => {
e.preventDefault();
props.getUser(result.username);
};
return (
<>
{results &&
results.map((result, id) => {
return (
<div key={id}>
<Link to={`/userProfile/${result.username}`}>
{result.username}
</Link>
</div>
);
})}
</>
);
};
export default SearchResults;
UserProfile component
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import { getUserAction } from "../actions/getUserAction";
const UserProfile = props => {
useEffect(() => {
props.getUserAction(props.match.params.username)
},[])
console.log(props)
return (
<div>
{props.user
? <div>{user.username}</div>
: <div>Loading...</div>
}
</div>
);
}
const mapStateToProps = state => {
return {
user: state.users
}
}
const mapDispatchToProps = (dispatch) => {
return {
getUser: (query) => dispatch(getUserAction(query)),
};
};
export default connect(mapStateToProps, mapDispatchToProps)(UserProfile);
Edit: Add a param to your link and remove the onclick. Update the Route to expect a :username param. You can access the param through props in UserProfile component.
Make sure to perform the action or access state when mounting the UserProfile component so you have some data when it renders.
Edit 2: Added UserProfile component to answer. You want to dispatch your action when the component is mounting. Also, set a ternary to show "Loading..." if state.user isn't done being fetched.
I am making a posts project with React. Whenever My home page just consists a bunch of posts in cards view. If I click on a certain card, it should navigate me to that post with some details. However, before the page renders completely, I still see some lines or 'unfinished' elements so to speak.
This is what I see before the post and it's description render completely
How can I fix this?
Here is my JS file with posts:
import React, { Component } from 'react'
import { Card } from "react-bootstrap";
import { Link } from "react-router-dom";
import './Posts.css'
export class Posts extends Component {
render() {
return (
<div>
{this.props.posts.map(post => (
<Link to={`/post/${post._id}`} key={post._id}>
<Card key={post._id} className="shadow-sm">
<Card.Img variant="top" src={post.image} />
<Card.Body>
<Card.Title id="cardtitle">{post.title}</Card.Title>
</Card.Body>
</Card>
</Link>
))}
</div>
)
}
}
And here is the Post detail:
import React, { useState, useEffect } from 'react'
import axios from "axios";
import { Media } from "react-bootstrap";
import './Post.css'
function Post({ match }) {
const [post, setPost] = useState({});
useEffect(() => {
const fetchPost = async () => {
const res = await axios.get(`${POST_URL}${match.params.id}`);
setPost(res.data);
}
fetchPost();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const POST_URL = `/api/v1/posts/`;
return (
<Media>
<img className="align-self-center mr-3 postImage" src={post.image} alt={post.title} />
<Media.Body>
<h5 className="postTitle text-center">{post.title}</h5>
<p>{post.description}</p>
</Media.Body>
</Media>
)
}
export default Post
You can create a function inside your component that conditionally render if the data has returned from the API. Something like this:
import React, { useState, useEffect } from 'react'
import axios from "axios";
import { Media } from "react-bootstrap";
import './Post.css'
function Post({ match }) {
const [post, setPost] = useState();
useEffect(() => {
const fetchPost = async () => {
const res = await axios.get(`${POST_URL}${match.params.id}`);
setPost(res.data);
}
fetchPost();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const POST_URL = `/api/v1/posts/`;
const renderPost = () => {
if (post) {
return(
<Media>
<img className="align-self-center mr-3 postImage" src={post.image} alt={post.title} />
<Media.Body>
<h5 className="postTitle text-center">{post.title}</h5>
<p>{post.description}</p>
</Media.Body>
</Media>
);
}
return <h1>Loading</h1>;
}
return (
renderPost();
)
}
export default Post
I recommend you to render a spinner or a placeholder to improve the user experience while the data is loading.