I'm learning React/Redux, and while rewriting my redux store to better store the state and add async thunks, my app stopped rendering. My console shows no errors and the page is blank.
App.js
import './App.css'
import React from 'react'
import SearchBar from '../features/search/SearchBar'
import Post from '../components/Post'
import Subreddits from '../components/Subreddits'
import { BsReddit } from 'react-icons/bs'
const App = () => {
let listOfPosts = []
for (let i = 0; i < 5; i++){
listOfPosts.push(<Post iterator={i} />)
}
return (
<main>
<header>
<h1>
<BsReddit />
<span>Reddit</span> Minimal
</h1>
<SearchBar />
</header>
<div className='post-container'>
{listOfPosts.map((post, i) => <li key={'post_' + i}>{post}</li>)}
</div>
<Subreddits />
</main>
);
}
export default App
store.js
import { configureStore } from '#reduxjs/toolkit'
import { postsReducer } from '../features/posts/postsSlice'
import React from 'react'
export const store = configureStore({
reducer: {
posts: postsReducer
}
})
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app/App';
import { store } from './app/store'
import { Provider } from 'react-redux'
const render = () => {
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
}
store.subscribe(render)
I can show more of the code, but i assume the problem is somewhere in these 3 files. I truly don't understand why its not rendering.
Related
I'm kind of new to redux and react and I'm kind of stuck. so I have this webapp that adds a new list to the redux state todos array.
In my redux devtools state the new list is being added properly but the useSelector in my app is not being updated properly and I can't seem to figure out why.
I tried looking it up online and tried different methods but they didn't work ;-;
Sorry if this is a nooby question and ty in advance for any help <3
my App.js file:
import { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { addTask } from './redux/toDoSlice';
import SideBar from './components/SideBar';
function App() {
const lists = useSelector((state)=> state.todos);
return (
<SideBar
lists = {lists}
/>
)
export default App;
toDoSlice.js file:
import {createSlice} from '#reduxjs/toolkit';
const toDoSlice = createSlice({
name: "todos",
initialState: [],
reducers: {
addList: (state , action)=>{
state.push(action.payload.list);
},
}
});
export const {
addList,
} = toDoSlice.actions;
export default toDoSlice.reducer;
my SideBar.js file:
import React, { useState } from "react";
import { useDispatch } from 'react-redux';
import { addList } from "../redux/toDoSlice";
export default function SideBar(props){
const lists = props.lists;
const dispatch = useDispatch();
const addNewListEvent = (e)=>{
e.preventDefault();
var date_time = new Date().toLocaleString();
var title = 'my list '+date_time;
var list = {
_id:date_time,
title:title,
items:[]
};
dispatch(addList({list: list}));
}
return(
<button onClick={addNewListEvent}>New</button>
)
}
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import './index.css';
import App from './App';
import store from './redux/store';
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
I've made two different windows with react and provide same store. But if i change store data in one window, second window doesn't changed. And idk know how to synchronize.(All Reducers and Actions made as on default React project)
First provide(index.js):
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import {Provider} from 'react-redux';
import store from "../redux/store";
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider>,
document.getElementById("root"))
First App.js:
import React, {useEffect, useState} from 'react';
import {useDispatch, useSelector} from "react-redux";
import {addNumberAction} from "../redux/addNumber";
export default function App() {
const {number} = useSelector(state=>state.testPage)
const dispatch = useDispatch();
let changeNumber = number
return (
<>
<h1>First</h1>
<button onClick={()=>dispatch(addNumberAction(++changeNumber))}>{number}</button>
</>
)
}
Second provide(index.js):
import React from 'react';
import ReactDOM from 'react-dom'
import App from "./App";
import {Provider} from "react-redux";
import store from "../redux/store";
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider>,
document.getElementById("root2")
)
Second App.js:
import React, {useEffect} from 'react';
import {useDispatch, useSelector} from "react-redux";
import {addNumber, addNumberAction} from "../redux/addNumber";
export default function App() {
const {number} = useSelector(state=>state.testPage)
const dispatch = useDispatch();
let changeNumber = number
return (
<>
<h1>Second</h1>
<button onClick={()=>dispatch(addNumberAction(++changeNumber))}>{number}</button>
</>
)
}
They are not the same store. Each time you use <Provider store={store}> it basically create a store for any components inside it. Calling two <Provider store={store}> will create 2 independent store.
I tried to configure redux state to share state between browser’s windows. At the end, I found info, how it can be with hook.
For example,
import React, { useState, useEffect } from "react";
function HelloStorage() {
const [name, setName] = useState("");
useEffect(() => {
localStorage.setItem("name", name);
}, [name]);
useEffect(() => {
const onReceiveMessage = (e) => {
const { key, newValue } = e;
if (key === "name") {
setName(newValue);
}
};
window.addEventListener("storage", onReceiveMessage);
return () => {
window.removeEventListener("storage", onReceiveMessage);
};
}, []);
const handleChange = (e) => {
setName(e.target.value);
};
return <input value={name} onChange={handleChange} />;
}
For this link you can find information, how create a custom React Hook to share state between browser tabs without Redux state.
You can use redux-state-sync for that (https://www.npmjs.com/package/redux-state-sync).
Lets say you create the store currently like this:
import { configureStore } from '#reduxjs/toolkit';
export const store = configureStore({
reducer
});
All you need is adding the middleware to the store:
import { configureStore } from '#reduxjs/toolkit';
import { createStateSyncMiddleware, initMessageListener } from 'redux-state-sync'
export const store = configureStore({
reducer,
middleware: [createStateSyncMiddleware()],
});
initMessageListener(store);
//created action.js,rootReducer called everything in index.js but having issue on same..
//getting issue:-
Error: Could not find "store" in the context of "Connect(Home)". Either wrap the root component in a <Provider>, or pass a custom React context provider to <Provider> and the corresponding React context consumer to Connect(Home) in connect options.
//added rootReducer.js:-
import {combineReducers} from 'redux';
export default combineReducers({
})
//added action.js:-
export const helloRedux= () =>(dispatch:any)=>{
alert("Heloo Redux")
}
//added index.js:-
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { Provider} from 'react-redux';
import { createStore, applyMiddleware} from 'redux';
import ReduxThunk from 'redux-thunk';
import rootReducer from './rootReducer';
const store = createStore(rootReducer, applyMiddleware(ReduxThunk))
const MyAppWithStore = () => (
<Provider store={store}>
<App />
</Provider>
);
ReactDOM.render(<MyAppWithStore />, document.getElementById('root'));
serviceWorker.unregister();
//added home.js:-
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { helloRedux } from '../services/action'
class Home extends Component {
render() {
console.log(this.props)
return (
<div>
<h1>home Component</h1>
<button onClick = {() => this.props.helloRedux()}>Click Me</button>
{/* <button onClick = {() => this.helloRedux()}>Click Me</button> */}
</div>
);
}
}
const MapDispachToProps = dispach =>({
helloRedux:()=> dispach(helloRedux())
})
const mapStateToProps= state=> {
{
}
}
const HomeCom =connect(
mapStateToProps,
MapDispachToProps
)(Home)
export default HomeCom;
//app.js
import React from 'react';
import logo from './logo.svg';
import './App.css';
import Home from './component/home'
function App() {
return (
<div className="App">
<header className="App-header">
{/* <img src={logo} className="App-logo" alt="logo" /> */}
<Home />
</header>
</div>
);
}
export default App;
//created action.js,rootReducer called everything in index.js but having issue on same..
//getting issue:-
Error: Could not find "store" in the context of "Connect(Home)". Either wrap the root component in a <Provider>, or pass a custom React context provider to <Provider> and the corresponding React context consumer to Connect(Home) in connect options.
I'm using react-router v4.2.2 in my project, and am trying to create a set of cards that each link to other components. Right now I'm just testing that the router works, by routing each Card to one specific component called 'Project1'. This, however, is not working; I'm not seeing the div inside the Project1 component pop up. What am I doing wrong?? Shouldn't each Card link to the Project1 component?
Here is the code for the main container that holds the cards:
import React from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import ProjectCard from '../components/project_card.js';
import Project1 from '../components/project1.js';
class ProjectCards extends React.Component {
render() {
var projectCards = this.props.projects.map((project, i) => {
return (
<div key={i}>
<Link to={`/${project.title}`}>
<ProjectCard title={project.title} date={project.date} focus={project.focus}/>
</Link>
</div>
);
});
return (
<div>{projectCards}</div>
);
}
}
function mapStateToProps(state) {
return {
projects: state.projects
};
}
export default connect(mapStateToProps)(ProjectCards);
Here is the code for the Routes container:
import React from 'react';
import Project1 from '../components/project1.js';
import { connect } from 'react-redux';
import { Route, Switch } from 'react-router-dom';
class Routes extends React.Component{
render() {
var createRoutes = this.props.projects.map((project, i) => {
return <Route key={i} exact path={`/${project.title}`} component={Project1}/>
});
return (
<Switch>
{createRoutes}
</Switch>
);
}
}
function mapStateToProps(state) {
return {
projects: state.projects
};
}
export default connect(mapStateToProps)(Routes);
Here is the code for the index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { applyMiddleware, createStore } from 'redux';
import ReduxPromise from 'redux-promise';
import { BrowserRouter } from 'react-router-dom';
import App from './components/App.jsx';
import css from '../style/style.css';
import style from '../style/style.css';
import reducers from './reducers';
const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
, document.getElementById('root'));
and the code for Project1, which should display when a Card has been clicked:
import React from 'react';
const Project1 = () => {
return (
<div>hello there this is Project1</div>
);
}
export default Project1;
When you click on a link, you navigate to Project1, which has no Routes defined. You basically destroy your Route when you lick on it because the Switch is in the same component as the Link. The Switch statement needs to be moved to a 3rd component so that it still exists after clicking on a linking card.
I am doing Server Side Rendering for the first time with React and Redux and seeming to be having a little difficulty. I am getting the warning:
Warning: Did not expect server HTML to contain a <li> in <ul>.
I have looked this up and it means that there is a html tree mismatch. I am not sure how that is. Is there an obvious way to fix it? Here is the code I have that is throwing the warning.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import actions from '../actions';
class UsersList extends Component {
componentDidMount() {
if (this.props.users.length > 0) {
return;
}
this.props.fetchUsers();
}
render() {
const { users } = this.props;
return (
<div>
<ul>
{users.map(user => {
return <li key={user.id}>{user.name}</li>;
})}
</ul>
</div>
);
}
}
const stateToProps = state => {
return {
users: state.users
};
};
const dispatchToProps = dispatch => {
return {
fetchUsers: () => dispatch(actions.fetchUsers())
};
};
const loadData = store => {
return store.dispatch(actions.fetchUsers());
};
export { loadData };
export default connect(stateToProps, dispatchToProps)(UsersList);
Here is the client.js:
// Startup point for the client side application
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import { renderRoutes } from 'react-router-config';
import Routes from './Routes';
import reducers from './reducers';
const store = createStore(reducers, {}, applyMiddleware(thunk));
ReactDOM.hydrate(
<Provider store={store}>
<BrowserRouter>
<div>{renderRoutes(Routes)}</div>
</BrowserRouter>
</Provider>,
document.querySelector('#root')
);
The problem was that since the server had rendered the list of users already, it had a ul with lis. But when the client loaded there was no initial data so there was only an ul and no lis to accompany it.
To fix this problem, which I think not many will have because you need to do this anyway in server side rendering is to pass some initial state into the createStore redux function:
You will need to do this in two places. In the html on your server side and in your entry point on the client side.
Short example could be:
<html>
<head></head>
<body>
<div id="root">${content}</div>
<script>
window.INITIAL_STATE = ${serialize(store.getState())}
</script>
<script src="bundle.js"></script>
</body>
</html>
As you can see we have to set the initial state to the store that you created on your server side.
On your client side:
// Startup point for the client side application
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import { renderRoutes } from 'react-router-config';
import Routes from './Routes';
import reducers from './reducers';
const store = createStore(reducers, window.INITIAL_STATE, applyMiddleware(thunk));
ReactDOM.hydrate(
<Provider store={store}>
<BrowserRouter>
<div>{renderRoutes(Routes)}</div>
</BrowserRouter>
</Provider>,
document.querySelector('#root')
);
Now you have access to this global variable window.INITIAL_STATE and you just pass that through into the second argument of createStore which is the initial state.
I'm posting here now because I was searching this exact issue with nextJS SSG. Just like Dave Newton mentioned, in the jsx return I would simply add a check and not render the <ul> if it is empty due to a conditional check on the <li>'s. Something like this:
{Object.keys(el[i]).length > 0 ?
<ul>
{getKeys(docs, item, el).map(
([key, value], i) => {
const name = `${docs[item][el][key].name}`
const path = `${docs[item][el][key].path}`
return (name !== "undefined") && (
<li key={i}><a href={path}>{name}</a></li>
)}
)}
</ul>
: ''
}