React useContext() default value has not changed even I provide a value - javascript

Why is the UserContext default value has not changed even I specify a value in <UserContext.Provider>? How can I override the default value of the Context?
this is the App.jsx
import './App.css';
import React, { createContext } from 'react';
import ComponentB from './components/hooks/context/ComponentC';
function App() {
return (
<div className="App">
<ComponentB />
</div>
);
}
export default App;
this is the ComponentB
import React, { createContext, useState } from 'react';
import ComponentC from './ComponentC';
export const UserContext = React.createContext('default');
export const ChannelContext = React.createContext('default');
const provider = UserContext.Provider;
function ComponentB() {
return (
<div>
<provider value='Username'>
<ComponentC />
</provider>
</div>
);
}
export default ComponentB;
this is the ComponentC
import React from 'react';
import ComponentE from './ComponentE';
const ComponentC = () => {
return <ComponentE />;
}
export default ComponentC;
this is the ComponentE
import React, { Component, useContext } from 'react';
import { UserContext, ChannelContext } from './ComponentB';
const ComponentE = () => {
const username = useContext(UserContext);
const channel = useContext(ChannelContext);
return <div>username : {username} channel : {channel}</div>;
}
export default ComponentE;

In your App.jsx file, you say this:
import ComponentB from './components/hooks/context/ComponentC';
^ ^
Down the chain, this leads to this being rendered:
<div className="App">
<div>
username : {username} channel : {channel}
</div>
</div>
As you can see, there's no provider.
Even still, if we fix this one character typo, the issue persists.
This is because you say
const provider = UserContext.Provider;
...
<provider>
...
</provider>
which isn't allowed.
If you do
<UserContext.Provider>
...
</UserContext.Provider>
it works.
https://codesandbox.io/s/wizardly-andras-csxxy?file=/src/App.js
Regarding the first issue, this is why you should do
export const MyComponent = () => <></>;
import { MyComponent } from "./MyComponent";
instead of
const MyComponent = () => <></>;
export MyComponent;
import MyComponent from "./MyComponent";

Related

Reuse ReactJS component

I have built a ReactJS component for rendering emoction. Separate component can be built for each emoction, but I want to use one component but pass separate emoction as required.
This is what works so far:
emoction.js
import { faHeart } from "#fortawesome/free-solid-svg-icons";
import { faHeartBroken } from "#fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import React, { useState } from 'react';
const Emoction = () => {
return (
<FontAwesomeIcon icon={faHeart} />
);
};
export default Emoction;
emoction_hb.js
import { faHeart } from "#fortawesome/free-solid-svg-icons";
import { faHeartBroken } from "#fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import React, { useState } from 'react';
const EmoctionHb = () => {
return (
// <input type="text" />
<FontAwesomeIcon icon={faHeartBroken} />
);
};
export default EmoctionHb;
Now, I am bundling these two components as:
expanded_content.js
import Emoction from "../emoctions/emoctions";
import EmoctionHb from "../emoctions/emoctions_hb";
import styled from "#emotion/styled";
import { faHeartBroken } from "#fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "#fortawesome/react-fontawesome";
import React, { Component } from 'react';
const Merged = styled.div`
display: flex;
flex-direction: row;
justify-content: flex-start;
`;
const expandedContent = () => {
return(
<div>
<Merged>
<Emoction/>
<EmoctionHb/>
</Merged>
</div>
)
};
export default expandedContent;
which when I rendered using App.js
import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter as Router, Switch, Route} from 'react-router-dom';
import expandedContent from './components/merged_component/expanded_content'
class App extends React.Component {
render(){
return(
<Router>
<>
<Route path='/ExpandedContent' exact component={expandedContent}/>
</>
</Router>
)
}
}
export default App;
gives me.
What I am trying to do is that instead of creating a component called emoctions_hb.js I want to reuse emoction.js by passing "faHeartBroken" as the value in it.
If emoction.js is called without any value, I want it to use "faHeartBroken" as default value.
Tried following on to create Parent-Child relationship using https://webomnizz.com/change-parent-component-state-from-child-using-hooks-in-react/ but it did not work out for me.
Just pass the icon as a prop and set the default value to faHeartBroken:
const Emoction = ({ faIcon = faHeartBroken }) => {
return (
<FontAwesomeIcon icon={faIcon} />
);
};
It looks like you're importing useState but you're not implementing it anywhere. You could try implementing state in your expanded_content.js file and pass that down to your child component emoction.js, like this:
const ExpandedContent = () => {
const [heart, setHeart] = useState(true)
return(
<div>
<Emoction heart={heart} setHeart={setHeart}/>
</div>
)
};
export default ExpandedContent;
Notice that you will need to change the name of your component. See the docs here https://reactjs.org/docs/hooks-rules.html.
Then, inside of your Emoction component you will have access to heart which is set to true by default and you can also implement some logic to toggle the state using the function setHeart which is passed down from ExpandedContent:
const Emoction = ({heart, setHeart}) => {
const handleHearts = () => {
setHeart(heart => !heart)
}
return (
heart ? <FontAwesomeIcon icon={faHeart} /> : <FontAwesomeIcon icon={faHeartBroken} />
);
};
export default Emoction;
By using a ternary statement to return your component you can decide to show faHeart or faHeartBroken depending on the current state. All you need to do is add the functionality wherever you need it.

