EDIT: I imported something wrong :facepalm:
Let me first run down what code ive written to get this output then I will tell you the expected output and what im confused about
App.jsx
import React from "react";
import Home from "./components/pages/HomePage";
import store from "./ducks/store";
import { Provider } from "react-redux";
import { BrowserRouter, Route, Switch } from "react-router-dom";
const App = () => {
return (
<BrowserRouter>
<Provider store={store}>
<Switch>
<Route exact path="/" component={Home} />
</Switch>
</Provider>
</BrowserRouter>
);
};
export default App;
Home.jsx
import React, { useEffect } from "react";
import FlexBox from "../../shared/FlexBox";
import BlogPostList from "./SortSettings";
import { useSelector, useDispatch } from "react-redux";
import { fetchAllBlogs } from "../../../ducks/blogs";
import {
getBlogData,
getBlogPosts,
getBlogTags,
} from "../../../ducks/selectors";
import SpinLoader from "../../shared/SpinLoader";
const Home = () => {
const blogData = useSelector((state) => getBlogData(state));
const blogPosts = useSelector((state) => getBlogPosts(state));
const blogTags = useSelector((state) => getBlogTags(state));
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchAllBlogs());
}, [dispatch]);
// TODO: handle if blogData.requestError comes back as true
if (blogData.isLoading || !blogPosts || !blogTags) {
return (
<FlexBox
alignItems="center"
justifyItems="center"
width="100vw"
height="100vh"
>
<SpinLoader />
</FlexBox>
);
}
return (
<FlexBox height="100vh" width="100vw">
<BlogPostList blogPosts={blogPosts} />
</FlexBox>
);
};
export default Home;
BlogPostList.jsx
import React from "react";
import BlogPost from "./BlogPost";
import FlexBox from "../../shared/FlexBox";
const BlogPostList = ({ blogPosts }) => {
return (
<FlexBox flexDirection="column">
Why in the world is this rendering a SortSettings component AHHHHHH!
</FlexBox>
);
};
export default BlogPostList;
Now my question is this why is it that the Home component is rendering a component as showed here https://gyazo.com/8cac1b28bdf72de9010b0b16185943bb what I would expect the Home component to be rendering is a BlogPostList if anyone has an idea help would be appreciated ive been stuck on this for awhile now (im pretty new so this might just be a noob mistake so sorry if its something obvious)
Related
I have information in the state (true or false) that I want to display if is true this Navbar component, but when I use the hook, I get an error message:
hook error
My code:
import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'connected-react-router';
import store, { history } from './reduxStore';
import AppRouterContainer from './pages/AppRouterContainer';
import Feedback from './pages/feedback/Feedback';
import Navbar from './components/Navbar/Navbar';
import { useTypedSelector } from '../src/hooks/useTypedSelector';
const isAuth = useTypedSelector((state) => state.auth.isAuth);
const App = () => (
<BrowserRouter>
<Provider store={store}>
<ConnectedRouter history={history}>
<AppRouterContainer />
{isAuth && (
<Navbar />
)}
<Feedback />
</ConnectedRouter>
</Provider>
</BrowserRouter>
);
export default App;
You need to create a wrapper component to have access to store in your context (I think your useTypedSelector() hook needs that access).
You can use hooks only inside a function, not just inside a module.
Check out this example:
import React from 'react';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import { ConnectedRouter } from 'connected-react-router';
import { useTypedSelector } from '../src/hooks/useTypedSelector';
import Navbar from './components/Navbar/Navbar';
import AppRouterContainer from './pages/AppRouterContainer';
import Feedback from './pages/feedback/Feedback';
import store, { history } from './reduxStore';
const NavbarWrapper = () => {
const isAuth = useTypedSelector((state) => state.auth.isAuth);
if (!isAuth) {
return null;
}
return <Navbar />;
};
const App = () => (
<BrowserRouter>
<Provider store={store}>
<ConnectedRouter history={history}>
<AppRouterContainer />
<NavbarWrapper />
<Feedback />
</ConnectedRouter>
</Provider>
</BrowserRouter>
);
export default App;
Also, I think you should move the NavbarWrapper component to a separate file.
Here is an Error:
Objects are not valid as a React child (found: object with keys {$$typeof, type, compare, WrappedComponent}). If you meant to render a collection of children, use an array instead.
It works just fine if I don't use connect in main.js and using connect in App.js doesn't make an Error, but once I use connect in main.js it throws me this error. What do I do wrong? And I'm using connect same way as in App.js Thank you
Here is sandBox https://codesandbox.io/s/busy-euler-7mpi7?file=/src/main.js
you can experience, just delete connect in main.js and it will start working
App.js
import React, { useEffect } from "react";
import './styles/main.scss';
import './App.scss';
import routes from "./router/router";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import { connect } from "react-redux";
import {checkUser, fetchUsers, fetchPolls} from "./store/index";
function App (props) {
useEffect(() => {
let { loadUsers} = props
loadUsers();
}, [])
let jsxRoutes = routes.map(el =>
<Route
path={el.url}
exact={el.exact}
key={el.url}>
{ el.component }
</Route>
)
return (
<Router>
<div className="App">
<Switch>
{ jsxRoutes }
</Switch>
</div>
</Router>
);
}
const mapStateToProps = state => {
return {
users: state.users.data,
}
}
const mapDispatchToProps = dispatch => {
return {
loadUsers: () => dispatch(fetchUsers())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
Main.js
import React from "react";
import "./main.scss"
import { connect } from "react-redux"
const Main = (props) => {
return(
<main>
main pg
</main>
)
}
export default connect(null, null)(Main);
In App.js try this instead:
let jsxRoutes = routes.map((el) => (
<Route path={el.url} exact={el.exact} key={el.url} component={el.component} />
));
Or the shorter version: <Route {...el} />
I started to learn React, I'm trying to retrieve data from api, the data is an object with the fields of base, date & rates, without any problem I can print and logout base & date but rates which is an object not.
console.log gives undefined, when trying to iterate is obviously that the object does not exist but in DevTools i can see normal data
Thank you for your help and greetings
Context:
export const ExchangeProvider = props => {
const [lastestExchanges, setLastestExchanges] = useState({})
const fetchLastestExchange = async () => {
try {
await fetch(`https://api.exchangeratesapi.io/latest`).then(data => data.json()).then(data => setLastestExchanges(data))
} catch (err) {
console.log(err)
}
}
useEffect(() => {
fetchLastestExchange()
}, [])
return (
<ExchangeContext.Provider value={[lastestExchanges, setLastestExchanges]}>
{props.children}
</ExchangeContext.Provider>
)
}
Usage:
import React, {useState, useContext} from "react";
import {ExchangeContext} from "../ExchangeContext";
function HomeView() {
const [lastestExchange, setLastestExchange] = useContext(ExchangeContext)
console.log(lastestExchange)
return (
<div className="container">
<p>{lastestExchange.base}</p>
<p>{lastestExchange.date}</p>
{/*<p>{lastestExchange.rates['PLN']}</p>*/}
<ul>
{/*{Object.keys(lastestExchange.rates).map(key => <li>{lastestExchange.rates[key]}</li>)}*/}
</ul>
</div>
)
}
export default HomeView
Provider usage:
import React from 'react';
import HomeView from "./Views/HomeView";
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
import {ExchangeProvider} from "./ExchangeContext";
function App() {
return (
<ExchangeProvider>
<div className="App container w-full flex h-full">
<Router>
<Switch>
<Route path="/">
<HomeView/>
</Route>
</Switch>
</Router>
</div>
</ExchangeProvider>
);
}
export default App;
You can use react context simpler like this :
// src/ThemeContext.js
import React from 'react';
const ThemeContext = React.createContext(null);
export default ThemeContext;
// src/ComponentA.js
import React from 'react';
import ThemeContext from './ThemeContext';
const A = () => (
<ThemeContext.Provider value="green">
<D />
</ThemeContext.Provider>
);
// src/ComponentD.js
import React from 'react';
import ThemeContext from './ThemeContext';
const D = () => (
<ThemeContext.Consumer>
{value => (
<p style={{ color: value }}>
Hello World
</p>
)}
</ThemeContext.Consumer>
);
I expect that console.log('Refresh') runs every time the route changes (switching from Component1 to Component2). But it's only triggering on first render. Why?
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
ReactDOM.render(<BrowserRouter><App /></BrowserRouter>, document.getElementById('root'));
App.js:
import React, { useEffect } from 'react';
import { Switch, Route } from 'react-router-dom';
import Nav from './Nav';
import Component1 from './Component1';
import Component2 from './Component2';
const App = () => {
useEffect( () => console.log('Refresh'));
return (
[<Switch>
<Route component = {Nav}/>
</Switch>,
<Switch>
<Route exact path = '/component1' component = {Component1}/>
<Route exact path = '/component2' component = {Component2}/>
</Switch>]
);
}
export default App;
Nav.js:
import React from 'react';
import { Link } from 'react-router-dom';
const Nav = () => {
return (
<div>
<Link to = '/component1'>Component 1</Link>
<Link to = '/component2'>Component 2</Link>
</div>
);
}
export default Nav;
Component1.js:
import React from 'react';
const Component1 = () => {
return (
<div>
<p>Hi</p>
</div>
);
}
export default Component1;
Component2.js:
import React from 'react';
const Component2 = () => {
return (
<div>
<p>Bye</p>
</div>
);
}
export default Component2;
The useEffect is not triggered because the App component is not re-rendered, nothing changed in that component (no state or props update).
If you want the App component to re-render when the route change, you can use the withRouter HOC to inject route props, like this :
import { Switch, Route, withRouter } from 'react-router-dom';
const App = () => {
useEffect( () => console.log('Refresh'));
return (...);
}
export default withRouter(App);
Example : https://codesandbox.io/s/youthful-pare-n8p1y
use the key attribute so everytime we render new component (different key)
<Route path='/mypath/:username' exact render= {routeProps =><MyCompo {...routeProps} key={document.location.href} />} />
Use the 2nd argument to useEffect to conditionally apply effect. For example via react-router-dom, you get some properties
const { schoolId, classId } = props
useEffect(() => {
// fetch something here
}, [schoolId, classId)
Here [schoolId, classId acts as the unique identifier for useEffect to trigger.
Using Hooks:
use useLocation and useLayoutEffect get more efficiency:
import { useLocation } from "react-router-dom";
//...
const location = useLocation();
//...
useLayoutEffect(() => {
console.log("location",location)
}, [location])
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>
);
};