Fetching data in other components with react hook - reactjs

Im new to react hooks and are experimenting a bit. I can display my values that are generated in Provider.js in App.js through Comptest.js. My problem is that the structure of my project with css etc makes it inconvenient to have a structure in the App.js like this:
<Provider>
<Comptest />
</Provider>
is it possible to fetch the data without displaying the components in that way in the app? just passing it between the components.
Here is a compact version of my application:
App.js
import React, { useContext } from "react";
import Provider from "./Provider";
import Comptest from "./Comptest";
import DataContext from "./Context";
function App() {
return (
<div className="App">
<h2>My array!</h2>
<Provider>
<Comptest />
</Provider>
</div>
);
}
export default App;
Provider.js
import React, { useState } from "react";
import DataContext from "./Context";
const Provider = props => {
const data = ["item1", "item2"];
return (
<DataContext.Provider value={data}>{props.children}</DataContext.Provider>
);
};
export default Provider;
Comptest.js
import React from "react";
import DataContext from "./Context";
const Comptest = () => {
const content = React.useContext(DataContext);
console.log(content);
return <div>{(content)}</div>;
};
export default Comptest;
Context.js
import React from "react";
const DataContext = React.createContext([]);
export default DataContext;

Related

How to provide context from contextApi for a specific set of routes in nextjs and preserve state with routing through linking?

I am using contextApi with nextjs and I'm having some trouble when providing a context just for certain routes. I am able to make the context available for just a few routes, but when I transition from one to the other through linking, I end up losing the state of my application.
I have three files inside my pages folder:
index.tsx,
Dashboard/index.tsx and
SignIn/index.tsx.
If I import the provider inside the files Dashboard/index.tsx and SignIn/index.tsx and go from one page to the other by pressing a Link component from next/link, the whole state is set back to the initial state.
The content of the Dashboard/index.tsx file
import React from 'react';
import Dashboard from '../../app/views/Dashboard';
import { AuthProvider } from '../../contexts/auth';
const Index: React.FC = () => (
<AuthProvider>
<Dashboard />
</AuthProvider>
);
export default Index;
This is the contend of the SignIn/index.tsx file:
import React from 'react';
import SignIn from '../../app/views/SignIn';
import { AuthProvider } from '../../contexts/auth';
const Index: React.FC = () => (
<AuthProvider>
<SignIn />
</AuthProvider>
);
export default Index;
The views folder is where I create the components that will be rendered.
The content of the file views/SignIn/index.tsx is:
import React, { useContext } from 'react';
import Link from 'next/link';
import { AuthContext } from '../../../contexts/auth';
const SignIn: React.FC = () => {
const { signed, signIn } = useContext(AuthContext);
async function handleSignIn() {
signIn();
}
return (
<div>
<Link href="Dashboard">Go back to Dashboard</Link>
<button onClick={handleSignIn}>Click me</button>
</div>
);
};
export default SignIn;
And the content of the file views/Dashboard/index.tsx is:
import React, { useContext } from 'react';
import Link from 'next/link';
import { AuthContext } from '../../../contexts/auth';
const Dashboard: React.FC = () => {
const { signed, signIn } = useContext(AuthContext);
async function handleSignIn() {
signIn();
}
return (
<div>
<Link href="SignIn">Go back to sign in page</Link>
<button onClick={handleSignIn}>Click me</button>
</div>
);
};
export default Dashboard;
I am able to access the context inside both /Dashboard and /SignIn, but when I press the link, the state comes back to the initial one. I figured out that the whole provider is rerenderized and therefore the new state becomes the initial state, but I wasn't able to go around this issue in a "best practices manner".
If I put the provider inside _app.tsx, I can maintain the state when transitioning between pages, but I end up providing this state to the / route as well, which I am trying to avoid.
I was able to go around this by doing the following, but it really does not seem to be the best solution for me.
I removed the Providers from Pages/SignIn/index.tsx and Pages/Dashboard/index.tsx and used the following snippet for the _app.tsx file:
import React from 'react';
import { AppProps } from 'next/app';
import { useRouter } from 'next/router';
import { AuthProvider } from '../contexts/auth';
const App: React.FC<AppProps> = ({ Component, pageProps }) => {
const router = useRouter();
const AuthProviderRoutes = ['/SignIn', '/Dashboard'];
return (
<>
{AuthProviderRoutes.includes(router.pathname) ? (
<AuthProvider>
<Component {...pageProps} />
</AuthProvider>
) : <Component {...pageProps} />}
</>
);
};
export default App;
Does anyone have a better solution?

