I am trying to map through the data in context and render out a list of cars. But I keep running into some sort of error. I would like to be able to select one of the cars and have it navigate me to another page that displays more details of the car. Current error is (TypeError: value.state.car is undefined). Any help would be great! Current code
Context.js
import React, {Component } from 'react';
import { carInventory } from './data';
export const MyContext = React.createContext();
export class MyProvider extends Component {
state = {
cars: []
};
componentDidMount() {
this.setCars();
}
setCars = () => {
let tempProducts = [];
carInventory.forEach(item =>{
const singleItem = {...item};
tempProducts = [...tempProducts,singleItem];
})
this.setState(()=>{
return {cars:tempProducts}
})
}
getItem = (id) => {
const car = this.state.cars.find(item => item.id === id);
return car;
}
handleDetail = (id) =>{
const car = this.getItem(id);
this.setState(()=>{
return {detailProduct:car}
})
}
render() {
return (
<MyContext.Provider value={{
state: this.state,
handleDetail: this.handleDetail}}>
{this.props.children}
</MyContext.Provider>
)
}
}
VehicleList.js
import React, { Component } from "react"
import './App.css';
import Vehicles from "./Vehicles"
import { MyProvider, MyContext } from "./Context";
export default class VehicleList extends Component {
render() {
return (
<div className="vehicles">
<div className="showcase">
<MyContext.Consumer>
{(value) => (
<React.Fragment>
{value.state.car.map(car => {
return (
<Vehicles key={car.id} car=
{car} />
);
})}
</React.Fragment>
)}
</MyContext.Consumer>
</div>
</div>
)
}
}
import React, { Component } from 'react';
import './App.css';
import { MyProvider, MyContext } from "./Context";
import { Link } from 'react-router-dom';
class Vehicles extends Component {
render() {
const { make, model, id, info, img } = this.props.cars
return (
<div className="col-9 mx-auto col-md-6 col-lg-3 my-3">
<div className="card">
<MyContext.Consumer>
{(value) => (<div className="img-container p-5"
onClick={() =>{
value.handleDetail(id)
}}>
<Link to="/VehicleOverview">
<img src={img} alt="product" className="card-img-top"/>
</Link>
</div>)}
</MyContext.Consumer>
</div>
</div>
)
}
}
export default Vehicles;
This is the kind of thing where static type checkers can really save you some grey hairs.
Your context indeed does not have any property car.
It does, however, have a property cars.
So you should map over cars, i.e. value.state.cars.map(car => ...)
:)
Related
I'm trying to implement react-virtualized, so I went into official doc here, and completed it in my code , here is my code :
import React from "react";
import { List } from "react-virtualized";
class Users extends React.Component {
state = {
userList: ["1", "222", "33"]
};
rowRenderer({ key, index, isScrolling, isVisible, style }) {
return (
<div key={key} style={style}>
{
console.log(this.state.userList[index])
//this.state.userList[index]
}
</div>
);
}
render() {
return (
<div>
<List
width={300}
height={300}
rowCount={this.state.userList.length}
rowHeight={20}
rowRenderer={Users.rowRenderer}
/>
</div>
);
}
}
export default Users;
Now the thing is in rowRender function, it console.log perfectly. But when I replace that with the commented line, it gives me error saying rowRenderer is not a function and when I add function before that, it gets parsing error.
How this can be resolved?
codesandbox.io
In codesandbox it is giving one more error : A cross-origin error was thrown.
Users is your class definition, but the rowRenderer function is a method on your class instance. You should use this.rowRenderer instead.
import React from "react";
import { List } from "react-virtualized";
class Users extends React.Component {
state = {
userList: ["1", "222", "33"]
};
rowRenderer = ({ key, index, isScrolling, isVisible, style }) => {
return (
<div key={key} style={style}>
{this.state.userList[index]}
</div>
);
}
render() {
return (
<div>
<List
width={300}
height={300}
rowCount={this.state.userList.length}
rowHeight={20}
rowRenderer={this.rowRenderer}
/>
</div>
);
}
}
export default Users;
You can try writing rowRenderer function after render. With full code only i can test. Something like this:
import React from 'react';
import * as StaticData from '../StaticData'
import {List} from 'react-virtualized';
class Users extends React.Component{
state={
userList:['1','222','33']
};
componentDidMount() {
StaticData.getData("/users").then(data => {
this.setState({userList:data});
console.log(this.state.userList)
});
}
render() {
function rowRenderer({
key,
index,
isScrolling,
isVisible,
style,
}) {
return (
<div key={key} style={style}>
{
console.log(key)
//this.state.userList[index]
}
</div>
);
}
return(
<div>
<List
width={300}
height={300}
rowCount={this.state.userList.length}
rowHeight={20}
rowRenderer={Users.rowRenderer}
/>
</div>
);
}
}
export default Users;
I have a product component which I wish to pass its picture to another component, my picture is uploaded from Rails backend, and the product details are mapped. What I want to achieve is when you click on the button then your product picture will display in the design page to be custom design.
import axios from 'axios';
import CardDeck from 'react-bootstrap/CardDeck';
import Card from 'react-bootstrap/Card';
import {Link} from 'react-router-dom';
import Display from './Display';
const SERVER_URL = "http://localhost:3000/products/index";
const IMAGE_URL = "http://localhost:3000/";
class Product extends Component {
constructor(props) {
super(props);
console.log(props);
this.state = {
products: [],
name: '',
material: ''
}
}
fetchProducts () {
axios.get(SERVER_URL).then((res) => {
//console.log(res.data);
const allProducts = res.data;
//this.setState({product: []});
this.setState({products: res.data});
this.setState({material: res.data.id});
// const aProduct = [...new Set(allProducts.map(pro => pro.name))]
// console.log(aProduct);
})
}
componentDidMount(){
this.fetchProducts();
}
_handleClick = event => {
event.preventDefault();
axios.get(SERVER_URL,{
//product:{name: this.state.name, category: this.state.category}
}).then(res =>{
this.setState({
})
}).catch(error => {console.log(error);
});
}
render() {
return (
<div className="productGrid">
{this.state.products.map((product, index) => (
// <p>Name: {product.name} <p>Price:{product.price}</p> <p>Category:{product.category}</p>
// <p> Fixing Method:{product.fixing_method}</p> <p>Material:{product.material}</p>
// <p>Height:{product.height}</p>
<CardDeck>
<Card>
<Card.Img variant="top" src={IMAGE_URL + product.img_tag} />
<Card.Body>
<Card.Title>Name: {product.name}</Card.Title>
<Card.Text>Category: {product.category}</Card.Text>
<Card.Text>Price: ${product.price}</Card.Text>
<Card.Text>Material: {product.material}</Card.Text>
<Card.Text>Fixin Method: {product.fixing_method}</Card.Text>
<Card.Text>Shape: {product.shape}</Card.Text>
<Card.Text>Height: {product.height}</Card.Text>
<Card.Text>Width: {product.width}</Card.Text>
<Link to={"/DesignPage/" + product.id}><button >Design Me</button></Link>
</Card.Body>
</Card>
</CardDeck>
))}
<Display image={IMAGE_URL + product.img_tag}/>
</div>
);
}
}
export default Product;
Design Page
When you click on design me button then based on the product the picture of that product should appear in design page
import React, { Component } from 'react';
import SideBar from './SideBar';
import Nav from './Nav'
import Display from './Display';
class DesignPage extends Component {
constructor(props) {
super(props);
// console.log(props.match.params.design);
this.state = { }
}
render() {
return (
<div><Nav/>
<div className="container py-4">
<div className="row">
<div className="col-lg-5">
<SideBar/>
</div>
<div className="col-lg-6">
{/* <Display design={this.props.match.params.design}/> */}
<Display/>
</div>
</div>
</div>
</div>
);
}
}
export default DesignPage;
Display
import React from 'react';
import Product from './Product';
const Display = (props) => {
console.log(props.design);
return(
<div className="card card-content">
<div className="container-lg">
<div>{props.design}</div>
</div>
</div>
)
}
export default Display;
Access like props.image instead of props.design as i can see you are assigning your concatenating url to image in Product component.
const Display = (props) => {
console.log(props.image);
return(
<div className="card card-content">
<div className="container-lg">
<div>{props.image}</div>
</div>
</div>
)
}
Can somebody please help me in fixing this code.
I have created two files called app.js and searchbooks.js for the project. But I received these two reviews.
Below is app.js
import React from 'react'
// import * as BooksAPI from './BooksAPI'
import './App.css';
import * as BooksAPI from './BooksAPI';
// import Header from './components/Header';
import BookShelf from './Components/BookShelf';
import SearchBooks from './Components/SearchBooks';
import {Route} from 'react-router-dom';
class BooksApp extends React.Component {
state = {
books:[]
}
componentDidMount(){
BooksAPI.getAll().then(data=>{
this.setState({
books:data
})
})
}
bookShelfHandler =(book,shelf)=>{
BooksAPI.update(book,shelf)
BooksAPI.getAll().then(data=>{
this.setState({
books:data
})
})
}
render() {
return (
<div className="app">
{/* <Route exact path="/" render={()=>(
<Header/>
)}/> */}
<Route exact path="/" render={()=>(
<BookShelf books={this.state.books} bookShelfHandler = {this.bookShelfHandler}/>
)}/>
<Route path="/search" render={()=>(
<SearchBooks bookShelfHandler= {this.bookShelfHandler}
books={this.state.books}
/>
)}/>
</div>
)
}
}
export default BooksApp
This is the searchbook.js file
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import Book from './Book'
import NotFound from './NotFound'
import * as BooksAPI from '../Utils/BooksAPI'
import PropTypes from 'prop-types'
class SearchBooks extends Component {
static propTypes = {
myBooks: PropTypes.array.isRequired,
onBookUpdate: PropTypes.func.isRequired
}
state = {
query: '',
books: [],
error: ''
}
bookShelfHandler() {
return this.props.bookShelfHandler;
}
handleChange = (query) => {
this.setState({ query })
this.bookSearch(query)
}
changeBookShelf = (books) => {
let all_Books = this.props.myBooks
for (let book of books) {
book.shelf = "none"
}
for (let book of books) {
for (const b of all_Books) {
if (b.id === book.id) {
book.shelf = b.shelf
}
}
}
return books
}
bookSearch = (query) => {
if (query) {
BooksAPI.search(query, 10)
.then((books) => {
if (books.length) {
books = books.filter((book) => (book.imageLinks))
// books = this.changeBookShelf(books)
this.setState({ books, error: '' })
} else {
this.setState({ books: [], error: 'error' })
}
})
} else {
this.setState({ books: [], query: ''})
}
}
addBook = (book, shelf) => {
this.props.onBookUpdate(book, shelf)
}
render () {
return (
<div className='search-books'>
<div className='search-books-bar'>
<Link className='close-search' to='/'>Close</Link>
<div className='search-books-input-wrapper'>
<input type='text'
placeholder='Search by title or author'
value={this.state.query}
onChange={(e) => (this.handleChange(e.target.value))} />
</div>
</div>
<div className='search-books-results'>
<ol className='books-grid'>
{this.state.query &&
this.state.books.map((book) => (<Book bookShelfHandler={this.props.bookShelfHandler} book={book} key={book.id} onUpdate={(shelf) => (this.addBook(book, shelf))}/>))}
{
this.state.error && <NotFound />
}
</ol>
</div>
</div>
)
}
}
export default SearchBooks
I tried fixing the code but still stuck at some points. Please help me in fixing the errors, the project is made on react and have to be submitted in 8 hours.
See the picture above to know more about the errors.
As per the review and from the code,
In you app.js file you are passing props
<SearchBooks bookShelfHandler= {this.bookShelfHandler}
books={this.state.books}
/>
but the required props in you component is
myBooks: PropTypes.array.isRequired,
onBookUpdate: PropTypes.func.isRequired
it just you are passing the wrong prop names to the component,
to fix this just change the names in your app.js file to
<SearchBooks onBookUpdate= {this.bookShelfHandler}
myBooks={this.state.books}
/>
I'm doing a project which does a get of the json-server, and render them on the screen.
But when I added a filtering function on it, it only renders after I type a name to filter. I wanted him to render everyone and make the filter.
My Body.js (Where is my function of render):
import React from 'react';
import './Body.css';
import { Link } from "react-router-dom";
class Body extends React.Component {
constructor(props) {
super(props);
this.state = {
input: "",
employeeBody: this.props.employee,
}
}
getName = () => {
const { employee, add } = this.props;
const {employeeBody} = this.state;
return employee.map(name => (
<div className='item'>
<Link className="link" to={`/user/${name.id}`}>
<div onClick={() => add(name)} key={name.id}>
<img className="img"
src={`https://picsum.photos/${name.id}`}
/>
</div>
<h1 className="name2"> {name.name} </h1>
</Link>
</div>
));
};
---
getValueInput = (evt) => {
const inputValue = evt.target.value;
this.setState({ input: inputValue });
this.filterNames(inputValue);
console.log(this.state.employeeBody)
}
filterNames (inputValue) {
const { employee } = this.props;
this.setState({
employeeBody: employee.filter(item =>
item.name.includes(inputValue))
});
}
---
render() {
return (
<div>
<div className="body">
{this.getName()}
</div>
<div className='input'>
<input type="text" onChange={this.getValueInput} />
</div>
</div>
)
}
}
export default Body;
My App.js (Where i get the state by get of axios.):
import React from 'react';
import {
BrowserRouter as Router,
Route
} from "react-router-dom";
import './App.css';
import axios from 'axios';
import Body from './Body';
import User from './User';
import Header from './Header';
class AppRouter extends React.Component {
state = {
employeeCurrent: [],
employee: []
};
componentDidMount() {
axios
.get("http://127.0.0.1:3004/employee")
.then(response => this.setState({
employee: response.data
}));
}
add = name => {
this.setState(prevState => {
const copy = prevState.employeeCurrent.slice(1);
copy.push(name);
return {
employeeCurrent: copy
};
});
};
render() {
return ( <
Router >
<
div className = "router" >
<
Header / >
<
Route exact path = "/"
render = {
props => ( <
Body { ...props
}
add = {
this.add
}
employee = {
this.state.employee
}
employeeCurrent = {
this.state.employeeCurrent
}
/>
)
}
/> <
Route path = "/user/:id"
component = {
props => ( <
User { ...props
}
employee = {
this.state.employee
}
employeeCurrent = {
this.state.employeeCurrent
}
/>
)
}
/> <
/div> <
/Router>
);
}
}
export default AppRouter;
Someone would can help me ?
You should filter in the render method.
render() {
const { employee: employees } = this.props; // rename the variable {employee} to plural {employees}, it has more sense.
const { input } = this.state;
return (
<div>
<div className="body">
{employees
.filter(employee => employee.name.includes(input))
.map(employee => {
<div className='item'>
<Link className="link" to={`/user/${employee.id}`}>
<div onClick={() => add(employee)} key={employee.id}>
<img className="img"
src={`https://picsum.photos/${employee.id}`}
/>
</div>
<h1 className="name2"> {employee.name} </h1>
</Link>
</div>
})}
</div>
<div className='input'>
<input type="text" onChange={(e) => this.setState({ input: e.target.value })} />
</div>
</div>
);
}
Remember that the method includes is case sensitive, it should be lowerCase it before to compare.
P.S.: You could also create a variable / component / function and render split all the "logic" of rendering there.
I am passing the state of the parent component from the parent component to the child component.And in the child component,I have a different state.I am performing some actions on the child component's state and the result of that has to be added to the parent component's state.So,in my parent component I have written a callback function which will update the state of the parent component.The code is:
updateState = (booksList) => {
this.setState({books : this.state.books.push(booksList)});
}
So,this function is then passed to the child component as props:
<BookSearch
books={this.state.books}
handleShelfChange={this.handleShelfChange}
updateState={this.updateState}/>
Then in my child component,I am trying to implement the callback function as :
let getBook = this.state.books.filter(filteredBook => filteredBook.shelf !== "none")
this.props.updateState(getBook)
But this is not working as expected.Is this the correct way?Can anyone please help me with this?
I have tried to solve my problem by implementing the solution provided here : How to pass data from child component to its parent in ReactJS? , but I am getting some errors.
EDIT
Parent component : App.js
import React from 'react'
import * as BooksAPI from './BooksAPI'
import { Link } from 'react-router-dom'
import { Route } from 'react-router-dom'
import './App.css'
import BookList from './BookList'
import BookSearch from './BookSearch'
class BooksApp extends React.Component {
constructor(props) {
super(props);
this.state = {
books: [],
showSearchPage : false
};
//this.updateState = this.updateState.bind(this)
}
componentDidMount() {
BooksAPI.getAll().then((books) => {
this.setState({ books })
})
console.log(this.state.books);
}
filterByShelf = (bookName,shelfName) =>
bookName.filter(book => book.shelf===shelfName)
isTheBookNew = book => {
let is = false;
if (book.shelf === "none")
{ this.setState(state =>
{
books: state.books.push(book)});
is = true;
console.log(this.state.books);
}
return is;
};
handleShelfChange = (bookOnChange, newSehlf) => {
!this.isTheBookNew(bookOnChange) && this.setState(state => {
let newBooks = state.books.map(book =>
{ if (bookOnChange.id === book.id)
{ book.shelf = newSehlf; }
return book;
});
return {
books: newBooks
};
}
);
BooksAPI.update(bookOnChange, newSehlf);
};
updateState = (booksList) => {
const books = [...this.state.books, booksList]
this.setState({ books });
}
render() {
return (
<div className="app">
<Route exact path="/" render={() => (
<div className="list-books">
<div className="list-books-title">
<h1>MyReads</h1>
</div>
<BookList
books={this.filterByShelf(this.state.books,'currentlyReading')}
shelfName='Currently Reading'
handleShelfChange={this.handleShelfChange}/>
<BookList
books={this.filterByShelf(this.state.books,'wantToRead')}
shelfName='Want to Read'
handleShelfChange={this.handleShelfChange}/>
<BookList
books={this.filterByShelf(this.state.books,'read')}
shelfName='Read'
handleShelfChange={this.handleShelfChange}/>
<div className="open-search">
<Link
to="./search" />
</div>
</div>
)
} />
<Route path="/search" render={() =>
<BookSearch
books={this.state.books}
handleShelfChange={this.handleShelfChange}
updateState={this.updateState}/>
} />
</div>
)
}
}
export default BooksApp
BookSearch.js :
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import escapeRegExp from 'escape-string-regexp'
import sortBy from 'sort-by'
import * as BooksAPI from './BooksAPI'
import BookList from './BookList'
class BookSearch extends Component {
constructor(props) {
super(props);
this.state = {
search:'',
books:[]
}
}
updateSearch = (searchString) => {
this.setState({search: searchString.trim()})
let searchResults = BooksAPI.search(this.state.search,1).then((book_search) => {
if (book_search != undefined) {
console.log(book_search);
book_search.map((book) => book.shelf = 'none');
this.setState({ books : book_search }, this.check); // callback function to this.setState
console.log(this.state.books)
}
})
}
check = () => {
let parent_books = this.props.books;
console.log(this.state.books)
const book_result = this.state.books.map((book) => {
const parent = parent_books.find(parent => parent.title === book.title );
if(parent) {
//console.log(parent);
book.shelf = parent.shelf;
//console.log(book)
}
return book;
})
this.setState({books: book_result}, () => {console.log(this.state.books)})
}
updateParentState = () => {
let getBook = this.state.books.filter(filteredBook => filteredBook.shelf !== "none")
this.props.updateState(getBook)
}
render() {
return(
<div className="search-books">
<div className="search-books-bar">
<Link
to="/"
className="close-search">
Close
</Link>
<div className="search-books-input-wrapper">
<input
type="text"
placeholder="Search by title or author"
value={this.state.search}
onChange={(event) => this.updateSearch(event.target.value)}/>
</div>
</div>
<div className="search-books-results">
<ol className="books-grid">
<BookList
books={this.state.books}
handleShelfChange={this.props.handleShelfChange}
updateParentState={this.updateParentState}/>
</ol>
</div>
</div>
)
}
}
export default BookSearch
BookList.js
import React, { Component } from 'react';
import Book from './Book'
class BookList extends Component {
constructor(props) {
super(props);
this.state = {
showSearchPage : false
}
console.log(this.props.books)
}
render() {
return(
<div className="app">
<div>
<div className="list-books-content">
<div>
<div className="bookshelf">
<h2 className="bookshelf-title">{this.props.shelfName}</h2>
<div className="bookshelf-books">
<ol className="books-grid">
{this.props.books.map(book =>
<li key={book.title}>
<Book
book={book}
handleShelfChange={this.props.handleShelfChange}
update={this.props.updateParentState} />
</li>)
}
</ol>
</div>
</div>
</div>
</div>
</div>
</div>
)
}
}
export default BookList;
Book.js
import React, { Component } from 'react'
class Book extends Component {
constructor(props) {
super(props);
this.props.updateParentState;
}
render() {
return(
<div className="book">
<div key={this.props.book.title}>
<div className="book-top">
<div className="book-cover" style={{width:128, height:193, backgroundImage: `url(${this.props.book.imageLinks.thumbnail})`}}>
<div className="book-shelf-changer">
<select id="bookName" value={this.props.book.shelf}
onChange={(event) => this.props.handleShelfChange(this.props.book, event.target.value)}>
<option value="moveTo" disabled>Move to...</option>
<option value="currentlyReading">Currently Reading</option>
<option value="wantToRead">Want to Read</option>
<option value="read">Read</option>
<option value="none">None</option>
</select>
</div>
</div>
</div>
<div className="book-title">{this.props.book.title}</div>
<div className="book-authors">{this.props.book.authors}</div>
</div>
</div>
)
}
}
export default Book
So, I have 4 components as shown above.From App component,I am calling BookSearch component where I can search for books and send the books to App component by selecting the value in dropdown. Each book in BookSearch component will initially be assigned a shelf property of "none".When a user selects a shelf value from Booksearch,the book will automatically be added to the App component.So,when I navigate back to the App component from BookSearch,I should be able to see the book in the assigned shelf.So,for this case,I am using the updateSearch function. The books are displayed through the Book component which has the dropdown value.
If i understood this correctly, you are mutating the state in the function updateState.
What you should be doing instead is,
const updateState = (booksList) => {
const books = [ ...this.state.books, ...booklist ];
this.setState({ books });
}
In parent component constructor add the following line:
this.updateState = this.updateState.bind(this)
More at https://reactjs.org/docs/handling-events.html