I am trying to use Context API to create a Trello Clone application. Currently the ElementContext.js does not pass any data to App.js or HomePage.jsx.
I have tried multiple different methods but all result in not finding the data or saying "... is not defined" or just simply not working. My code is listed below.
App.js:
import React, { useContext } from 'react';
import './App.css';
import Todobox from './components/Todobox';
import HomePage from './components/HomePage'
import { ElementContext } from './ElementContext';
function App() {
return (
<>
<ElementContext.Provider>
<HomePage />
</ElementContext.Provider>
</>
);
}
export default App;
ElementContext.js:
import React, { createContext, useState } from 'react';
import Todobox from './components/Todobox';
export const ElementContext = createContext();
export const ElementContextProvider = () => {
const [elements, setElements] = useState([]);
const newElement = () =>{
setElements('Todobox');
};
const value = {
elements,
setElements,
newElement
};
return(
<ElementContext.Provider value={value}> </ElementContext.Provider>
)
}
export default ElementContext;
HomePage.jsx:
import react from 'react';
import { useContext } from 'react';
import '../App.css';
import ElementContext from '../ElementContext';
export default function HomePage(){
const newElement = useContext(ElementContext);
return(
<div className='page-container'>
<div className='header'>
<a className='header-title'>Trello Clone!</a>
<a className='header-button' onClick={newElement}>Create a list</a>
</div>
<div className='element-field'>
</div>
</div>
)
}
Any help is appreciated since I am new! Thank you in advanced! :)
App.jsx
import React, { useContext } from 'react';
import './App.css';
import Todobox from './components/Todobox';
import HomePage from './components/HomePage'
import { ElementContextProvider } from './ElementContext';
function App() {
return (
<>
<ElementContextProvider> // NOT <ElementContext.Provider>
<HomePage />
</ElementContextProvider>
</>
);
}
export default App;
ElementContext.jsx
import React, { createContext, useState } from 'react';
import Todobox from './components/Todobox';
export const ElementContext = createContext();
export const ElementContextProvider = ({children}) => {
const [elements, setElements] = useState([]);
const newElement = () =>{
setElements('Todobox');
};
const value = {
elements,
setElements,
newElement
};
return(
<ElementContext.Provider value={value}>
{children} // PASS CHILDREN COMPONENT
</ElementContext.Provider>
)
}
// export default ElementContext; // REMOVE THIS LINE
Edited HomePage.jsx
import react from 'react';
import { useContext } from 'react';
import '../App.css';
import ElementContext from '../ElementContext';
export default function HomePage(){
const { newElement } = useContext(ElementContext); // NOT const newElement = useContext(ElementContext);
return(
<div className='page-container'>
<div className='header'>
<a className='header-title'>Trello Clone!</a>
<a className='header-button' onClick={newElement}>Create a list</a>
</div>
<div className='element-field'>
</div>
</div>
)
}
Hope this is helpful for you.
Related
When I try to use createContext() the console gives me this error:
App.js:6
Uncaught TypeError: Cannot destructure property 'consoleLogFunction' of '(0 , react__WEBPACK_IMPORTED_MODULE_1__.useContext)(...)' as it is null.
I've seen others asking questions about this here in Stack Overflow but I can't find a solution.
GlobalContext.js
import React from 'react'
import { createContext } from 'react'
export const AppContext = createContext();
function GlobalContext() {
const consoleLogFunction = () => {
console.log("ok")
}
return (
<AppContext.Provider value={{consoleLogFunction}}></AppContext.Provider>
)
}
export default GlobalContext
App.js
import "./index.css";
import { useContext, useEffect } from "react";
import { AppContext } from "./components/GlobalContext";
function App() {
const { consoleLogFunction } = useContext(AppContext);
useEffect(() => {
consoleLogFunction();
}, []);
return (
<AppContext>
<div>home</div>
</AppContext>
);
}
export default App;
You don't need to export 'AppContext', creating the provider and exporting that is good enough.
Try this, I've also made a couple of modifications to make it easier to use the context later:
GlobalContext.js
import React from 'react'
import { createContext, useContext } from 'react'
const AppContext = createContext();
function GlobalContext({ children }) {
const consoleLogFunction = () => {
console.log("ok")
}
return (
<AppContext.Provider value={{consoleLogFunction}}>
{ children }
</AppContext.Provider>
)
}
export default GlobalContext;
// This is a helper
export const useGlobalContext = () => useContext(AppContext);
Home.js
import { useEffect } from "react";
import { useGlobalContext } from "./components/GlobalContext";
export const Home = ({ children }) => {
const { consoleLogFunction } = useGlobalContext();
useEffect(() => {
consoleLogFunction();
}, []);
return(
<div>home</div>
)
};
App.js
import "./index.css";
import { useEffect } from "react";
import GlobalContext from "./components/GlobalContext"
import { Home } from "./components/Home";
function App() {
return(
<GlobalContext>
<Home />
</GlobalContext>
);
}
export default App;
hello man the problem is because App component is not wrapped inside GlobalContext . and in the GlobalContext component you should handle the children prop.
it will work when doing it like this example :
import { useEffect, useContext, createContext } from "react";
import "./styles.css";
export const AppContext = createContext();
function GlobalContext(props) {
const consoleLogFunction = () => {
console.log("ok");
};
return (
<AppContext.Provider value={{ consoleLogFunction }}>
{props.children}
</AppContext.Provider>
);
}
const Home = () => {
const { consoleLogFunction } = useContext(AppContext);
useEffect(() => {
consoleLogFunction();
}, []);
return <div>home</div>;
};
export default function App() {
return (
<GlobalContext>
<Home />
</GlobalContext>
);
}
Hope this help.
I have a video which plays on a loop on the landing page. It takes sometime for it to load. I also have a custom spinner (SpinnerComponent) which I want to play while the video is getting loaded:
import React, { useEffect, useState } from "react";
import NavbarComponent from './Components/Navbar/navbar';
import AboutComponent from './Components/About/about';
import HealthComponent from './Components/Health/health';
import SpinnerComponent from './Components/Spinner/spinner';
import LandingComponent from './Components/Landing/landing';
import ContactUs from './Components/ContactUs/contactUs';
import Footer from './Components/Footer/footer';
function App() {
return (
<>
<NavbarComponent/>
<LandingComponent/>
<AboutComponent/>
<HealthComponent/>
<ContactUs/>
<Footer/>
</>
);
}
export default App;
Landing Component
import React, {useEffect} from 'react'
import './styleLanding.css';
import AOS from 'aos';
import 'aos/dist/aos.css';
import sample from '../../videos/movie.mp4'
export const LandingComponent = () => {
useEffect(() => {
AOS.init();
}, [])
return (
<div id="home" className="style-landing" >
<div className="style-overlay"></div>
<video src={sample} type={'video/mp4'} preload={'auto'} className="style-video" autoPlay loop muted/>
<div className="style-content">
<h2 className="style-heading" data-aos="zoom-in" data-aos-duration="1000" data-aos-offset="130">EXPERIENCE THE BEST</h2>
<h1 className="style-sub-heading" data-aos="zoom-in" data-aos-duration="1000" data-aos-offset="130">nutrition</h1>
</div>
</div>
)
}
export default LandingComponent
How can I modify it such that the spinner component plays on the entire screen (without the navbar) and only once the video is done loading, I can show the entire webpage?
Thanking in advance!
App.js:
import React, { useEffect, useState } from "react";
import NavbarComponent from './Components/Navbar/navbar';
import AboutComponent from './Components/About/about';
import HealthComponent from './Components/Health/health';
import SpinnerComponent from './Components/Spinner/spinner';
import LandingComponent from './Components/Landing/landing';
import ContactUs from './Components/ContactUs/contactUs';
import Footer from './Components/Footer/footer';
function App() {
const [loaded, setLoaded] = useState(false);
useEffect(() => {
const video = document.getElementById('landing-video');
video.addEventListener('load', () => {
setLoaded(true);
});
}, []);
return (
<>
{loaded ? (
<>
<NavbarComponent/>
<LandingComponent/>
<AboutComponent/>
<HealthComponent/>
<ContactUs/>
<Footer/>
</>
) : (
<SpinnerComponent fullscreen={true} />
)}
</>
);
}
export default App;
LandingComponent.js:
import React, {useEffect, useRef, useState} from 'react'
import './styleLanding.css';
import AOS from 'aos';
import 'aos/dist/aos.css';
import sample from '../../videos/movie.mp4'
export const LandingComponent = () => {
const [loaded, setLoaded] = useState(false);
const videoRef = useRef(null);
useEffect(() => {
AOS.init();
const video = videoRef.current;
video.addEventListener('canplaythrough', () => {
video.play();
setLoaded(true);
});
}, [])
return (
<div id="home" className="style-landing" >
<div className="style-overlay"></div>
<video ref={videoRef} id="landing-video" src={sample} type={'video/mp4'} preload={'auto'} className="style-video" autoPlay loop muted hidden={!loaded} />
<div className="style-content">
<h2 className="style-heading" data-aos="zoom-in" data-aos-duration="1000" data-aos-offset="130">EXPERIENCE THE BEST</h2>
<h1 className="style-sub-heading" data-aos="zoom-in" data-aos-duration="1000" data-aos-offset="130">nutrition</h1>
</div>
</div>
)
}
export default LandingComponent
I am trying to open a new page from the home page where the data is passed on clicking the item on the list. The code I have works for older versions of react-router-dom I believe. Can someone tell me how can I do the same thing in newer version of React? I need to display the ID and title in the new page.
I tried to follow the example here https://codesandbox.io/s/focused-wright-w8il9?file=/src/ViewUserDetails.js:354-377
The error says AppPage.js:15 Uncaught TypeError: Cannot read properties of null (reading 'DUMMY_DATA')
If there are any corrections otherwise too, please correct me. I'm new to this.
Thank You.
GridList.js
import React from "react";
import ReactDOM from "react-dom";
import { Link, useNavigate } from "react-router-dom";
import { Component } from "react";
import GridItem from "./GridItem";
import AppPage from "./AppPage";
import classes from "./GridList.module.css";
import App01 from "./app-01.png";
import App02 from "./app-02.png";
const GridList = (props) => {
const DUMMY_DATA = [
{
title: "tic tac toe",
id: 1,
image: App01,
},
{
title: "snake",
id: 2,
image: App02,
},
];
return (
<div className={classes.gridc1}>
{DUMMY_DATA.map((item) => {
return (
<div key={item.id}>
<Link
to={{
pathname: `/apppage/${item.id}`,
state: { DUMMY_DATA: item },
}}
>
<GridItem key={item.id} image={item.image} />
</Link>
</div>
);
})}
</div>
);
};
export default GridList;
AppPage.js
import React from "react";
import {ReactDOM, render } from "react-dom";
import { useLocation } from "react-router-dom";
import { Component } from "react";
const AppPage = _ => {
const { state }= useLocation();
return (
<div>
<p>{state.DUMMY_DATA.id}</p>
<p>{state.DUMMY_DATA.title}</p>
</div>
);
};
export default AppPage;
I have solved this by following a documentation for upgrading to v6 react-router-dom and it finalllllly worked. Here it is: https://reactrouter.com/docs/en/v6/getting-started/tutorial
The solution was simple. I was just looking for a better documentation for upgrading from v5 to v6, I guess.
Also, I will attach the code if it's gonna help anybody here. (I have renamed a few things like 'DUMMY_DATA' to 'items' just for my convenience. The code would explain it clearly anyway ). If there are any doubts further, do comment and I'll try to reply! :)
GridList.js
import React from "react";
import ReactDOM from "react-dom";
import { Link, useParams } from "react-router-dom";
import { Component } from "react";
import GridItem from "./GridItem";
import AppPage from "./AppPage";
import classes from "./GridList.module.css";
import { getItems } from "./Data";
let items = getItems();
const GridList = (props) => {
return (
<div className={classes.gridc1}>
{items.map((item) => {
return (
<div key={item.id}>
<Link to={`/apppage/${item.id}`} key={item.id}>
<GridItem key={item.id} image={item.image} />
</Link>
</div>
);
})}
</div>
);
};
export default GridList;
Data.js
import React from "react";
import ReactDOM, { render } from "react-dom";
import App01 from "./app-01.png";
import App02 from "./app-02.png";
let items = [
{
title: "tic tac toe",
id: 1,
image: App01,
},
{
title: "bytes",
id: 2,
image: App02,
},
];
export function getItems() {
return items;
}
export function getItem(id) {
return items.find((item) => item.id === id);
}
AppPage.js
import React from "react";
import ReactDOM, { render } from "react-dom";
import { useParams } from "react-router-dom";
import { Component } from "react";
import { getItem } from "./Data";
const AppPage = _ => {
let params = useParams();
let item = getItem(parseInt(params.id, 10));
return (
<div>
<p ptitle = {item.title} />
<p pid = {item.id}> />
</div>
);
};
export default AppPage;
import React from "react";
import ReactDOM, { render } from "react-dom";
import Body from "../ui/Body";
import Head from "../ui/Head";
import Tail from "../ui/Tail";
import { useLocation } from "react-router-dom";
import { Component } from "react";
const AppPage = () => {
//don't forget to use this, good luck :)
const { state } = useLocation();
return (
<div>
// and use "state", don't use static
// what is static ?
<p>{state.DUMMY_DATA.id}</p>
<p>{state.DUMMY_DATA.title}</p>
</div>
);
};
export default AppPage;
I am trying to use the React useState Hook for an online project. What I want to happen is, when I type the users name in the search box, It will find the users card on the browser. I am able to log the user to the console, but I am stuck on how to get it to render on screen. Tried so many ways and just not getting it.
console output
App.js
import React, { useState } from 'react';
import CardList from './CardList';
import {robots} from './robots';
import SearchBox from './SearchBox';
function App() {
let [searchInput] = useState('');
function onSearchChange(e) {
searchInput = e.target.value;
const filteredRobots = robots.filter(function(robot){
return robot.name.toLowerCase().includes(searchInput.toLowerCase());
});
console.log(filteredRobots);
}
return (
<div className='tc'>
<h1>RoboFriends</h1>
<SearchBox searchChange={onSearchChange} />
<CardList id={robots.id} name={robots.name} email={robots.email}/>
</div>
);
}
export default App;
CardList.js
import React from 'react';
import Card from './Card';
import {robots} from './robots';
function CardList(props) {
return (
<div>
{
robots.map(function(user) {
return <Card key={user.id} id={user.id} name={user.name} email={user.email} />
})
};
</div> )
}
export default CardList;
Card.js
import React from 'react';
import 'tachyons';
function Card(props) {
return (
<div className='bg-light-green dib br3 pa3 ma2 grow shadow-5'>
<img src={`https://robohash.org/${props.id}`} alt="Robot" />
<h2>{props.name}</h2>
<p>{props.email}</p>
</div>
);
}
export default Card;
React only re-render when you set a state to a new value.
Check the code below:
import React, { useState } from 'react';
import CardList from './CardList';
import {robots} from './robots';
import SearchBox from './SearchBox';
function App() {
let [searchInput, setSeachInput] = useState('');
function onSearchChange(e) {
// set state here to re-render
setSeachInput(e.target.value);
}
// use might want to use useMemo to improve this, I just want to make it simple now
const filteredRobots = robots.filter(function(robot){
return robot.name.toLowerCase().includes(searchInput.toLowerCase());
});
console.log(filteredRobots);
return (
<div className='tc'>
<h1>RoboFriends</h1>
<SearchBox searchChange={onSearchChange} />
{/* using filteredRobots herer*/}
<CardList id={filteredRobots.id} name={filteredRobots.name} email={filteredRobots.email}/>
</div>
);
}
export default App;
In your App.js file, the searchInput is not being set to the state
import React, { useState } from 'react';
import CardList from './CardList';
import {robots} from './robots';
import SearchBox from './SearchBox';
function App() {
let [searchInput, setSearchInput] = useState('');
function onSearchChange(e) {
setSearchInput(e.target.value)
}
**You can pass the filterRobots in place of robots to get only words passed in the search box**
const filteredRobots = robots.filter(function(robot){
return robot.name.toLowerCase().includes(searchInput.toLowerCase());
});
return (
<div className='tc'>
<h1>RoboFriends</h1>
<SearchBox searchChange={onSearchChange} />
<CardList robots={filteredRobots}/>
</div>
);
}
export default App;
In the CardList File
import React from 'react';
import Card from './Card';
function CardList({robots}) {
{
robots.map((user, i) => {
return (
<Card
key={i}
id={user[i].id}
name={user[i].name}
email={user[i].email}
/>
);
})
}
}
export default CardList;
You should not be mutating the searchInput value like searchInput = e.target.value. It is better to call a setter function to update the value. For example,
const [searchInput, setSearchInput] = useState('');
// to update the value of searchInput call setSearchInput
function onSearchChange(e) {
setSearchInput(e.target.value)
}
State changes are asynchronous. When you try to filter the robots it is not guaranteed that it will be called with the latest value of searchInput that's why you should be using useEffect hook which will filter the robots when the value of searchInput changes.
Here is a solution,
import React, { useState } from 'react';
import CardList from './CardList';
import {robots} from './robots';
import SearchBox from './SearchBox';
function App() {
let [searchInput, setSearchInput] = useState('');
let [filterdRobots, setFilteredRobots] = useState(robots);
function onSearchChange(e) {
setSearchInput(e.target.value);
}
useEffect(() => {
setFilteredRobots(robots.filter(r =>
r.name.toLowerCase().includes(searchInput.toLowerCase())))
}, [searchInput, robots])
return (
<div className='tc'>
<h1>RoboFriends</h1>
<SearchBox searchChange={onSearchChange} />
<CardList robots={filteredRobots}/>
</div>
);
}
export default App;
check the codesanbox for demo
I have a simple react hooks application - a list of Todos - with react router v4
On the List of Todos, when a Todo is clicked I need to:
Dispatch the current todo in context
Redirect to another route (from /todos to /todos/:id)
In the previous React Class based implementation I could use this.context.history.push to redirect to another route.
How would I handle that using React Hooks in combination of React Router v4 (in code below see my comment in function editRow())?
Code below:
=====index.js=====
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter} from "react-router-dom"
import App from './App';
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>, document.getElementById('root'));
=====main.js=====
import React from 'react'
import { Switch, Route } from 'react-router-dom'
import TodosList from './todoslist'
import TodosEdit from './todosedit'
const Main = () => (
<main>
<Switch>
<Route exact path="/todos" component={TodosList}/>
<Route exact path="/todos/:id" component={TodosEdit} />
</Switch>
</main>
)
export default Main
=====app.js=====
import React, {useContext, useReducer} from 'react';
import Main from './main'
import TodosContext from './context'
import todosReducer from './reducer'
const App = () => {
const initialState = useContext(TodosContext);
const [state, dispatch] = useReducer(todosReducer, initialState);
return (
<div>
<TodosContext.Provider value={{state, dispatch}}>
<Main/>
</TodosContext.Provider>
</div>
)
}
export default App;
=====TodosContext.js=====
import React from 'react'
const TodosContext = React.createContext({
todos: [
{id:1, text:'Get Grocery', complete:false},
{id:2, text:'Excercise', complete:false},
{id:3, text:'Drink Water', complete:true},
],
currentTodo: {}
})
export default TodosContext
=====reducer.js=====
import React from 'react'
export default function reducer(state, action){
switch(action.type){
case "GET_TODOS":
return {
...state,
todos: action.payload
}
case "SET_CURRENT_TODO":
return {
...state,
currentTodo: action.payload
}
default:
return state
}
}
=====Todos.js=====
import React, {useState, useContext, useEffect} from 'react';
import TodosContext from './context'
function Todos(){
const [todo, setTodo] = useState("")
const {state, dispatch} = useContext(TodosContext)
useEffect(()=>{
if(state.currentTodo.text){
setTodo(state.currentTodo.text)
} else {
setTodo("")
}
dispatch({
type: "GET_TODOS",
payload: state.todos
})
}, [state.currentTodo.id])
const editRow = event =>{
let destUrlEdit = `/todos/${event.id}`
let obj = {}
obj.id = event.id
obj.text = event.text
dispatch({type:"SET_CURRENT_TODO", payload: obj})
//after dispatch I would like to redirect to another route to do the actual edit
//destUrlEdit
}
return(
<div>
<h1>List of ToDos</h1>
<h4>{title}</h4>
<ul>
{state.todos.map(todo => (
<li key={todo.id}>{todo.text}
<button onClick={()=>{
editRow(todo)}}>
</button>
</li>
))}
</ul>
</div>
)
}
export default Todos;
It's actually a lot simpler than the other answers, React Router v5.1 provides a useHistory hook.
import React from 'react'
import { useHistory } from 'react-router-dom'
const MyComponent = () => {
const history = useHistory()
const handleButtonClick = (event) => {
history.push(event.target.value)
}
return (
<button
type="button"
value="/my/path"
onClick={handleButtonClick}
>
Navigate Me!
</button>
)
}
Your problem is related to Programmatically navigating using react-router-v4 instead of with hooks,
In react-router-v4, you would get history from props if the Todos component is rendered as a child or Route or from an ancestor that is render form Route and it passed the Router props to it. However it is not receiving Router props, you can use withRouter HOC from react-router to get the router props and call props.history.push(destUrlEdit)
import React, {useState, useContext, useEffect} from 'react';
import TodosContext from './context'
import { withRouter } from 'react-router-dom';
function Todos(props){
const [todo, setTodo] = useState("")
const {state, dispatch} = useContext(TodosContext)
useEffect(()=>{
if(state.currentTodo.text){
setTodo(state.currentTodo.text)
} else {
setTodo("")
}
dispatch({
type: "GET_TODOS",
payload: state.todos
})
}, [state.currentTodo.id])
const editRow = event =>{
let destUrlEdit = `/todos/${event.id}`
let obj = {}
obj.id = event.id
obj.text = event.text
dispatch({type:"SET_CURRENT_TODO", payload: obj})
//after dispatch I would like to redirect to another route to do the actual edit
//destUrlEdit
props.history.push(destUrlEdit);
}
return(
<div>
<h1>List of ToDos</h1>
<h4>{title}</h4>
<ul>
{state.todos.map(todo => (
<li key={todo.id}>{todo.text}
<button onClick={()=>{
editRow(todo)}}>
</button>
</li>
))}
</ul>
</div>
)
}
export default withRouter(Todos);
You can use UseNavigate to move the change page. here is the sample example
"react-router-dom": "^6.2.1",
// Route File
import React, { Suspense, lazy } from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import IndexLayout from "../layouts";
import NotFoundPage from "../views/404";
import Loader from "../components/Loader";
const Dashboard = lazy(() => import("../containers/DashboardContainer"));
const Router = () => {
return (
<BrowserRouter>
<IndexLayout> // this one is kind of HOC
<Routes>
<Route
path="/"
element={
<Suspense fallback={<Loader />}>
<Dashboard />
</Suspense>
}
/>
</end every thing>
// any component
import React, { useEffect } from "react";
import { useNavigate } from "react-router-dom";
const TestComponent = ({ newSignup }) => {
const navigate = useNavigate();
useEffect(() => {
if (newSignup) {
navigate("/login");
}
}, [newSignup]);
return (
<div>
</div>
)
}
export default TestComponent
Using react-redux and connected-react-router...
import {useDispatch } from 'react-redux';
import { push } from 'connected-react-router';
export default () => {
const dispatch = useDispatch();
return (
<Button onClick={() => dispatch(push('/login'))}>
Login
</Button>
);
};