Warning on empty prop: why it happens? - reactjs

I have component, which needs to fetch server data before rendering:
class BooksManage extends Component {
componentWillMount () {
const { dispatch, fetchManagePage } = this.props
dispatch(fetchManagePage())
}
render () {
const sections = this.props.books.map((section, index) => {
return (
<div className='row' key={index}>
<BookSectionContainer index={index} />
</div>
)
})
return (
<div className='row'>
<div className='col-md-8'>
{sections}
<BookPaginationContainer paginationClass='books' />
</div>
<div className='col-md-3'>
<div style={{position: 'fixed', width: 'inherit'}}>
<EmployeesListContainer />
</div>
</div>
</div>)
}
}
As you can see, it uses BookPaginationContainer, which expects several state parameters:
Warning: Failed prop type: The prop `currentPage` is marked as required in `Paginate`, but its value is `undefined`.
Etc.
Problem: as BookPaginationContainer is child of BooksManage, what I expect to achieve with componentWillMount() is that dispatching an action fetches state parameters which needed.
What actually happens:
action REQUEST_MANAGE_PAGE # 18:41:49.770
Warning: Failed prop type: The prop `currentPage` is marked as required in `Paginate`, but its value is `undefined`.
action RECEIVE_MANAGE_PAGE # 19:01:40.327
After all page renders correctly, but I'm concerned with how good is this.

