I can't access the Context Provider in useContext hook in React - javascript

I created a Context API for menu bar and it is my first time using it, and that also I am using it with typescript which i started learning few days ago.
Context Creator and Provider Code in App.jsx
type MenuBar = {
menu: boolean
setMenu: any
}
function App() {
const MenuBarContext = React.createContext<Partial<MenuBar>>({})
const [menu, setMenu] = useState<boolean>(false)
return (
<MenuBarContext.Provider value={{ menu, setMenu }}>
<div className="h-full font-primary-400">
<Router>
<Navbar />
<Routes>
<Route path="/" element={<HomePage />} />
</Routes>
</Router>
</div>
</MenuBarContext.Provider>
)
}
Accesing using UseContext Hook in Navbar Component
const { menu, setMenu } = useContext(MenuBarContext)
I want to pass the state in the context API and access it in different components. But the accessing the context API using useContext hook gives error as -
Cannot find name 'MenuBarContext'.

Define the context in a separate file and import it where you need it.
MenuBarContext.ts:
import React from 'react';
// other imports...
const MenuBarContext = React.createContext<Partial<MenuBar>>({});
export default MenuBarContext;
Import it like so when you need to use MenuBarContext.Provider or useContext(MenuBarContext):
import MenuBarContext from './MenuBarContext';

Related

Failing to Access Outlet Context in React

