I have a context file and state file for Products. when I try to use that context in products component it is returning undefined values
productContext.js
import { createContext } from "react";
const ProductContext = createContext();
export default ProductContext;
ProductState.js
import ProductContext from './productContext';
import { useState } from 'react';
const ProductState = (props) => {
const productsInitial = [
{
"_id": "63a4cc857f40d0063116be5f",
"user": "63a4cbfd7f40d0063116be5d",
"title": "Cassava",
"description": "On-demand sand castle construction expertise.",
"imgURL": "null",
"price": "30",
"__v": 0
},
{
"_id": "63a4ccad7f40d0063116be69",
"user": "63a4cbfd7f40d0063116be5d",
"title": "Soyabeans",
"description": "On-demand sand castle construction expertise.",
"imgURL": "null",
"price": "30",
"__v": 0
}
]
const [products, setProducts] = useState(productsInitial);
// console.log(products);
return (
<>
<ProductContext.Provider value={{ products, setProducts }}>
{props.children}
</ProductContext.Provider>
</>
)
}
export default ProductState;
ProductComponent.js (react component)
import React, { useContext } from 'react';
import ProductContext from '../context/products/productContext';
import ProductItems from './ProductItems';
console.log(ProductContext);
const ProductComponent = () => {
const context = useContext(ProductContext);
const { products, setProducts } = context;
return (
<>
<div className="col-lg-4 ">
{products.map((product) => {
return <ProductItems product={products} />
})}
</div>
</>
)
}
export default ProductComponent;
the line
const context = useContext(ProductContext);
in Products.js Component is returning undefined value because ProductContext is returning undefined
Component that use the context must be wrapped using ProductState component
If you don't know where to add ProductState you can add in your entry point file.
root.render((
<ProductState>
<App />
</ProductState>
))
You can simplify the object destructuring
const { products, setProducts } = useContext(ProductContext);
Related
I'm trying to create this logic a whole day and can't find anything helpful.. A small introduce of my app: While studying, I'm creating my local date app as practical job to improve more skills and logics. The problem is that I'm trying to get single user in main(home) page, when user login it should see already loaded single user from MongoDB where he can click like or dislike button. When user clicks one of them this user will push in user liked or dislike array and next user displays. I don't have like system yet, cause I can't get a single user. So later I wanna do filter method if user already liked or disliked other user, that one user didn't appear anymore and let users view his liked and disliked users.
So the main thing is that I have a get method where I'm getting all users and tried to create different routes to get only one of them, but can't imagine how to create correct logic of that.. 4-5 hours ago I tried to make for, map, forEach cycles but still can't understand the main logic here..
Here's my code, if it's not enough to understand just ask me for more code.
mainController(back-end):
getSingleUser: async (req, res) => {
const { secret } = req.params;
const findUser = await UserSchema.findOne({ secret });
if (findUser) {
return res.send({ error: false, message: 'User found', data: findUser });
}
return res.send({ error: true, message: 'User not found', data: null });
},
getAllUsers: async (req, res) => {
try {
const allUser = await UserSchema.find({});
res.status(200).json(allUser);
} catch (error) {
res.status(400).json({ message: error.message });
}
}
mainRouter(back-end):
const express = require('express')
const { login, register, getSingleUser, getAllUsers } = require("../controller/mainController")
const { loginValidate, registerValidate } = require("../middleware/authValidator")
const mainRouter = express.Router()
mainRouter.post('/register', registerValidate, register);
mainRouter.post('/login', loginValidate, login);
mainRouter.get('/api', getAllUsers)
mainRouter.get('/user/:secret', getSingleUser)
module.exports = mainRouter;
HomePage(front-end):
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import Toolbar from '../components/Toolbar';
import { get } from '../helper/helper';
import SingleUser from '../components/SingleUser';
export default function HomePage() {
const [allUsers, getAllUsers] = useState([])
const secret = window.localStorage.getItem('secret')
useEffect(() => {
async function fetchUsers() {
const resp = await get('api')
getAllUsers(resp)
}
fetchUsers()
}, [])
return (
<div className='home__page'>
<Toolbar />
<div className="home__users">
{allUsers && allUsers.filter(user => user.secret !== secret).map((users, i) => <SingleUser users={users} key={i} />)}
</div>
<footer className='footer'>
<p>All rights reserved by Cartoon Match organization. To read more about our policy <Link to='/policy'>click here</Link>. </p>
</footer>
</div>
)
}
This is result of HomePage users fetch:
[
{
"_id": "638e31a2579ba2a13b454943",
"image": [
"https://picsum.photos/id/377/4884/3256",
"https://picsum.photos/id/378/5000/3333"
],
"username": "testas",
"firstName": "testas",
"lastName": "testas",
"email": "test#gmail.com",
"gender": "male",
"city": "asdadas",
"country": "aasdasd",
"birth": "2020-02-20",
"phone": "65asd15a6sd",
"password": "$2b$04$zpqQVU7cFUfdA9DCfKmFYOOdY93OVPXoQq08kM87ehwwxD25/pl6q",
"likes": [],
"liked": [],
"secret": "Ey2_-dVlpg",
"__v": 0
},
{
"_id": "639130a43c2e51dc08531019",
"image": [
"https://www.1zoom.me/big2/98/183590-20043.jpg",
"https://pngimg.com/uploads/shrek/shrek_PNG22.png"
],
"username": "SirShrek",
"firstName": "Sir",
"lastName": "Shrek",
"email": "callmeshrek#swamp.com",
"gender": "male",
"city": "Swamp",
"country": "Fairy-Tale Land Of Duloc",
"birth": "1999-02-27",
"phone": "830-476-5664",
"password": "$2b$04$u1jacGrx6yWOMxfkllMxWO.5in4rQDFIbbCMMixzHqV9rObtq1tKG",
"likes": [],
"liked": [],
"secret": "FP8Rd4izgQ",
"__v": 0
},
{
"_id": "6391c8df7f83052432e2d939",
"image": [
"https://static.wikia.nocookie.net/vsbattles/images/e/e8/Dexter_Rendered.png/revision/latest?cb=20180919045250",
"https://i.ebayimg.com/images/g/VesAAOSwrohehKhn/s-l1600.jpg"
],
"username": "Dexter",
"firstName": "Dexter",
"lastName": "Michael Louis McPherson lll",
"email": "dexter#science.com",
"gender": "male",
"city": "Genius Grove",
"country": "Peter Lorre",
"birth": "2014-12-31",
"phone": "555-0100",
"password": "$2b$04$BMUidKOaRBtWDNKH.NduB.gkgzUhPNuXVk10ip7lPo/N/1k/8sxvW",
"likes": [],
"liked": [],
"dislike":[],
"secret": "9k6ilC2ZZg",
"__v": 0
}]
This is SingleUser component:
import React, { useEffect, useState } from "react";
import MainContext from '../context/MainContext'
import { get, put } from '../helper/helper'
export default function SingleUser({ users }) {
return (
<div className='single__user'>
<img src={users.image[0]} alt="" />
<h3>{users.firstName} {users.lastName}</h3>
<h4>{users.gender}</h4>
<button>Dislike</button>
<button onClick={postLikes}>Like</button>
</div>
);
}
I want it to read the json in utf format and display in terminal
import * as fs from 'node:fs';
export default function handler(req, res) {
fs.readFile("/blogdata/how-to-learnjavascript.json","utf-8",(err,data)=>{console.log(data)})
res.status(200).json({ name: 'John Doe' })
}
If You want to use fs module in Next.js.You can only do FS-related operations while on the server, within getStaticProps or getServerSideProps
Here You Have simple example how to use fs and path module with getStaticProps:
index.js (home page)
import fs from "fs";
import path from "path";
export default function Home({ data }) {
const persons = data.persons;
return (
<div style={{ padding: 30 }}>
<h1>Example Next.js & Node.js FS module</h1>
<div>
{persons.map((person) => (
<div
key={person.id}
style={{ padding: 20, borderBottom: "1px solid #ccc" }}
>
<h2>{person.name}</h2>
<p>{person.email}</p>
</div>
))}
</div>
</div>
);
}
export async function getStaticProps(context) {
const dataFilePath = path.join(process.cwd(), "data.json");
const fileContents = fs.readFileSync(dataFilePath, "utf8");
const data = JSON.parse(fileContents);
console.log(data);
return { props: { data } };
}
data.json
{
"persons": [
{
"id": 1,
"name": "dolores",
"email": "dolores#gmail.com"
},
{
"id": 2,
"name": "ed",
"email": "ed#yahoo.com"
},
{
"id": 3,
"name": "james",
"email": "james007#yahoo.com"
},
{
"id": 4,
"name": "joda",
"email": "joda-jedi#gmail.com"
}
]
}
Output(server side) folder and file structure:
Output browser:
Tested with "next": "12.1.6"
import logo from "./logo.svg";
import "./App.css";
import Navbar from "./Components/Navbar";
import ProductList from "./Components/ProductList";
import React, { useState } from "react";
function App() {
const productList = [
{
price: 9999,
name: "IPhone 10S Max",
quantity: 0,
},
{
price: 999,
name: "Redmi Note 10S Max",
quantity: 0,
},
];
let [productList , setProductList] = useState(productList);
const incrementQuantity = (index) => {
let newProductList = [...productList];
newProductList[index].quantity++;
setProductList(newProductList);
};
const decrementQuantity = (index) => {
let newProductList = [...productList];
newProductList[index].quantity > 0
? newProductList[index].quantity--
: (newProductList[index].quantity = 0);
setProductList(newProductList);
};
return (
<>
<Navbar />
<main className="container mt-5">
<ProductList
productList={productList}
incrementQuantity={incrementQuantity}
decrementQuantity={decrementQuantity}
/>
</main>
{/*<Footer/>*/}
</>
);
}
export default App;
**Line 21:7: Parsing error: Identifier 'productList' has already been declared. (21:7)**
Not able fetch the right output because of the parsing error.
Your state name of the value is the same like the object name:
//try to name it smth like initialProductList and pass it to useState(initalProductList)
const productList = [
{
price: 9999,
name: "IPhone 10S Max",
quantity: 0,
},
{
price: 999,
name: "Redmi Note 10S Max",
quantity: 0,
},
];
let [productList , setProductList] = useState(productList);
Also,
useState() shuld be defind with const like so:
const [productList , setProductList] = useState(productList);
I'm running into a funny problem. I'm using NextJS for its server-side rendering capabilities and am using ReactQuill as my rich-text editor. To get around ReactQuill's tie to the DOM, I'm dynamically importing it. However, that presents another problem which is that when I try to attach a ref to the ReactQuill component, it's treated as a loadable component instead of the ReactQuill component. I need the ref in order to customize how images are handled when uploaded into the rich-text editor. Right now, the ref returns current:null instead of the function I can use .getEditor() on to customize image handling.
Anybody have any thoughts on how I can address this? I tried ref-forwarding, but it's still applying refs to a loadable component, instead of the React-Quill one. Here's a snapshot of my code.
const ReactQuill = dynamic(import('react-quill'), { ssr: false, loading: () => <p>Loading ...</p> }
);
const ForwardedRefComponent = React.forwardRef((props, ref) => {return (
<ReactQuill {...props} forwardedRef={(el) => {ref = el;}} />
)})
class Create extends Component {
constructor() {
super();
this.reactQuillRef = React.createRef();
}
imageHandler = () => {
console.log(this.reactQuillRef); //this returns current:null, can't use getEditor() on it.
}
render() {
const modules = {
toolbar: {
container: [[{ 'header': [ 2, 3, false] }],
['bold', 'italic', 'underline', 'strike'],
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
[{ 'script': 'sub'}, { 'script': 'super' }],
['link', 'image'],
[{ 'indent': '-1'}, { 'indent': '+1' }],
[{ 'align': [] }],
['blockquote', 'code-block'],],
handlers: {
'image': this.imageHandler
}
}
};
return(
<ForwardedRefComponent
value={this.state.text}
onChange={this.handleChange}
modules={modules}
ref={this.reactQuillRef}/> //this.reactQuillRef is returning current:null instead of the ReactQuill function for me to use .getEditor() on
)
}
}
const mapStateToProps = state => ({
tutorial: state.tutorial,
});
export default connect(
mapStateToProps, {createTutorial}
)(Create);
I share my solution with hope that it helps you too.
Helped from https://github.com/zenoamaro/react-quill/issues/642#issuecomment-717661518
const ReactQuill = dynamic(
async () => {
const { default: RQ } = await import("react-quill");
return ({ forwardedRef, ...props }) => <RQ ref={forwardedRef} {...props} />;
},
{
ssr: false
}
);
export default function QuillWrapper() {
const quillRef = React.useRef(false)
return <>
<ReactQuill forwardedRef={quillRef} />
</>
}
for example you can use the ref to upload image with custom hanlder
import React, { useMemo } from "react";
import dynamic from "next/dynamic";
const ReactQuill = dynamic(
async () => {
const { default: RQ } = await import("react-quill");
return ({ forwardedRef, ...props }) => <RQ ref={forwardedRef} {...props} />;
},
{
ssr: false,
}
);
export default function QuillWrapper({ value, onChange, ...props }) {
const quillRef = React.useRef(false);
// Custom image upload handler
function imgHandler() {
// from https://github.com/quilljs/quill/issues/1089#issuecomment-318066471
const quill = quillRef.current.getEditor();
let fileInput = quill.root.querySelector("input.ql-image[type=file]");
// to prevent duplicate initialization I guess
if (fileInput === null) {
fileInput = document.createElement("input");
fileInput.setAttribute("type", "file");
fileInput.setAttribute(
"accept",
"image/png, image/gif, image/jpeg, image/bmp, image/x-icon"
);
fileInput.classList.add("ql-image");
fileInput.addEventListener("change", () => {
const files = fileInput.files;
const range = quill.getSelection(true);
if (!files || !files.length) {
console.log("No files selected");
return;
}
const formData = new FormData();
formData.append("file", files[0]);
formData.append("uid", uid);
formData.append("img_type", "detail");
quill.enable(false);
console.log(files[0]);
axios
.post("the/url/for/handle/uploading", formData)
.then((response) => {
// after uploading succeed add img tag in the editor.
// for detail visit https://quilljs.com/docs/api/#editor
quill.enable(true);
quill.insertEmbed(range.index, "image", response.data.url);
quill.setSelection(range.index + 1);
fileInput.value = "";
})
.catch((error) => {
console.log("quill image upload failed");
console.log(error);
quill.enable(true);
});
});
quill.root.appendChild(fileInput);
}
fileInput.click();
}
I don't know much about useMemo
but if i don't use the hook,
the editor keeps rerendered resulting in losing focus and I guess perfomance trouble too.
const modules = useMemo(
() => ({
toolbar: {
container: [
[{ font: [] }],
[{ size: ["small", false, "large", "huge"] }], // custom dropdown
["bold", "italic", "underline", "strike"], // toggled buttons
[{ color: [] }, { background: [] }], // dropdown with defaults from theme
[{ script: "sub" }, { script: "super" }], // superscript/subscript
[{ header: 1 }, { header: 2 }], // custom button values
["blockquote", "code-block"],
[{ list: "ordered" }, { list: "bullet" }],
[{ indent: "-1" }, { indent: "+1" }], // outdent/indent
[{ direction: "rtl" }], // text direction
[{ align: [] }],
["link", "image"],
["clean"], // remove formatting button
],
handlers: { image: imgHandler }, // Custom image handler
},
}),
[]
);
return (
<ReactQuill
forwardedRef={quillRef}
modules={modules}
value={value}
onChange={onChange}
{...props}
/>
);
}
In NextJS, React.useRef or React.createRef do not work with dynamic import.
You should Replace
const ReactQuill = dynamic(import('react-quill'), { ssr: false, loading: () => <p>Loading ...</p> }
);
with
import ReactQuill from 'react-quill';
and render after when window is loaded.
import ReactQuill from 'react-quill';
class Create extends Component {
constructor() {
super();
this.reactQuillRef = React.createRef();
this.state = {isWindowLoaded: false};
}
componentDidMount() {
this.setState({...this.state, isWindowLoaded: true});
}
.........
.........
render(){
return (
<div>
{this.isWindowLoaded && <ReactQuil {...this.props}/>}
</div>
)
}
}
Use onChange and pass all the arguments, here one example to use the editor.getHTML()
import React, { Component } from 'react'
import dynamic from 'next/dynamic'
import { render } from 'react-dom'
const QuillNoSSRWrapper = dynamic(import('react-quill'), {
ssr: false,
loading: () => <p>Loading ...</p>,
})
const modules = {
toolbar: [
[{ header: '1' }, { header: '2' }, { font: [] }],
[{ size: [] }],
['bold', 'italic', 'underline', 'strike', 'blockquote'],
[
{ list: 'ordered' },
{ list: 'bullet' },
{ indent: '-1' },
{ indent: '+1' },
],
['link', 'image', 'video'],
['clean'],
],
clipboard: {
// toggle to add extra line breaks when pasting HTML:
matchVisual: false,
},
}
/*
* Quill editor formats
* See https://quilljs.com/docs/formats/
*/
const formats = [
'header',
'font',
'size',
'bold',
'italic',
'underline',
'strike',
'blockquote',
'list',
'bullet',
'indent',
'link',
'image',
'video',
]
class BlogEditor extends Component {
constructor(props) {
super(props)
this.state = { value: null } // You can also pass a Quill Delta here
this.handleChange = this.handleChange.bind(this)
this.editor = React.createRef()
}
handleChange = (content, delta, source, editor) => {
this.setState({ value: editor.getHTML() })
}
render() {
return (
<>
<div dangerouslySetInnerHTML={{ __html: this.state.value }} />
<QuillNoSSRWrapper ref={this.editor} onChange={this.handleChange} modules={modules} formats={formats} theme="snow" />
<QuillNoSSRWrapper value={this.state.value} modules={modules} formats={formats} theme="snow" />
</>
)
}
}
export default BlogEditor
If you want to use ref in Next.js with dynamic import
you can use React.forwardRef API
more info
I am trying to setState from this data,
var axios = require('axios');
import Trails from './trails';
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
this.state = {
trails: []
}
}
componentWillMount() {
axios
.get('https://www.mtbproject.com/data/get-trails-by-id?ids=2081068,830442%208013961&key=(API-KEY)')
.then(response => response.data)
.then(trails => this.setState({trails}));
}
which looks like this:
{
"trails": [
{
"id": 2081068,
"name": "San Dieguito River Park - Bernardo Bay\/ Piedras Pintadas Trail",
"type": "Featured Ride",
"summary": "Sweet little loop of singletrack trails.",
"difficulty": "blue",
"stars": 3.6,
"starVotes": 24,
"location": "Escondido, California",
"url": "https:\/\/www.mtbproject.com\/trail\/2081068\/san-dieguito-river-park-bernardo-bay-piedras-pintadas-trail",
"imgSqSmall": "https:\/\/cdn-files.apstatic.com\/mtb\/2148715_sqsmall_1372258680.jpg",
"imgSmall": "https:\/\/cdn-files.apstatic.com\/mtb\/2148715_small_1372258680.jpg",
"imgSmallMed": "https:\/\/cdn-files.apstatic.com\/mtb\/2148715_smallMed_1372258680.jpg",
"imgMedium": "https:\/\/cdn-files.apstatic.com\/mtb\/2148715_medium_1372258680.jpg",
"length": 8.2,
"ascent": 570,
"descent": -567,
"high": 488,
"low": 317,
"longitude": -117.0766,
"latitude": 33.0512,
"conditionStatus": "All Clear",
"conditionDetails": "Dry",
"conditionDate": "2018-09-11 09:12:17"
}
],
"success": 1
}
Then I am trying to map it like this:
render() {
return (
<div className='App'>
<div className="container">
<div className="jumbotron">
<h4>Mtb</h4>
<p>Trails:</p>
</div>
{this.state.trails.map(trail => (
<Trails key={trail.id}
conditions={trail.conditionDetails}
/>
))
}
</div>
</div>
);
}
}
I then get an error saying that my map method is not a function. Can someone point out what I am doing wrong?
When I console.log my state it appears that it is not being set, might this be the issue and be the explanation for why it is not working?
You are setting trails to be the entire data object you get in response to your request. Use the trails property of the data object instead.
componentWillMount() {
axios
.get('https://www.mtbproject.com/data/get-trails-by-id?ids=2081068,830442%208013961&key=(API-KEY)')
.then(response => this.setState({ trails: response.data.trails }));
}