Don't render components that need data until you have that data.
render() {
...
if(!this.props.requireData) {
return <div>Loading</div>;
}
return (
<div className='row'>
...

Related

Rendering text after fetching data from database using React and AWS Amplify

I am trying to use React and AWS Amplify to build a web app that will make a request to the database and display some text accordingly. However, I encounter the following error:
Error: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead.
The error message also indicates that the error is occurring after setNfts(nftList);
App.jsx:
function App() {
const [nfts, setNfts] = useState([]);
const fetchNfts = async () => {
try {
const nftData = await API.graphql(graphqlOperation(listNfts));
const nftList = nftData.data.listNfts.items;
console.log('nft list', nftList);
setNfts(nftList);
} catch (error) {
console.log('error on fetching nfts', error);
}
};
useEffect(() => {
fetchNfts();
}, []);
return (
<div className="App">
<header className="App-header">
<AmplifySignOut />
<h2>My App Content</h2>
</header>
<Box sx={{ display: 'grid', columnGap: 3, gridTemplateColumns: 'repeat(3, 1fr)' }}>
{nfts.map((nft, idx) => {
return (
<NftCard filePath={nft.filePath} name={nft.name} creator={nft.creator} idx={idx} />
);
})}
</Box>
</div>
);
}
NftCard.jsx:
import React from 'react';
import { Paper } from '#material-ui/core';
export function NftCard(filePath, name, creator, idx) {
return(
<Paper variant="outlined" elevation={2} key={`nft${idx}`}>
<div className="nftCard">
<div>
<img src={filePath} alt="" width="100px" height="100px" />
<div className="nftTitle">{name}</div>
<div className="nftCreator">{creator}</div>
</div>
</div>
</Paper>
);
}
console.log('nft list', nftList);
Does this have any data logged out?
And if so, can you please provide output?
You can try to send data to Component with:
<NftCard filePath={nft["0"].filePath} name={nft["0"].name} creator={nft["0"].creator} idx={idx} />
and Show the result of that
The props passed to NftCard should be listed separately and have curly braces around them:
function NftCard({ filePath, name, creatorThumbnail, creator, price, idx }) {
...
}

Pass an object value in props to another component

Consider the code below, I need to pass an id which is in object format to another component by props. But I have try many time and it not working. I think I may have some thing mistake, but I'm not sure where is it.
Main page (updated):
render(props){
const data = this.state.data;
return (
<div>
<Header />
<NavigationBar />
<PurchaseInfoView show={this.state.displayModal} closeModal={this.closeModal} value={this.openModal}/>
<div className="purchase-side">
<div className="side-title">
<h1>Purchase Order List</h1>
</div>
<hr class="solid" />
{
Object.keys(data).map((key) =>
<div className="list-item">
<h2 onClick= {() => this.openModal(data[key].id)}> //get id
{ data[key].item_name}
</h2>
</div>
)}
</div>
<div className="dads">
</div>
</div>
);
}
openModal (Updated):
openModal = (id) => {
this.setState(
{
displayModal: true,
id: id
});
console.log(id) // i can get the id from here id=1
};
PurchaseInfoView to get the id (Updated).
class PurchaseInfoView extends Component {
render() {
console.log(this.props.id) // get undefined
return (
<div className="Modal"
style={{
transform: this.props.show ,
opacity: this.props.show ? "1" : "0"
}}
>
<h3>Purchase Order detail</h3>
<p>Id: {this.props.id}</p> //cannot get it
</div>
);
}
}
export default PurchaseInfoView;
console.log result:
If you want to pass an object to props here are the steps:
define the object in your parents state.
pass the object in props when calling components
get the object from props in child.
Here you are missing the second step!
You should try these:
MainPage
render(props){
const { data, modalObject, displayModal } = this.state; //use destructuring is more readable
return (
<div>
<Header />
<NavigationBar />
<PurchaseInfoView show={displayModal} closeModal={this.closeModal} modalObject={modalObject}/> //pass the object from destructuring state as props
<div className="purchase-side">
<div className="side-title">
<h1>Purchase Order List</h1>
</div>
<hr class="solid" />
{
Object.keys(data).map((key) =>
<div className="list-item">
<h2 onClick= {() => this.openModal(data[key].id)}> //get id
{ data[key].item_name}
</h2>
</div>
)}
</div>
<div className="dads">
</div>
</div>
);
}
OpenModal
openModal = (id) => {
this.setState(
{
displayModal: true,
modalObject: {id: id, ...any others key/val pair}
});
};
PurchaseInfoView
class PurchaseInfoView extends Component {
render() {
const { modalObject} = this.props; //here get your object from props
console.log(modalObject.id);// here you have the object
return (
<div className="Modal"
style={{
transform: this.props.show ,
opacity: this.props.show ? "1" : "0"
}}
>
<h3>Purchase Order detail</h3>
<p>Id: {modalObject.id}</p>
</div>
);
}
}
Tell me if you have any question in comment ;)
NB: i did this with an object (aka {} ) if you needed more things in your modal than just id. If just id is needed you just have to replace the modalObject by just the "id" you need
Cheers!
EDIT: for this solution to work you have to either:
initialise your state to this at least:
this.state={ modalObject : { id: ''}}
or make a not null test in your child component before displaying the element like so:
Id: {modalObject && modalObject.id ? modalObject.id : ' '}
These are needed because on first render your state will have the initial state you setted so if you didnt set anythin or didnt test for a value... well... it's undefined! :)
(note if id is null instead of having an undefined error you will have a blank space displaying in your modal)
Guess you are calling it wrongly. It should be {this.props.id}
render() {
console.log(this.props.id);
return (
<div className="Modal">
<h3>Purchase Order detail</h3>
<p>Id: {this.props.id}</p> //Changed line
</div>
);
}
Inside main page pass the id to PurchaseInfoView and access it as a prop
<PurchaseInfoView show={this.state.displayModal} closeModal={this.closeModal} value={this.openModal} id={this.state.id}/>

How to pass Mobx store as props to react compoent

I have this app that uses mobx, in it there is a component called "Listings" that uses some state from mobx to render a list of items.
The way it is right now, is that the Listings component gets the data it needs(store.restaurantResults[store.selectedFood]) from inside of it by using the mobx store like so:
const Listings = () => {
const store = React.useContext(StoreContext);
return useObserver(() => (
<div className="pa2">
{store.restaurantResults[store.selectedFood] &&
store.restaurantResults[store.selectedFood].map((rest, i) => {
return (
<div key={i} className="pa2 listing">
<p>{rest.name}</p>
</div>
);
})}
</div>
));
};
But i think this is wrong, as it couples the component with the data, I want instead to pass that data via props so it can be reusable.
What is the correct way to do this? Right now my App looks like this, where it's being wrapped around a storeProvider:
function App() {
return (
<StoreProvider>
<div className="mw8 center">
<Header title="EasyLunch" subTitle="Find Pizza, Burgers or Sushi in Berlin the easy way"/>
<FixedMenu menuItem1={"Pizza"} menuItem2={"Burger"} menuItem3={"Sushi"} />
<p className="b tc pt3">or...</p>
<Search />
<Listings />
</div>
</StoreProvider>
);
}
My idea is to extract everrything inside the StoreProvider into another component that has a store and returns the jsx via useObserver so that I can acces the store and then pass what i need as props to the other components. like this:
const Wapper = () => {
const store = React.useContext(StoreContext);
return useObserver(() => (
<div className="mw8 center">
<Header title="EasyLunch" subTitle="Find Pizza, Burgers or Sushi in Berlin the easy way" />
<FixedMenu menuItem1={"Pizza"} menuItem2={"Burger"} menuItem3={"Sushi"} />
<p className="b tc pt3">or...</p>
<Search />
<Listings listings={store.restaurantResults[store.selectedFood]} />
</div>
))
}
And then on the listings component change the hard coded store.restaurantResults[store.selectedFood] inside to use the props that is being passes now, that is called listigs like so:
const Listings = ({listings}) => {
const store = React.useContext(StoreContext);
return useObserver(() => (
store.loading
? <Loading />
: <div className="pa2">
<div className="flex flex-wrap">
{listings &&
listings.map((rest, i) => {
return (
<div key={i} className="pa2 listing">
<img className='object-fit' src={rest.image_url} alt="restuarant" />
<p>{rest.name}</p>
<p>{rest.location.address1}</p>
</div>
);
})}
</div>
</div>
));
};
And this works, but is this the right way to go about this?
As <Listings/> can be provided with listing and loading you can:
const Listings = ({listings, loading}) => {
if(loading) return <Loading />
return (
<div className="pa2">
<div className="flex flex-wrap">
{listings && listings.map((rest, i) => {
return (
<div key={i} className="pa2 listing">
<img className='object-fit' src={rest.image_url} alt="restuarant" />
<p>{rest.name}</p>
<p>{rest.location.address1}</p>
</div>
);
})}
</div>
</div>
);
}
No observables used, no useObservable required.
You want to useObservables on store for listings then no reason to wrap all components with useObservable. You should wrap <Listings/> only.
I usually define my store as a global, so every component has visibility of it:
class Store {
#observable myVar
}
global.store = new Store()
And in my components i just use it:
#observer
export default class MyComponent extends React.Component {
constructor () {
super()
store.myVar = 0
}
setMyVar (a) {
store.myVar += 1
}
render () {
return <button onClick={this.setMyVar}>
Clicked {store.myVar} times
</button>
}
}

Accesing object using props in ReactJs

I'm trying to access object keys using props as an index but it's not working. Error: Objects are not valid as a React child (found: object with keys {id, name, img_url, location}). If you meant to render a collection of children, use an array instead.
I am new to React so I appreciate any help.
My code:
class ExpandCard extends React.Component {
render() {
const props = this.props;
const profiles = props.profiles;
return(
<>
<div className="">
{profiles[props.active]}
</div>
</>
);
}
}
class App extends React.Component {
state = {
profiles: testData,
active: null,
}
getActive = (dataFromCard) => {
console.log('the magic number is', dataFromCard);
this.setState({active: dataFromCard});
}
render() {
return (
<div>
<div className="wrapper">
<header>
<div className="logo">LOGO</div>
</header>
<Form />
<div className="cards">
<div className="card-list">
{this.state.profiles.map(profile => <Card key={profile.id} {...profile} activeCard={this.getActive} />)}
</div>
<div className="expand-card">
<ExpandCard active={this.state.active} profiles={this.state.profiles} />
</div>
</div>
</div>
</div>
);
}
}
It looks like {profiles[props.active]} returns an object that looks like this:
{ id, name, img_url, location }
You can't return an object from a React component, maybe you meant to return {profiles[props.active].name}?

How to pass ref of component up three levels for scroll event?

I am using the <InfiniteScroll/> component in my code like this:
<div style="height:700px;overflow:auto;" ref={(ref) => this.scrollParentRef = ref}>
<div>
<InfiniteScroll
pageStart={0}
loadMore={loadFunc}
hasMore={true || false}
loader={<div className="loader" key={0}>Loading ...</div>}
useWindow={false}
getScrollParent={() => this.scrollParentRef}
>
{items}
</InfiniteScroll>
</div>
</div>
I need to pass the from ref = {(ref) => this.scrollParentRef = ref; } to the component's grandparent, however my code does not work as expected. Here is my full component source code:
class AppEmpty extends Component {
constructor(props) {
super(props);
}
componentDidMount(){
this.props.setReferences(this.scrollParentRef);
}
render() {
const { children } = this.props;
const isActive = this.props.isActive;
return (
<div className="App">
<NavBar/>
<MenuRight/>
<div id="content"
className={isActive ? "content_enable": ""}
style={this.props.menuRight.styleContent}
ref={ (ref) => this.scrollParentRef = ref }
>
<FlashMessages/>
{children}
</div>
</div>
);
}
}
AppEmpty.protoTypes = {
children: PropTypes.element.isRequired,
};
function mapsStateToProps(state){
return {
menuRight: state.menuRight,
isActive: state.isActive,
};
}
export default connect (mapsStateToProps, {setReferences})(AppEmpty);
The documentation says:
getScrollParent Function Override method to return a different scroll listener if it's not the immediate parent of InfiniteScroll.
Can someone suggest a way to pass that reference in an easy way to the <InfiniteScroll/>?
After trying everything, I realized that I can pass as a refs using document.getElementById ('comp_id_a_referenciar') :(
Content-> compomente_base -> form_busqueda, Tables -> InfiniteScroll(Refs_Content).
<div>
<InfiniteScroll
pageStart={0}
loadMore={loadFunc}
hasMore={true || false}
loader={<div className="loader" key={0}>Loading ...</div>}
useWindow={false}
getScrollParent={ () => document.getElementById('content') }
>
{items}
</InfiniteScroll>
working! nice!

Resources