Unable to access Context in app.js react-native

I am currently trying to use context within a react native application. My context code is as follows:
import React, { useState, createContext } from 'react'
export const AuthContext = createContext()
export const AuthProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState(null)
return (
<AuthContext.Provider value={{ currentUser}}>
{children}
</AuthContext.Provider>
)
}
I then import context into my App.js and wrap my App.js in the provider like so:
import React, { useContext } from 'react'
import { AuthProvider, AuthContext } from './src/Context/AuthContext'
other imports etc...
export default function App() {
const { currentUser } = useContext(AuthContext)
return (
<>
<AuthProvider>
<IconRegistry icons={EvaIconsPack} />
<NavigationContainer>
<RootStackScreen isAuth={false} />
</NavigationContainer>
</AuthProvider>
</>
)
}
when i try to access currentUser in the app.js I get the following error:
component exception: undefined is not an object, evalauting useContext.currentUser
If I try to access current user in other components of my application; however, I do not receive this error and currentUser console logs to the correct value of null. I am wondering then how I can go about accessing context in app.js. is it possible to wrap a react-native application's index.js in the auth provider? or am i doing something wrong context wise.
version of react-native: 0.63.3
version of react: 16.13.1
This happening because you are accessing the context outside the provider.
If you check your createContext, you are not providing a default value.
export const AuthContext = createContext(/*No default value*/)
When the useContext is called outside the provider it will use the default value in your case its 'undefined' and it throws an error when you try to access the property currentUser of undefined.
One way to solve this issue is to use the state in app.js file instead of a separate provider component.
export default function App() {
const [currentUser, setCurrentUser] = useState(null);
const state={currentUser, setCurrentUser};
return (
<>
<AuthContext.Provider value={state}>
<IconRegistry icons={EvaIconsPack} />
<NavigationContainer>
<RootStackScreen isAuth={false} />
</NavigationContainer>
</AuthContext.Provider
</>
)
}
This will not have any impact to other components and also you can access the currentUser variable easily just like you access any state.
I haven't used the above answer, but I moved AuthContext.Provider to the index.js file and wrapped the App component in it. Now it is working fine for me. I did this because in App.js I have large code due to navigation and routing logic.
Using this we can keep our context logic separated. I used this with React.js and not with ReactNative
ex.
import React from "react";
import ReactDOM from "react-dom";
import App from "./components/App";
import * as serviceWorker from "./serviceWorker";
import "./index.css";
import { AuthProvider } from './src/Context/AuthContext'
ReactDOM.render(
<AuthProvider>
<App />
</AuthProvider>
document.getElementById("root")
);
serviceWorker.unregister();

Invalid hook call. Hooks can only be called inside of the body of a function component when using Hooks

I am not able to use React hooks. I have 4 components:
ComponentA
componentC
componentE
componentF
I need to pass value to componentF directly from componentA without having to pass from componentC and componentE. All components are in a single tree.
// componentA
import React from 'react';
import './App.css';
import ComponentC from "./components/ComponentC";
export const UserContext = React.useContext();
function App() {
return (
<div className="App">
<UserContext.Provider>
<ComponentC value={'My message'}/>
</UserContext.Provider>
</div>
);
}
export default componentA;
// componentF
import React from 'react';
import UserContext from '../App';
function ComponentF() {
return (
<div>
<UserContext.Consumer>
{
user => {
return (
<div>you are {user}</div>
)
}
}
</UserContext.Consumer>
</div>
)
}
export default ComponentF;
It is giving an error when I am trying to use context:
Invalid hook call. Hooks can only be called inside of the body of a function component.
The mistake is that you are using React.useContext()
You should be doing:
export const UserContext = React.createContext();
To pass to the child components of Component A, you can do it like this:
<UserContext.Provider value={100}>
<ComponentC .../>
</UserContext.Provider>
And instead of using UserContext.Consumer, you can get the value using React.useContext inside the component body of ComponentF.
// componentF
import React from 'react';
import {UserContext} from '../App';
function ComponentF() {
const value = React.useContext(UserContext);
return (
<div>
....
{value} // which will be equal to 100
</div>
)
}
export default ComponentF;

