I'm new to redux and trying to grasp the concept of it. I'm receiving the error when redux is looking for context value. I'm sure that I wrapped the whole application in the Provider but still don't understand why it still results in the following error:
Error: could not find react-redux context value; please ensure the component is wrapped in a Provider
This is my themeReducer.js file:
const initialState = {
theme: "dark",
}
const themeReducer = (state = initialState, action) => {
if (action.type === "changeTheme"){
if (state.theme === "dark"){
return {theme: "light"};
} else {
return {theme: "dark"}
}
}
return state
}
export default themeReducer
This is my index.js for the store:
import {createStore, combineReducers} from 'redux';
import themeReducer from './reducers/themeReducer'
const rootReducer = combineReducers({
theme: themeReducer
})
const store = createStore(rootReducer)
export default store
Lastly, this is where I wrapped the root component with the provider to access the state:
import "react-native-gesture-handler";
import React from "react";
import { ThemeProvider } from "styled-components";
import AppNavContainer from "./src/navigations/index";
import { Provider } from "react-redux";
import { useSelector } from "react-redux";
import store from "./src/store/index"
export default function App() {
const currentTheme = useSelector(state => state.theme)
return (
<Provider store={store}>
<ThemeProvider theme={{ mode: {currentTheme} }}>
<AppNavContainer />
</ThemeProvider>
</Provider>
);
}
I'm using the styled component to access the theme value to change the background colour of my custom components. How I understand redux is that if I wrap everything under the Provider, I can access that state anywhere underneath that Provider. Did I do something wrong? Any opinions are appreciated
return (
<Provider store={store}>
<ThemeProvider theme={{ mode: {currentTheme} }}>
<AppNavContainer />
</ThemeProvider>
</Provider>
);
The store (and thus useSelector) is only available to components that are farther down the component tree from the Provider. So in ThemeProvider, in AppNavContainer, and anything inside AppNavContainer. You cannot use useSelector in App, because there is no Provider farther up the tree from App.
You'll probably want to split this component into multiple components. For example:
export default function App() {
return (
<Provider store={store}>
<ThemeWrapper />
</Provider>
);
}
function ThemeWrapper () {
const currentTheme = useSelector(state => state.theme)
return (
<ThemeProvider theme={{ mode: {currentTheme} }}>
<AppNavContainer />
</ThemeProvider>
);
}
Related
I'm building a react Amazon clone, so in order to give the user the ability to add a product into his basket (as the first version of all the future checkout system), I've used Context Api to manage this.
When I finished writing Context Api code and adding it to the index.js file so I could access the data I got a blank screen with absolutely no error messages. I don't know where is exactly the problem.
StateProvider.js
import React , { createContext, useContext, useReducer} from 'react'
export const StateContext = createContext();
export const StateProvider = ({reducer , initialValue, children}) => {
<StateContext.Provider value={useReducer(reducer, initialValue)}>
{children}
</StateContext.Provider>
};
export const useStateValue = () => useContext(StateContext)
reducer.js
export const initialState = {
basket: [],
};
const reducer = (state, action) => {
switch(action.type) {
case "ADD_TO_BASKET":
return{
...state,
basket: [...state.basket, ...action.item]
};
default:
return state;
};
};
export default reducer;
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import { StateProvider } from './Special_components/StateProvider';
import reducer, { initialState } from './Special_components/reducer';
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<StateProvider reducer={reducer} initialState={initialState} >
<App />
</StateProvider>
</React.StrictMode>
)
Looks like you're providing props to the wrong component.
Try this in index.js:
...
<StateProvider reducer={reducer} initialValue={initialState}>
<App />
</StateProvider>
...
Also note how I renamed the prop to initialValue because that's what your StateProvider is expecting.
You forgot to add return in your StateProvider function
export const StateProvider = ({reducer , initialValue, children}) => {
return (
<StateContext.Provider value={useReducer(reducer, initialValue)}>
{children}
</StateContext.Provider>
)}
export const useStateValue = () => useContext(StateContext)
You must change arrow ES6 {} => () because {} need return
export const StateProvider = ({reducer , initialValue, children}) => (
<StateContext.Provider value={useReducer(reducer, initialValue)}>
{children}
</StateContext.Provider>
);
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)
My Component is not receiving the props from the redux store. I am calling the Action by an onClick event which is this. On Click the name "wrapper" is changed to "wrapper slide-menu" in the Redux Store, however, the Props are not updating when I do console.log(). , but when I check Redux dev tools, the store is shown to be updated.
<a onClick={ ()=> this.props.setName("wrapper")} ><i className="zmdi zmdi-menu ti-align-right"></i></a>
This is a part of my Component which is console logging siderbarname which is updated from the redux store.
import React, { Component } from "react";
import "../App.css";
import { Link } from "react-router-dom";
class Index extends Component {
constructor(props) {
super(props);
this.state = {
sidebarname: this.props.sidebarname
};
}
render() {
const sidebarName = this.props.sidebarname;
console.log("sidebarname is " + sidebarName);
console.log(this.props);
This is my Action.
import { connect } from "react-redux";
import * as Actions from "./indexActionTypes";
import App from "../../_layouts";
const mapStateToProps = state => ({
sidebarname: state.sidebarReducer.sidebarname
});
const mapDispatchToProps = dispatch => ({
setName: sidebarname => {
//console.log('setName is called');
//console.log('togglemargin is'.togglemargin);
if (sidebarname !== "wrapper slide-menu") {
dispatch({
type: Actions.TOGGLE_SIDEBAR,
sidebarname: sidebarname
});
}
if (sidebarname === "wrapper") {
console.log("this is called if2");
dispatch({
type: Actions.TOGGLE_SIDEBAR,
sidebarname: "wrapper slide-menu"
});
}
}
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
And this is my Reducer.
import * as Actions from "../../actions/indexToggle/indexActionTypes";
//const sidebarname = "wrapper slide-menu";
let initialState = { sidebarname: "wrapper" };
const sidebarReducer = (state = initialState, action) => {
switch (action.type) {
case Actions.TOGGLE_SIDEBAR:
console.log("reducer called");
console.log(state);
//console.log('action',action);
return Object.assign({}, state, {
sidebarname: action.sidebarname
});
default:
return state;
}
};
export default sidebarReducer;
This is my Store.
import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import rootReducer from "../reducers/rootreducer";
//const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
compose(
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
);
export default store;
This is my App.js File.
import React, { Component } from 'react';
import './App.css';
import { Provider } from 'react-redux';
import store from './store/store';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import App from './actions/indexToggle/indexActions';
import FirstDashboard from './_layouts/views/firstDashboard';
import SecondDashboard from './_layouts/views/secondDashboard';
import ThirdDashboard from './actions/dashboardToggle/thirdDashboardToggleAction';
import FourthDashboard from './_layouts/views/fourthDashboard';
class Main extends Component {
render() {
return (
<Provider store={store}>
<Router>
<div>
<App />
<Route path='/overview1' exact strict component={FirstDashboard} />
<Route path='/overview2' exact strict component={SecondDashboard} />
<Route path='/overview3' exact strict component={ThirdDashboard} />
<Route path='/overview4' exact strict component={FourthDashboard} />
</div>
</Router>
</Provider>
);
}
}
export default Main;
I am really getting confused why it is not rendering inside the component.
However, it is getting updated in the store.
#codemt Please add below method in your code it will receive the updated props
componentWillReceiveProps(nextProps) {
const {sidebarname} = nextProps;
this.setState({sidebarname});
}
Please put inside your relative component in your case is Index
I am unable to make the store available to children components.
The setup is a SPA with Symfony as back-end, though this should not make a difference for this matter.
The entry point for Webpack is the file:
/client/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, applyMiddleware, compose } from 'redux';
import ReduxPromise from 'redux-promise';
import Root from './App';
import registerServiceWorker from './registerServiceWorker';
import reducers from './pages/combine_reducers';
let composeEnhancers = typeof(window) !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
const store = createStore(
reducers,
composeEnhancers(
applyMiddleware(ReduxPromise)
)
)
ReactDOM.render(
<Root store={store} />
, document.querySelector('#root')
);
registerServiceWorker();
The apps as such is at:
/client/App.js
import React from 'react';
import PropTypes from 'prop-types';
import { Provider } from 'react-redux';
import {
BrowserRouter as Router,
Route,
Link,
Switch
} from 'react-router-dom';
import HomePage from './pages/home/';
import AccountPage from './pages/account/';
const Root = ({ store }) => {
return(
<Provider store={store}>
<div className="App">
<header className="App-header">
<h1 className="App-title">Welcome to React</h1>
</header>
<Router>
<div>
<Link to="/account">Account</Link>
<Link to="/">Home</Link>
<div>
<Switch>
<Route path="/account" component={AccountPage} />
<Route path="/" component={HomePage} />
</Switch>
</div>
</div>
</Router>
</div>
</Provider>
)
}
Root.propTypes = {
store: PropTypes.object.isRequired
}
export default Root;
So far so good. The store is available in App.js.
But that's not the case at the next level. As you can see I'm attempting to make the store available using connect().
/client/pages/home/index.js
import React from 'react';
import { connect } from 'react-redux';
import Register from '../common/register/';
import PropTypes from 'prop-types';
class Home extends React.Component {
constructor(props){
super(props)
console.log(props);
}
render() {
return (
<div>
<h1> Hello World from home! </h1>
<Register />
</div>
);
}
}
Home.propTypes = {
store: PropTypes.object.isRequired
}
const mapStateToProps = (state) => {
return {
store: state.store,
}
}
export default connect(mapStateToProps)(Home)
At the lower level, the Register component, I'm able to submit the form, but the store not being available, I am unable to capture the response coming from the server.
/client/pages/common/register/index.js
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import RegisterForm from './containers/register';
import { actionSubmitRegister } from './actions/';
import PropTypes from 'prop-types';
class Register extends React.Component{
constructor (props) {
super(props);
this.state = {
registerResponse: '',
}
this.onSubmitRegister = this.onSubmitRegister.bind(this);
}
onSubmitRegister (event) {
event.preventDefault();
let submitForm = new Promise((resolve, reject) => {
actionSubmitRegister(this.props.form.RegisterForm.values);
});
submitForm.then((response) => {
console.log('response',response);
this.setState({registerResponse: this.props.submit_register.data});
console.log('registerResponse', this.state.registerResponse);
}).catch((error) => {
console.log(error);
});
}
render(){
return (
<div>
<div>
<RegisterForm
submitRegister={this.onSubmitRegister}
/>
<h3>{this.state.registerResponse}</h3>
</div>
</div>
)
}
}
/*
Register.propTypes = {
store: PropTypes.object.isRequired
}
*/
const mapStateToProps = (state) => {
return {
form: state.form,
submit_register: state.submit_register,
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators({actionSubmitRegister}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Register);
In mapStateToProps you map store: state.store but in general you use this method to map single props from your state to props in your component, not map the entire store (if this is even possible).
Eg:
form: state.form
The reason you are not able to access the store object in props is because you are not passing it down via props.
Provider from the react-redux library, makes it available to all children down the element tree. Store is made available via React's context API, NOT via props.
"Context is designed to share data that can be considered “global” for a tree of React components."
So in a child component of Provider, we can now do something like
render() {
const { store } = this.context;
console.log(store)
return(
...
)
}
This is the same way that react-redux's connect HOC is able to access the store and subsequently mapStateToProps or utilise the store's dispatch method to mapDispatchToProps.
Also I think Provider requires that it’s child element is a React component.
Check out this tutorial for a more in-depth explanation.
After the input I received above, I reviewed my code and got it to work.
Actually the main issue was on the /client/pages/common/register/index.js file, but I am posting the whole chain for reference:
/client/index.js
nothing to change
/client/App.js
The references to propTypes do not seem to be necessary, so I took them out.
import React from 'react';
import { Provider } from 'react-redux';
import {
BrowserRouter as Router,
Route,
Link,
Switch
} from 'react-router-dom';
import HomePage from './pages/home/';
import AccountPage from './pages/account/';
const Root = ({ store }) => {
return(
<Provider store={store}>
<div className="App">
<header className="App-header">
<h1 className="App-title">Welcome to React</h1>
</header>
<Router>
<div>
<Link to="/account">Account</Link>
<Link to="/">Home</Link>
<div>
<Switch>
<Route path="/account" component={AccountPage} />
<Route path="/" component={HomePage} />
</Switch>
</div>
</div>
</Router>
</div>
</Provider>
)
}
export default Root;
/client/pages/home/index.js
Here both propTypes and connect() do not seem to be required.
import React from 'react';
import Register from '../common/register/';
class Home extends React.Component {
constructor(props){
super(props)
}
render() {
return (
<div>
<h1> Hello World from home! </h1>
<Register />
</div>
);
}
}
export default Home;
/client/pages/common/register/index.js
The main issue here was the onSubmitRegister() method. The promise was not properly setup and I was referencing the action directly instead of using this.props. React do not seem to like that.
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import RegisterForm from './containers/register';
import { actionSubmitRegister } from './actions/';
class Register extends React.Component{
constructor (props) {
super(props);
this.state = {
registerResponse: '',
}
this.onSubmitRegister = this.onSubmitRegister.bind(this);
}
onSubmitRegister (event) {
event.preventDefault();
let submitForm = new Promise((resolve) => {
resolve(this.props.actionSubmitRegister(this.props.form.RegisterForm.values));
});
submitForm.then((result) => {
let data = result.payload.data;
this.setState({registerResponse: data.message});
}).catch((error) => {
console.log(error);
});
}
render(){
return (
<div>
<div>
<RegisterForm
submitRegister={this.onSubmitRegister}
/>
<h3>{this.state.registerResponse}</h3>
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
form: state.form,
submit_register: state.submit_register,
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators({actionSubmitRegister}, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Register);
My application is stored in /src/index.js but i also have a /App.js and a /index.js.
I don't know the difference between these and i think thats the reason im getting this error.
/index.js
import { AppRegistry } from 'react-native';
import App from './App';
AppRegistry.registerComponent('client', () => App);
/App.js
import App from './src/index';
export default App;
/src/index.js
import React from 'react';
import { AppRegistry } from 'react-native';
import { Provider, connect } from 'react-redux';
import { addNavigationHelpers } from 'react-navigation';
import Navigator from './routes/route';
import store from './store/configureStore';
const App = ({ dispatch, nav }) => {
<Navigator
navigation={addNavigationHelpers({
dispatch,
state: nav,
})}
/>
};
const mapStateToProps = state => ({
nav: state.nav,
});
const AppWithNavigation = connect(mapStateToProps)(App);
export default () => {
<Provider store={store}>
<AppWithNavigation />
</Provider>
}
I used create react native package to build this project and then tried to follow some guides to implement react navigation with redux.
Your default export is not returning anything :
export default () => {
<Provider store={store}>
<AppWithNavigation />
</Provider>
}
To return JSX with an arrow function you need to use () => ( <JSX /> ) or the equivalent with curly braces : () => { return ( <JSX /> ) } :
export default () => (
<Provider store={store}>
<AppWithNavigation />
</Provider>
)
or :
export default () => {
return (
<Provider store={store}>
<AppWithNavigation />
</Provider>
)
}
You forgot to return the components
const App = ({ dispatch, nav }) => {
return(
<Navigator
navigation={addNavigationHelpers({
dispatch,
state: nav,
})}
/>
)
};
export default () => {
return(
<Provider store={store}>
<AppWithNavigation />
</Provider>
)
}
I didn't mention this
import React from 'react';
and all other react-native components in my other files of screens.
Because I was calling my screen component from another file, from App.js file, so I also had to import react and react-native components in that file too.