Okay so I have scoured the internet for an example of how to do this but unfortunately I am not able to do so. Basically I have a componenet structure like this:
App.js
class App extends Componenent {
render() {
return (
<Routes>
<Route path='/signin' exact element={<SignIn />} />
<Route path='/admin' exact element={<Admin />} >
<Route path='home' exact element={<Home/>} />
<Route path='settings' exact element={<Settings/>} />
</Route >
);
}
}
export default App;
admin.jsx
import { useLocation, Outlet } from "react-router-dom";
const Admin = props => {
const location = useLocation();
return (
<div>
<p>Parent</p>
<div>
<Outlet context={'foo'} />
</div>
</div>
}
export default Admin;
settings.jsx
import React from "react";
const Settings = props => {
const context = useOutletContext();
console.log(context);
return (
<React.Fragment>
<p>settings</p>
</React.Fragment>
}
export default Settings;
However, each time I load the page, I get a an error that says exactly:
'useOutletContext' is not defined no-undef
and the app crashes. However, when I look at my componenet tree with the chrome react debug panel, the context is there in the outlet, I just don't know how to access it. Here are some screenshots so that we are on the same page:
Context is in the outlet
The same data is in the Context.Provider as "value" now
Nowhere to be seen in the Route.Provider
Nowhere to be seen in the Settings Componenet
Any and all help here would be appreciated, I am just not entirely sure of how to use useOuletContext(); even if I used followed the steps in the docs. Do I have to import it from somewhere? Does it have to be in the same file for it to work?
Yes, it still needs to be imported in the file using it, i.e. import { useOutletContext } from 'react-router-dom';.
import React from "react";
import { useOutletContext } from 'react-router-dom';
const Settings = props => {
const context = useOutletContext();
console.log(context);
return (
<React.Fragment>
<p>settings</p>
</React.Fragment>
);
}
export default Settings;

Pass updated parent state to child props React.js

I have two child components in my parent component with routing set up. Child1 component's button updates state of the parent component and I want to pass the updated version of that state to child2 component as props. The problem is, although the state obviously updates in the parent component(I checked it), it doesn't get passed to the child2 component(the older version of state gets passed instead). I think it's because the getdData function(in the parent component) executes after the child2 component gets rendered. How can I solve this issue so that correct state gets passed?
Here's the code:
Parent component:
import './App.css';
import Questions from './components/questions';
import DropDownDifficulty from './components/dropDownDifficulty';
import {useState} from 'react'
import axios from 'axios';
import {BrowserRouter as Router, Route, Redirect} from 'react-router-dom'
function App() {
const [data, setdata] = useState([])
const getdData = (values)=>{
setdata(values)
}
return (
<div className="App">
<Router>
<Route exact path='/'>
<DropDownDifficulty sendData={getdData}/>
</Route>
<Route exact path = '/quiz'>
<Questions specs={data}/>
</Route>
</Router>
</div>
);
}
export default App;
Child1 component's button:
<Button disabled={buttonDisabled} className='button' variant="contained" color="primary" onClick={()=>sendData([category, Difficulty])}>
Start Quiz
</Button>
child2 component:
import React from 'react'
import Question from './question'
export default function Questions({specs}) {
console.log(specs)
return (
<div>
</div>
)
}
Simple Solution :
1.Check for Correct value before passing it down to props
<div className="App">
<Router>
<Route exact path='/'>
<DropDownDifficulty sendData={getdData}/>
</Route>
{data?.length > 0 && <Route exact path = '/quiz'>
<Questions specs={data}/>
</Route>}
</Router>
</div>
You can use useEffect and listen to props and show value only after the correct data comes
import {useEffect} from 'react'
import Question from './question'
export default function Questions({specs}) {
const [showIfCorrect,setShowIfCorrect]=useState(false)
useEffect(()=>{
if(specs){
// Check if it is correct
setShowIfCorrect(true)
}
},[specs])
return (
<div>
{showIfCorrect && <div>Correct Question</div>}
</div>
)
}
use Context Api and update state from sibling component(DropDownDifficulty) and pass it to the Questions component
For How to implement in context api refer the code below

Why is my PhotoComponent not updating with the correct photos?

Here is App.js
import React, {Component} from 'react';
import {
BrowserRouter,
Route,
Switch,
Redirect
} from 'react-router-dom';
import Search from './Search';
import Nav from './Nav';
import '../index.css';
import axios from 'axios';
import apiKey from './Config';
import NotFound from './NotFound';
import PhotoList from './PhotoList';
class App extends Component {
state= {
pictures: []
}
componentDidMount() {
this.getImages()
}
getImages=(query='cats')=> {
axios.get(`https://www.flickr.com/services/rest/?method=flickr.photos.search&api_key=${apiKey}&tags=${query}&per_page=24&page=1&format=json&nojsoncallback=1`)
.then(res=> {
const pictures=res.data.photos.photo
this.setState({pictures});
}).catch((error)=> {
console.log("There was an error parsing your data", error);
})
}
render() {
console.log(this.state.pictures);
return (
<div className="container">
<Search />
<Nav getImages={this.getImages} />
<Switch>
<Route exact path="/" render={()=> <Redirect to={'/cats'} />} />
<Route path='/cats' render={()=> <PhotoList getImages={()=>this.getImages} query='cats' data={this.state.pictures}/>} />
<Route path='/dogs' render={()=> <PhotoList getImages={()=>this.getImages} query='dogs' data={this.state.pictures} />} />
<Route path='/computers' render={()=> <PhotoList getImages={()=>this.getImages} query='computers' data={this.state.pictures} />} />
<Route component={NotFound}/>
</Switch>
</div>
)
}
}
export default App;
Here is PhotoList.js
import React, {Component} from 'react';
import Photo from './Photo';
class PhotoList extends Component {
handleImages=()=> {
this.props.getImages(this.props.query);
}
componentDidMount() {
this.handleImages();
console.log(this.props.data)
}
componentDidUpdate() {
console.log(this.props.data)
}
render() {
return (
<div className="photo-container">
<h2>Results</h2>
<ul>
{this.props.data.map((photo,index)=>
<Photo
farm={photo.farm}
server={photo.server}
id={photo.id}
secret={photo.secret}
key={index}
/>
)}
</ul>
</div>
);
}
}
export default PhotoList;
I've passed the getImages function into PhotoList that fetches data and changes the main App's state. The function takes a query string (cats, dogs, or computers). Then the state data is passed down as props and mapped over in the PhotoList component.
My website still only displays cats, even when I type in a different path (ex /dogs, /computers), yet when I console log the query string, I'm clearly entering different values into it. So why am I still getting cats shown? I know by default the query is equal to cats, but when I call the function in PhotoList it should be set to a different value with the query prop.
What am I doing wrong?
I agree with the first answer that checking for changes and calling the function again might solve your problem, since getImages() only runs when the component mounts. I have some more advice that might help you organize your code better according to common practices and help you avoid this problem altogether, just by not necessitating the confusing passing around of variables between components.
It makes the most sense to have getImages() part of the PhotoList component. This is because you always need to call that function when you render a PhotoList. It's not essential to the App component. Read more about this kind of concept here: https://reactjs.org/docs/thinking-in-react.html
Since you are using react router, what you can do is grab the search keyword from the path, from the PhotoList component. This would look like declaring the query as <Route path='/:query' ... > and then in the rendered component (PhotoList), grabbing the parameter with this.props.match.query. Read more: https://tylermcginnis.com/react-router-url-parameters/
If you do this, you can set default values for props using PropTypes and defaultProps. read more: https://blog.bitsrc.io/understanding-react-default-props-5c50401ed37d
The issue is, componentDidMount from PhotoList component executes only once when component first time mounts with query cats.
For next route change it will not execute componentDidMount, and your will not get new data but the old one.
In your componentDidUpdate you have to check if you are getting new props and again call your handleImages function which calls your getImages function,
componentDidUpdate(prevProps) {
console.log(this.props.data)
if(prevProps.query !== this.props.query){
this.handleImages();
}
}
Another issue is getImages function is not getting called.
getImages={()=>this.getImages}
You should do this,
getImages={this.getImages}

React-Router-Dom | URL Changes, but the component doesn't update

I have a list of items, in a table in React. When I click on the table Link, the url updates, but the component doesn't get rendered. If I refresh the component is there.
I have read that some React-router-dom versions have some problems, and there are a lot of solutions that are being discussed here:
https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/guides/blocked-updates.md
That although is for the Beta Versions. I assume I need to do something with the higher order function withRouter from react-router-dom, but it doesn't work for me. On top of that, withRouter has been set up globally in the App.
const AppHeader = withRouter(({ history, location, ...props }) => (
<Header {...props} currentRoute={location.pathname} />
));
The thing is, my App has several applications. One Angular and 2 React ones. Do I need to set it up, on each application as well? This is the Parent Component with the router for the application the Links aren't working.
import React from 'react';
import { Route, Switch, Redirect } from 'react-router-dom';
import { connect } from 'react-redux';
import 'react-tagsinput/react-tagsinput.css';
import PolicyRoutes from 'dataprotection/policies/containers/PolicyRoutes';
import './styles.scss';
const AppRouter = () => (
<Switch>
<Route exact path="/policies" component={PolicyRoutes} />
<Redirect to="/policies" />
</Switch>
);
AppRouter.propTypes = {};
function mapStateToProps() {
return {};
}
export default connect(mapStateToProps)(AppRouter);
I have tried 2 solutions. The first is to actually wrap my component withRouter like so:
<Route exact path="/policies" component={withRouter(PolicyRoutes)} />
And the second is to wrap withRouter, the connect function. Both aren't working.
This is the component not working:
import React from 'react';
import { Route, Switch, Redirect } from 'react-router-dom';
import PropTypes from 'prop-types';
import OverviewScreen from '../OverviewScreen';
import PolicyDetailsScreen from '../PolicyDetailsScreen';
const PolicyRoutes = ({ match }) => (
<Switch>
<Route exact path={`${match.url}/details/:policyId`} component={PolicyDetailsScreen} />
<Route exact path={match.url} component={OverviewScreen} />
<Redirect to={match.url} />
</Switch>
);
PolicyRoutes.propTypes = {
match: PropTypes.object
};
export default PolicyRoutes;
Can anyone help? I don't know what the problem is...

react-router link for route from another component

I have a React Component called ContentBar that holds a Route to display dynamic content:
//ContentBar.js
var React = require('react')
import ContentBarRoute from '../../../../routes/contentbar.routes'
const ContentBar = () =>
(
<ContentBarRoute />
)
export default ContentBar
I've placed this ContentBar in my root App structure:
//App.js
<div className="logBar">
<ErrorBoundary>
<Responsive minWidth={960}>
<ContentBar />
</Responsive>
</ErrorBoundary>
</div>
And I've created a route for a new menu in the ContentBarRoute component which I'm loading in the ContentBar:
//ContactBarRoute.react.js
const ContentBarRoute = () => (
<main>
<Switch>
<Route exact path="/logbar"component={LogBar}/>
<Route path="/user/:number/settings" />
<Route path="/user/:number/profile" />
<Route path="/user/add" component={UserAddMenu} />
</Switch>
</main>
)
When I try to link to /user/add from another component though, I'm not able to update the route from another component:
//Contactlist.react.js
<div className="contact-list useradd">
<Button as={Link} to="/user/add" className="btn-useradd">
<FontAwesome className="icon-adduser" tag="i" name="plus" />
</Button>
</div>
Can someone help me see what I'm doing wrong here? There's not a lot of information about routing between components, I found one answer in my research but it was slightly different: React-Router-4 routing from another component
The problem is that my routes and links are in separate areas of the hierarchy, whereas that guy's components were all close together.
Update:
The other example doesn't talk about rendering new components in place of old ones where one component is totally separate from the other:
Here is my router definition it exists in the class that sends the App to the html div:
import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
//import { createStore } from 'redux'
import { HashRouter } from 'react-router-dom'
import configureStore from '../tools/store.enhancer';
import App from '../javascripts/entry';
//import rootReducer from '../app/reducers/index'
//let store = createStore(rootReducer)
const store = configureStore();
render((
<Provider store={store}>
<HashRouter>
<App />
</HashRouter>
</Provider>
), document.getElementById('root'));
The behavior I expect is that the existing component is switched out for the user-add component, but the actual behavior is that nothing happens when I click the button, and I get an error saying
Hash history cannot PUSH the same path; a new entry will not be added to the history stack

Categories