How can I maintain my store's state while using react-router and redux?

I am building an app prototype that essentially simulates ecommerce. I have components that each have different items that can be added to a cart(below I just show an example of how one would basically work). These components are accessed via different routes using the react-router. There is a header component that displays the number of items currently in the cart. The header gets the number of items in the cart from the state in the redux store. However, if I navigate to a new route, the store goes back to the default state. I need the the store to keep its state when a new route is navigated to. For example, if I go to the ShoppingPage, add an item to the cart, and then go back to the Home page, I need the cart to still have an item in it.
actions.js
export const actionTypes = Object.freeze({
UPDATE_CART: Symbol('UPDATE_CART'),
});
export const updateCart = (payload) => {
return {
type: actionTypes.UPDATE_CART,
payload,
};
};
export default actionTypes;
reducer.js
import actions from './actions';
export const INITIAL_STATE = {
cart: [],
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case actions.UPDATE_CART: {
return {
...state,
cart: action.payload,
};
}
default: {
return state;
}
};
};
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import reducer, { INITIAL_STATE } from './reducer';
const store = createStore(reducer, INITIAL_STATE);
console.log(store.getState());
ReactDOM.render(
<Provider store ={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
, document.getElementById('root'));
serviceWorker.unregister();
ShoppingPage.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { updateCart } from './actions';
class ShoppingPage extends Component {
addToCart = () => {
const cart = [...this.props.cart];
cart.push('new item');
this.props.modifyCart(cart);
render() {
return(
<div>
<button onClick={addToCart}>
Add To Cart
</button>
</div>
)
}
}
const mapDispatchToProps = dispatch => ({
modifyCart: payload => dispatch(updateCart(payload)),
});
const mapStateToProps = state => ({
cart: state.cart,
});
export default connect(
mapStateToProps,
mapDispatchToProps,
)(ShoppingPage);
Home.js
import React, { Component } from 'react';
import { ListGroup, ListGroupItem } from 'reactstrap';
class Home extends Component {
render() {
return(
<div>
<ListGroup>
<ListGroupItem><a href='/ShoppingPage'>ShoppingPage</a></ListGroupItem>
</div>
)
}
}
export default Home;
Header.js
import React, { Component } from 'react';
import { Navbar, NavbarBrand } from 'reactstrap';
import { connect } from 'react-redux';
class Header extends Component {
render() {
return(
<Navbar sticky='top' className='nav'>
<NavbarBrand href='/'>Online Shopping</NavbarBrand>
<span>{'Items in Cart: '}{this.props.cart.length}</span>
</Navbar>
)
}
}
const mapStateToProps = state => ({
cart: state.cart,
});
export default connect(
mapStateToProps
)(Header);
Routes.js
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import Home from './Home';
import ShoppingPage from './ShoppingPage';
const Routes = () => (
<Switch>
<Route exact path='/' component={Home} />
<Route exact path='/ShoppingPage' component={ShoppingPage} />
</Switch>
);
export default Routes;
App.js
import React from 'react';
import Routes from './Routes';
import Header from './Header';
function App() {
return (
<div>
<Header />
<Routes />
</div>
);
}
export default App;
What's likely happening is that during navigation the web app "reloads" again (which is wiping the redux state). In order to navigate with react router you want to look at <Link>.
For example,
Home.js
<a href='/ShoppingPage'>ShoppingPage</a>
should be changed to:
<Link to="/ShoppingPage">ShoppingPage</Link>

Redux, React-Redux accessing variable from one page to next

I'm defining a variable in Page1 and would like to access it in Page2 and then when clicking back to Page1 retrieve the same variable
So far, the variable is set on Page1 but cannot be retrieved on Page2
index.js
import {createStore, applyMiddleware, combineReducers} from 'redux'
import thunk from 'redux-thunk'
import {composeWithDevTools} from 'redux-devtools-extension'
import {Provider} from 'react-redux'
import variableReducer from './reducers'
const store = createStore(
variableReducer,
composeWithDevTools(applyMiddleware(thunk))
)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
serviceWorker.unregister();
actions/index.js
export const SET_MY_VARIABLE = 'SET_MY_VARIABLE'
export const setMyVariable = myVariable => ({
type: SET_MY_VARIABLE,
payload: {myVariable}
})
reducers/index.js
import {SET_MY_VARIABLE} from '../actions'
const initialState = {
myVariable: ''
}
const variableReducer = (state=initialState, action) => {
switch (action.type) {
case SET_MY_VARIABLE:
return {
...state,
myVariable: action.payload.myVariable
}
default:
return state
}
}
export default variableReducer
components/Page1.js
import React, {useEffect} from 'react'
import {connect, useDispatch} from 'react-redux'
import {setMyVariable} from '../actions'
const Page1 = (props) => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(setMyVariable(5000))
}, [])
return (
<div>
Setting variable<br />
Go to page 2
</div>
)
}
const mapState = state => {
return {
myVariable: state.myVariable
}
}
export default connect(mapState)(Page1)
components/Page2.js
import React from 'react'
import {connect} from 'react-redux'
const Page2 = (props) => {
const {myVariable} = props
console.log('props: ', props)
return (
<div>
Variable: {myVariable}
</div>
)
}
const mapState = state => {
console.log('map2 ', state.myVariable)
return {
myVariable: state.myVariable
}
}
export default connect(mapState)(Page2)
I should be able to set variables to the store in one component and access them throughout the entire App. Instead, I'm not able to retrieve them
Instead of using action.payload.myVariable use action.payload in your reducer/index.js
I've discovered the answer to my problem. I needed to change the <a href tag to a <Link> from react-router-dom in the Page1 component. The <a href was causing a complete reload of all JS and losing state. Here's the corrected component:
components/Page1.js
import React, {useEffect} from 'react'
import {connect, useDispatch} from 'react-redux'
import {setMyVariable} from '../actions'
import {Link} from 'react-router-dom'
const Page1 = (props) => {
const dispatch = useDispatch()
useEffect(() => {
dispatch(setMyVariable(5000))
}, [])
return (
<div>
Variable: {myVariable}<br />
<Link to="/page2">Go to page 2</Link>
</div>
)
}
const mapState = state => {
return {
myVariable: state.myVariable
}
}
export default connect(mapState)(Page1)