Getting undefined when accessing redux stores state property in react App

I am using redux in my react app and I am getting undefined when I access redux state property in one of my component, why is that? the state is valid when I call console.log in reducer file : here is my reducerFile :
const initState = {
isCurrentUser : true
}
export default function(state=initState, action) {
console.log(`this is from localAuthReducer ${state.isCurrentUser}`)
switch(action.type) {
default:
return state
}
}
Here is my react component :
import React, {Component} from 'react';
import styles from './IndexPage.module.scss';
import { connect } from 'react-redux';
import Header from './../../components/Header/Header';
class IndexPage extends Component {
render() {
return(
<div className={styles.container}>
<Header
isCurrentUser = {this.props.isCurrentUser}
/>
{ console.log(`this is from indexPage ${this.props.isCurrentUser}`)}
</div>
);
}
}
function mapStateToProps(state) {
return {
isCurrentUser : state.isCurrentUser
}
}
export default connect(mapStateToProps, null)(IndexPage);
Here is my index.js file :
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App/App';
import {Provider } from 'react-redux';
import {createStore, applyMiddleware} from 'redux';
import reducers from './reducers/index';
import reduxThunk from 'redux-thunk';
const store = createStore(
reducers,
applyMiddleware(reduxThunk)
);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
, document.querySelector("#root"));
I dont know where I am going wrong isCurrentUser must have value of true as it is the default value of the redux state

How to Pass in Store as a prop

I am currently having a problem getting store to be passed in as a prop and am wondering what to label a few things.
The current error is within create store, I'm unsure what to do with it.
I have tried other methods and only want to use the store method where I pass it in as a prop
import React from 'react';
import { MockGit } from './Constants';
import ExpansionPanelSummary from '#material-ui/core/ExpansionPanelSummary';
import ExpansionPanelDetails from '#material-ui/core/ExpansionPanelDetails';
import Typography from '#material-ui/core/Typography';
import ExpandMoreIcon from '#material-ui/icons/ExpandMore';
import ExpansionPanel from '#material-ui/core/ExpansionPanel';
import Button from '#material-ui/core/Button';
import TestAPI from './TestAPI';
import { displayGitData, userInfoURL, getDataSaga } from '../sagas/sagas';
import { createStore } from 'redux';
class GitData extends React.Component {
constructor(props) {
super(props);
}
render() {
const store = createStore(...); //this is what im unsure of.
const { store } = this.props;
return (
<ExpansionPanel>
<ExpansionPanelSummary expandIcon={<ExpandMoreIcon />}>
<Typography> {MockGit} </Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
{displayGitData()}
{userInfoURL()}
{getDataSaga()}
<TestAPI />
</ExpansionPanelDetails>
</ExpansionPanel>
);
}
}
export default GitData;
The goal is to get store passed in as a prop with no errors.
Any help would be great, Thanks!
You're doing it wrong, here's the recommended way to use React with Redux:
store.js
import { createStore } from 'redux';
export default createStore(...)
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store.js'
const App = () => (<h1>Hello from App</h1>);
ReactDOM.render(
<Provider store={store}><App/></Provider>
document.querySelector('#react-root')
);
You now have an app that is bound with the store.
The react-redux npm package allows also to bind component props to store dispatches and store state, example:
my-component.js
import React from 'react';
import { connect } from 'react-redux';
class MyComponent extends React.Component {
render() {
return (
<p>{this.props.hello}</p>
)
}
}
export default connect(state => ({hello: state.helloReducer.value}))(MyComponent)
For further tutorials, check the official docs of react-redux, or this good youtube playlist.

Resources