Would it be considered bad practice, using contextProvider without a consumer. Like I did below. I found out it worked just fine. Not exactly sure if it's the right way.
// context
import React, { useState } from 'react'
import { storeProducts, detailProduct } from './assets/data'
const ProductContext = React.createContext()
const ProductProvider = (props) => {
const [products, setProducts] = useState(storeProducts);
const [productDetails, setProductDetails] = useState(detailProduct)
console.log(products)
return (
<ProductContext.Provider value={{
products,
productDetails
}}>
{props.children}
</ProductContext.Provider>
)
}
const ProductConsumer = ProductContext.Consumer;
export { ProductProvider, ProductConsumer, ProductContext }
/// Context usage
import { ProductContext } from './context'
export default function ProductList() {
const appContext = useContext(ProductContext)
const { products } = appContext
console.log(appContext)
return (
<div className="py-5">
<div className="container">
<Title name="Product" title="Store"></Title>
<div className="row">
{/* <ProductConsumer>
{(product) => <{product}}
</ProductConsumer> */}
{products.map(p => {
return <h1>{p.title}</h1>
})}
</div>
</div>
</div>
It works just fine, just wanted to know it this could have side effects problems down the line.Or if it is outright discouraged and why.
Unlike class components, in functional components there is a different way to make use of context value with useContext hook and is an alternative to writing a Consumer and then using the render prop pattern.
export default function ProductList() {
const appContext = useContext(ProductContext)
const { products } = appContext
console.log(appContext)
return (
<div className="py-5">
<div className="container">
<Title name="Product" title="Store"></Title>
<div className="row">
{products.map(p => {
return <h1>{p.title}</h1>
})}
</div>
</div>
</div>
)
}
So, not defining a consumer doesn't mean you don't have a consumer of the context value
Doesn't seem like something bad you are doing -
Following is the useContext way of consuming the values -
import React, { useContext } from 'react';
// ...
function Display() {
const value = useContext(NumberContext);
return <div>The answer is {value}.</div>;
}
which seems like the way you are doing it.
"The only thing you want to watch out for is that you have to pass the whole context object to useContext – not just the consumer! React will warn you if you forget, but try to rememeber, eh?" - is the only difference between useContext hook and Comsumer.
For more details you can read about it -
https://daveceddia.com/usecontext-hook/
Related
I know same question probably asked multiple times. But I couldn't find the answer I'm looking for.
This is the code for Task:
import Navbar from './Navbar';
import "./Idea.css";
import GetData from '../restApiMethods/GetData';
import React from "react";
function Task() {
const ids = GetData("ideas/id");
return (
<div>
<Navbar />
<div className="idea-design">
<div className="container">
{
ids.map((id,index) => {
return (
<div key={index}>
{
GetData(`ideas/${id}`).map((task,index) => {
return(
<div key={index} className="row border border-secondary">
<div className="col">
<div>
<p>{task.taskDescription}</p>
</div>
</div>
</div>
)
})
}
</div>
)
})
}
</div>
</div>
</div>
)
}
export default Task
Getdata dunction:
import axios from "axios";
import {useState, useEffect} from "react";
function GetData(data) {
const [datas, setDatas] = useState([]);
useEffect(() =>{
const fetchData = () => {
axios.get(`http://localhost:8080/api/${data}`).then(res =>{
console.log(res);
setDatas(res.data);
});
};
fetchData();
}, []);
return datas;
}
export default GetData
If someone can give me some idea why I'm getting this error: Rendered more hooks than during the previous render, would be really helpful.
GetData actually is a custom hook because it's a function that calls hooks. Therefore subject to the rules of hooks.
It should be called useGetData -- I'll refer to it as that for this answer. You can't call it in a loop, as when the ids array changes length, the number of calls to useGetData will change in the parent component Task. This isn't allowed in React because hooks are supposed to be in a predictable order and never change -- it's a declarative model.
To fix this, break out a new component called Task (rename your current one to Tasks or whatever makes sense for you) and call it once in there. This doesn't break the rules of hooks as it is only within a component that the number of calls can't change between renders.
Tasks
import Navbar from "./Navbar";
import "./Idea.css";
import useGetData from "../restApiMethods/useGetData";
import React from "react";
import Task from "./Task";
function Tasks() {
const ids = useGetData("ideas/id");
return (
<div>
<Navbar />
<div className="idea-design">
<div className="container">
{ids.map((id, index) => {
return <Task id={id} key={id} />;
})}
</div>
</div>
</div>
);
}
export default Tasks;
Task
export default function Task({ id }) {
const data = useGetData(`ideas/${id}`);
return (
<div>
{data.map((task, index) => {
return (
<div key={index} className="row border border-secondary">
<div className="col">
<div>
<p>{task.taskDescription}</p>
</div>
</div>
</div>
);
})}
</div>
);
}
import axios from "axios";
import { useState, useEffect } from "react";
function useGetData(data) {
const [datas, setDatas] = useState([]);
useEffect(() => {
const fetchData = () => {
axios.get(`http://localhost:8080/api/${data}`).then((res) => {
console.log(res);
setDatas(res.data);
});
};
fetchData();
}, []);
return data;
}
export default useGetData;
I'm new to react and trying to learn by building this simple app. I want to expose data from parent app in a child component but my result in the component appears to be empty.
what should I do to get the result to appear and re-render in the child component?
import React, { useState, useEffect } from "react";
import socketIOClient from "socket.io-client";
import Main from "./Main";
import Response from "./response"
const ENDPOINT = "http://127.0.0.1:4001";
function App() {
const [response, setResponse] = useState([]);
useEffect(() => {
const socket = socketIOClient(ENDPOINT);
socket.on("tick", (data) => {
setResponse(data);
});
}, []);
console.log(response);
return (
<>
<div style={{ textAlign: "center" }}>
<Main
responseCurrency = {response.map(memb => <div>{memb.currencyPairName}</div>)}
dataObject = {response}
/>
</div>
</>
);
}
export default App;
import './Main.css';
function Main(props) {
const [result, setResult] = React.useState(props.dataObject);
React.useEffect(() => {
console.log(result)
setResult(result);
}, [result]);
return <div className="container">
{result[0]}
<div id="palceholder1" className="placeholder">hello</div>
<div id="palceholder2" className="placeholder"></div>
<div id="palceholder3" className="placeholder"></div>
</div>;
}
export default Main;
I am a beginner in ReactJS, so I'm lost at the moment.
Hope someone could shed a light..
Thanks in advance
Gali
function Main(props) {
const [result, setResult] = React.useState(props.dataObject);
The problem is that you're copying props into state. The above line of code says to create a new state for Main, who's initial value is props.dataObject. When props.dataObject changes later, nothing will happen to this state.
Instead, you should use the prop directly:
function Main(props) {
return (
<div className="container">
{props.dataObject[0]}
<div id="palceholder1" className="placeholder">
hello
</div>
<div id="palceholder2" className="placeholder"></div>
<div id="palceholder3" className="placeholder"></div>
</div>
);
}
I am working on a React project, In my project I have four components those are App, Componentc,
Componente, Componentf. Now I am trying to pass an Array from App to Componentf using Context API
I successfuly passed an Array, but the problem is in output the Array is showing like side by
side. but what I am expecting it has to show like Unordered list in html
Please help me to acheive this
This is App.js
import React from 'react';
import Componentc from './Componentc/Componentc';
// import './App.css';
export const UserContext = React.createContext()
const fruits = ['Apple','Orange','Banana','Grapes']
function App() {
return (
<div className="App">
<UserContext.Provider value={fruits}>
<Componentc></Componentc>
</UserContext.Provider>
</div>
);
}
export default App;
This is Componentc
import React from 'react';
import './Componentc.css';
import Componente from '../Componente/Componente';
const Componentc = () => {
return(
<Componente></Componente>
)
}
export default Componentc
This is Componente
import React from 'react';
import './Componente.css';
import Componentf from '../Componentf/Componentf';
const Componente = () => {
return(
<Componentf></Componentf>
)
}
export default Componente
This is Componentf
import React from 'react';
import './Componentf.css';
import { UserContext } from '../App'
const Componentf = () => {
return (
<div>
<UserContext.Consumer>
{
user => {
return <div className='d-block'>{user}</div>
}
}
</UserContext.Consumer>
<h1>Component F</h1>
</div>
)
}
export default Componentf
The value of your context is an array, but you are treating it like an object in the context consumer.
You only need to change return <div className='d-block'>{user}</div> by :
{user => {
return user.map(t => (
<div key={t} className="d-block">
{t}
</div>
));
}}
Although your variables should have meaningful names; I recommend changing the name of the user context and variable to be fruits.
Also, If you are using a recent react version (> 16.8), I also recommend that you use React.useContext API to receive values from context, code become more readable.
const Componentf = () => {
const fruits = React.useContext(FruitContext);
return (
<div>
{fruits.map(fruit => (
<div key={fruit} className="d-block">
{fruit}
</div>
))}
<h1>Component F</h1>
</div>
);
};
Here is a codesandbox demo
I'm trying to use React Context to update navbar title dynamically from other child components. I created NavbarContext.js as follows. I have wrapped AdminLayout with NavContext.Provider and use useContext in Course.js to dynamically update navbar title inside useEffect. However, when I'm doing this, react throws the following error on the screen.
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
How can I use context properly so that I can update Header title from Course.js inside its useEffect?
NavbarContext.js
import React, {useState} from 'react'
export default () => {
const [name,setName] = useState("")
const NavContext = React.createContext({
name: "",
changeName: name => setName(name)
})
const NavProvider = NavContext.Provider
const NavConsumer = NavContext.Consumer
return NavContext
}
AdminLayout.js
<NavContext.Provider>
<div className={classes.wrapper}>
<Sidebar
routes={routes}
logoText={"Widubima"}
logo={logo}
image={image}
handleDrawerToggle={handleDrawerToggle}
open={mobileOpen}
color={color}
{...rest}
/>
<div className={classes.mainPanel} ref={mainPanel}>
<Navbar
routes={routes}
handleDrawerToggle={handleDrawerToggle}
{...rest}
/>
{/* On the /maps route we want the map to be on full screen - this is not possible if the content and conatiner classes are present because they have some paddings which would make the map smaller */}
{getRoute() ? (
<div className={classes.content}>
<div className={classes.container}>{switchRoutes}</div>
</div>
) : (
<div className={classes.map}>{switchRoutes}</div>
)}
</div>
</div>
</NavContext.Provider>
Navbar.js
import NavContext from "context/NavbarContext"
export default function Header(props) {
function makeBrand() {
var name;
props.routes.map(prop => {
if (window.location.href.indexOf(prop.layout + prop.path) !== -1) {
name = prop.name;
document.title = name;
}
return null;
});
return name;
}
return (
<AppBar className={classes.appBar + appBarClasses}>
<Toolbar className={classes.container}>
<div className={classes.flex}>
{/* Here we create navbar brand, based on route name */}
<NavContext.Consumer>
{({ name, setName }) => (
<Button
color="transparent"
href="#"
className={classes.title}
style={{ fontSize: "1.5em", marginLeft: "-2%" }}
>
{makeBrand() || name}
</Button>
)}
</NavContext.Consumer>
</Toolbar>
</AppBar>
);
}
Course.js
import React, { useState, useEffect, useContext } from "react";
import NavContext from "context/NavbarContext"
const AdminCourse = props => {
const context = useContext(NavContext);
useEffect(() => {
Axios.get('/courses/'+props.match.params.courseId).then(
res => {
context.changeName("hello")
}
).catch(err => {
console.log(err)
})
return () => {
setCourseId("");
};
});
return (
<GridContainer>
</GridContainer>
);
};
export default AdminCourse;
i think problem is there with your NavbarContext.js.
you are not exporting NavContext also.
you are defining provider, consumer but you are not using them either.
here's how you can solve your problem.
first create context and it's provider in a file as following.
NavContext.js
import React, { useState } from "react";
const NavContext = React.createContext();
const NavProvider = props => {
const [name, setName] = useState("");
let hookObject = {
name: name,
changeName: setName
};
return (
<NavContext.Provider value={hookObject}>
{props.children}
</NavContext.Provider>
);
};
export { NavProvider, NavContext };
in above code first i am creating context with empty value.
the i am creating NavProvider which actually contains value name as a state hook inside it.hookObject exposes state as per your naming conventions in code.
now i for testing purpose i defined two consumers.
one is where we update name in useEffect, that is ,
ConsumerThatUpdates.js
import React, { useContext, useEffect } from "react";
import { NavContext } from "./NavContext";
const ConsumerThatUpdates = () => {
const { changeName } = useContext(NavContext);
useEffect(() => {
changeName("NEW NAME");
}, [changeName]);
return <div>i update on my useeffect</div>;
};
export default ConsumerThatUpdates;
you can update useEffect as per your needs.
another is where we use the name,
ConsumerThatDisplays.js
import React, { useContext } from "react";
import { NavContext } from "./NavContext";
const ConsumerThatDisplays = () => {
const { name } = useContext(NavContext);
return <div>{name}</div>;
};
export default ConsumerThatDisplays;
and finally my App.js looks like this,
App.js
import React from "react";
import "./styles.css";
import { NavProvider } from "./NavContext";
import ConsumerThatDisplays from "./ConsumerThatDisplays";
import ConsumerThatUpdates from "./ConsumerThatUpdates";
export default function App() {
return (
<div className="App">
<NavProvider>
<ConsumerThatDisplays />
<ConsumerThatUpdates />
</NavProvider>
</div>
);
}
hope this helps!!
if you want to know more about how to use context effectively, i recooHow to use React Context effectively
I'm somewhat confused on the relationship between functional components in React and the Render Props and HOC patterns.
That is,
is it true that the only way to create Render Prop is with a class component?
is it true that the only way to create an HOC is with a class component?
And the same for usage.
I'm trying to find examples of Render Props and HOC's with functional components and all I find are class components. I get that React Hooks do a lot of the same, but I'm trying to understand how the Render Props and HOC patterns can apply to functional components (or if they do at all)
Edit Below:
Applying what #chaimFriedman suggested, this is what I came up with using no class or component for an HOC hoping it makes sense.
import React, { useState, useEffect } from 'react';
import useAxios from 'axios-hooks';
function withFetching(url) {
return function(Speakers) {
return () => {
const [speakerData, setSpeakerData] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [{ data, loading }, refetch] = useAxios('http://localhost:4000/speakers');
useEffect(() => {
setSpeakerData(data);
setIsLoading(loading);
}, [loading]);
if (isLoading) return <div>loading..</div>;
return <Speakers data={speakerData}></Speakers>;
};
};
}
const Speakers = function(props) {
//debugger;
return (
<ul>
{props.data.map((speaker) => (
<li key={speaker.id}>
<span>
{speaker.firstName} {speaker.lastName}
</span>
</li>
))}
</ul>
);
};
const API = 'http://localhost:4000/speakers';
export default withFetching(API)(Speakers);
Both render props and HOC can absolutely apply to functional components. Let's think a little more about what each one really is to see why they do in fact work with functions as well as classes.
Render props is when you have a prop that is a function which returns JSX. This of course should work for function components because aside from life cycle methods there really isnt much that is different than class components. Here is an example with code.
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function Renderer(props) {
return (
props.children()
);
}
function App() {
return (
<div className="App">
<Renderer>
{() => {
return (
<h1>I am being rendered by Renderer</h1>
);
}}
</Renderer>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Now for HOC.
A HOC really is just a higher order function, but because we use it in react we call it a higher order component. A higher order function is a function which either accepts a function as an argument, or returns a function. Now a functional component can absolutely do this. Here is an example.
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function Renderer(Wrapped) {
return function New(props) {
return <Wrapped {...props} />
}
}
function Child(props) {
return (
<h1>Hello {props.name}</h1>
);
}
function App() {
const C = Renderer(Child)
return (
<div className="App">
<C name="john" />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
EDIT: I realized my HOC example was wrong so updated.
I hope this helps.
Here is the sample code converting example from React documentation into function component. https://reactjs.org/docs/render-props.html
import React from "react";
const Cat = ({mouse}) => {
return (
<img
src="/cat.png"
alt="cat"
style={{ position: "absolute", left: mouse.x, top: mouse.y }}
/>
);
};
const Mouse = (props) => {
const [state, setState] = React.useState();
const handleMouseMove = (event) => {
setState({
x: event.clientX,
y: event.clientY
});
};
return (
<div style={{ height: "100vh" }} onMouseMove={handleMouseMove}>
{props.render(state)}
</div>
);
};
const MouseTracker = () => {
return (
<div>
<h1>Move the mouse around!</h1>
<Mouse render={(mouse) => <Cat mouse={mouse} />} />
</div>
);
};
export const App = () => {
return (
<div className="App">
<MouseTracker />
</div>
);
}