Could not find store using redux-form

Console is throwing the following error:
Could not find "store" in either the context or props of "Connect(Form(Form))". Either wrap the root component in a < Provider >, or explicitly pass "store" as a prop to "Connect(Form(Form))".
Done everything as said in redux-form tutorial, previously store was working with a mock reducer.
The line in which the error appears is where render() is executed -take a look at index.js file-.
configureStore.js
import { createStore } from 'redux';
import { devToolsEnhancer } from 'redux-devtools-extension';
import rootReducer from './rootReducer';
export default function configureStore(initialState = {}) {
const store = createStore(rootReducer, initialState, devToolsEnhancer());
return { store };
}
index.js
import React from 'react';
import { render } from 'react-dom';
import Root from './Root';
import './index.css';
import App from './whitesheet-components/App';
import registerServiceWorker from './registerServiceWorker';
import configureStore from './store/configureStore';
const { store } = configureStore();
const MOUNT_NODE = document.getElementById('root');
render(
<App>
<Root store={store} />
</App>,
MOUNT_NODE,
);
registerServiceWorker();
Root.js
import React from 'react';
import PropTypes from 'prop-types';
import { Provider } from 'react-redux';
const Root = ({ store }) => (
<Provider store={store} />
);
Root.propTypes = {
store: PropTypes.object.isRequired,
};
export default Root;
rootReducer.js
// use combineReducers when they are more than one
import { combineReducers } from 'redux';
import { reducer as form } from 'redux-form';
import mockReducer from './mockReducer';
const rootReducer = combineReducers({
mockReducer,
form,
});
export default rootReducer;
Form.js
import React from 'react';
import { Field, reduxForm } from 'redux-form';
import PropTypes from 'prop-types';
import titleField from './titleField';
const Form = (props) => {
const {
handleSubmit, submitting,
} = props;
return (
<form onSubmit={handleSubmit}>
<Field component={titleField} />
<div>
<button type="submit" disabled={submitting}>
Submit
</button>
<button type="button" disabled={submitting} onClick={() => console.log('boton para agregar input')}>
+
</button>
</div>
</form>
);
};
Form.propTypes = {
handleSubmit: PropTypes.any.isRequired,
submitting: PropTypes.any.isRequired,
};
// validate: nombreFuncion, // <--- validation function given to redux-form
export default reduxForm({
form: 'exerciseCreatorForm', // a unique identifier for this form
})(Form);
ExerciseCreator.js
import React from 'react';
import Form from './Form';
import './styles.css';
const ExerciseCreator = () => (
<div className="component-exercise-creator">
<Form />
</div>
);
export default ExerciseCreator;
Have the provider wraps the your App component, not the other way around. Like this:
// Root.js
// ...other codes...
const Root = ({ store, children }) => (
<Provider store={store}>{children}</Provider>
);
// ...other codes...
// index.js
// ...other codes...
render(
<Root store={store}>
<App />
</Root>,
MOUNT_NODE,
);
// ...other codes...

how to get const MyContext = React.createContext(); for other component in React js

I am making a new app using new Context API. In MyProvider component I get an error:
undefined Provider.
So friends how I can achieve this MyContext? I created separate .js files and where should I place const MyContext = React.createContext();?
App.js
import React, {Component} from 'react';
import Calsi from './Calsi'
import MyProvider from './MyProvider'
const MyContext = React.createContext();
class App extends Component {
constructor() {
super();
window.MyContext = MyContext;
}
render() {
return (
<MyProvider>
<div>
<Calsi/>
</div>
</MyProvider>
);
}
}
export default App;
Calsi.js
import React, {Component} from 'react';
import Sum from './Sum'
export default class Calsi extends Component{
render() {
return (
<div>
<Sum/>
</div>
);
}
}
Sum.js
import React, {Component} from 'react';
const MyContext = window.MyContext;
export default class Sum extends Component {
render() {
return (
<div>
<MyContext.Consumer>
{(context) => (
<React.Fragment>
<p>a:{context.state.a}</p>
<p>b:{context.state.b}</p>
<p>Sum: {context.state.a + context.state.b}</p>
<button onClick={context.increaseA}>increment a</button>
</React.Fragment>
)}
</MyContext.Consumer>
</div>
)
}
}
Provider.js
import React, {Component} from 'react';
const MyContext = window.MyContext;
export default class MyProvider extends Component {
state = {
a: 0,
b: 20,
}
render() {
return (
<MyContext.Provider value={{
state: this.state,
increaseA: () => this.setState({
a: this.state.a + 1
})
}}>
{this.props.children}
</MyContext.Provider>
)
}
}
I am new in react so how I can do this correctly? Also I am using react 16.3.0 alpha2 version. Thanks for your help.
You have to export your context. Don't attach it to the window object of the browser (window.MyContext = MyContext).
Create a new file and name it MyContext.js.
export const MyContext = React.createContext();
Then import it in your Provider.js:
import { MyContext } from "./MyContext";
...
<MyContext.Provider value={...}>

Categories

Resources