How do access a value from Context.Consumer with recompose? - javascript

Im passing some data with React Context API
and I try to access it from inside a recompose methods
In what way do you access Consumer's data with recompose?
import React from "react";
import { MyContext } from "./index";
import { fromRenderProps, withProps, compose } from "recompose";
const enhance = compose(
/**
* #todo add 'Mr.' to each name
*/
withProps(/** How do I get "names" from Consumer here? */)
);
const GrandChild = props => {
return (
<MyContext.Consumer>
{names => {
console.log(names)
return (
<div>
<h2>GrandChild</h2>
{names.map((name, index) => (<li key={index}>{name}</li>))}
</div>
);
}}
</MyContext.Consumer>
);
};
export default enhance(GrandChild);
live code:
https://codesandbox.io/s/k0xm2vlw8r

Here is one way to solve this:
GrandChild.js
import React from "react";
import { MyContext } from "./index";
import { withProps, compose } from "recompose";
const enhance = compose(
withProps(({ names }) => ({ reshapedNames: ["this first", ...names] }))
);
const GrandChild = props => {
return (
<div>
<h2>GrandChild</h2>
{props.reshapedNames.map((name, index) => (
<li key={index}>{name}</li>
))}
</div>
);
};
const EnhancedGrandChild = enhance(GrandChild);
const EnhancedGrandChildWithContext = props => {
return (
<MyContext.Consumer>
{names => <EnhancedGrandChild names={names} {...props} />}
</MyContext.Consumer>
);
};
export default EnhancedGrandChildWithContext;
Just adds a separate layer to provide the context.
Here's the CodeSandbox:

Related

How to get value of a component to another component

I am creating a Project where I need to get value of a component state value to another component.
How can I get the value?
Information
Both are functional component.
I need to send data from A (state) to B(state).
A state data are set from react dropdown menu.Which is a button.
I don't use Link or Route to A dropdown that's why I can't get it with useParams()
I need to set value in B component which will fetch data with passing language.
I have import all needed things & don't have any warnings & error.
Code of component A
Send value from this language state to B
const A = () => {
const [language, setLanguage] = useState('en');
return (
<Navbar expand="xl" bg='light' expand={false}>
<Container>
<DropdownButton id="dropdown-basic-button" title={<MdOutlineLanguage />}>
<Dropdown.Item as="button" onClick={() => setLanguage('en')}>English</Dropdown.Item>
<Dropdown.Item as="button" onClick={() => setLanguage('ar')}>العربية</Dropdown.Item>
<Dropdown.Item as="button" onClick={() => setLanguage('bn')}>বাংলা</Dropdown.Item>
</DropdownButton>
</Container>
</Navbar>
)
};
export default A
Code of Component B
I need to get here A state value & set it to B state. Then pass to useGetDataQuery & fetch data.
const B = () => {
let [language, setLanguage] = useState('en')
const { data } = useGetDataQuery({language })
return (
<>
</>
)
}
export default B
Redux Section
I'm using readux & #reduxjs/toolkit to store fetch data. Can I store my language data to here. Than how can get to from anywhere of my component.
react-rotuer-dom v6
export default configureStore({
reducer: {
[dataOne.reducerPath]: dataOne.reducer,
[data2.reducerPath]: dataTwo.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false
}).concat([dataOne.middleware, dataTwo.middleware]),
})
Maybe instead of using useState, you can use a global state by using useContext because I think your language will be use on several places as request body and of course to edit the state value, you can combine it with useReducer.
// UPDATE
working code: https://codesandbox.io/s/sleepy-monad-21wnc
MyContext
import { useReducer, useContext, createContext } from "react";
const initialValue = {
language: "id"
// other: "value",
};
const AppContext = createContext(initialValue);
const AppReducer = (state, action) => {
switch (action.type) {
case "CHANGE_LANGUAGE":
return {
...state,
...action.payload
};
default:
return state;
}
};
const MyContext = ({ children }) => {
const [state, dispatch] = useReducer(AppReducer, initialValue);
return (
<AppContext.Provider value={[state, dispatch]}>
{children}
</AppContext.Provider>
);
};
export const useAppContext = () => useContext(AppContext);
export default MyContext;
this is the context and reducer component later on use in App.js
App.js
import A from "./A";
import B from "./B";
import MyContext from "./MyContext";
import "./styles.css";
export default function App() {
return (
<MyContext>
<div className="App">
<A />
<B />
</div>
</MyContext>
);
}
A.js
import { useAppContext } from "./MyContext";
const A = () => {
const [globalState, dispatch] = useAppContext();
const onChange = (e) => {
dispatch({
type: "CHANGE_LANGUAGE",
payload: {
[e.target.name]: e.target.value
}
});
};
return (
<>
<p>Start Of Component A </p>
<input value={globalState.language} name="language" onChange={onChange} />
<p>End Of Component A </p>
</>
);
};
export default A;
B.js
import { useAppContext } from "./MyContext";
const B = () => {
const [globalState, dispatch] = useAppContext();
return (
<>
<p>Start Of Component B </p>
<h2>Language Val : {globalState.language}</h2>
<p>End Of Component B </p>
</>
);
};
export default B;

Display the data from 'this.state.data'?

Goal:
*Get the data of of variable Cars to the 'this.state.data' when you have retrieved the data from API.
*Display data from 'this.state.data' and not using the variable Cars.
Problem:
I do not know how to do it and is is it possible to do it when you have applied refactoring SOLID?
Info:
I'm newbie in React JS.
Stackblitz:
https://stackblitz.com/edit/react-v39jre?
App.js
import React from 'react';
import './style.css';
import CarsList from './components/CarsList';
import React, { Component } from 'react';
class App extends Component {
constructor() {
super();
this.state = {
name: 'React',
data: null
};
}
render() {
return (
<div className="App">
<CarsList />
</div>
);
}
}
export default App;
CarsList.jsx
import React, { useState, useEffect } from 'react';
this.state = {
name: 'React',
data: null
};
const CarsList = () => {
const [cars, setCars] = useState([]);
useEffect(() => {
const fetchCars = async () => {
const response = await fetch(
'https://jsonplaceholder.typicode.com/users'
);
setCars(await response.json());
};
fetchCars();
}, []);
return (
<div>
{cars.map((car, index) => (
<li key={index}>
[{++index}]{car.id} - {car.name}$
</li>
))}
</div>
);
};
export default CarsList;
After getting the response in the child component you should do a callback function which can be passed as prop from parent to child. Using the function you can pass the data from child to parent and update the parent state.
App.js
import { useState } from "react";
import CarsList from "./CarsList";
import "./styles.css";
export default function App() {
const [state, setState] = useState([]);
const handleUpdateParentState = (data) => {
setState(data);
};
console.log("state in parent", state);
return (
<div>
<CarsList updateParentState={handleUpdateParentState} />
</div>
);
}
CarsList.js
import React, { useState, useEffect } from "react";
const CarsList = (props) => {
const [cars, setCars] = useState([]);
useEffect(() => {
const fetchCars = async () => {
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/users"
);
const data = await response.json();
setCars(data);
props?.updateParentState(data);
} catch (error) {
console.log(error);
}
};
fetchCars();
}, []);
return (
<ul>
{cars?.map((car, index) => (
<li key={index}>
[{++index}]{car.id} - {car.name}$
</li>
))}
</ul>
);
};
export default CarsList;
Codesandbox
Data can be shared using props but from parent component to child component only. We cannot pass child component state to parent component through props.
Though we can create a function at parent level and pass it to child component as props so we can execute there.
In your case, you have to create a function in App component and pass it on carList component as props. In carList component you do not have to create the cars state. After fetching the cars from API just call the function you passed from App component
App.js
import React from 'react';
import './style.css';
import CarsList from './components/CarsList';
import React, { Component } from 'react';
class App extends Component {
constructor() {
super();
this.state = {
name: 'React',
data: null
};
}
function setCarList(cars) {
this.setState({
date: cars
});
}
render() {
return (
<div className="App">
<CarsList setCars={setCarList}/>
</div>
);
}
}
export default App;
CarList.js
import React, {useEffect } from 'react';
this.state = {
name: 'React',
data: null
};
const CarsList = (props) => {
useEffect(() => {
const fetchCars = async () => {
const response = await fetch(
'https://jsonplaceholder.typicode.com/users'
);
this.props.setCars(await response.json());
};
fetchCars();
}, []);
return (
<div>
{cars.map((car, index) => (
<li key={index}>
[{++index}]{car.id} - {car.name}$
</li>
))}
</div>
);
};
export default CarsList;
It doesn't make much sense for each CarList component to load data if you're going to have loads of them and they're going to share information with each other. You should load all your data in your App component using an array of API fetch calls and then use Promise.all to extract and parse the data, and then add it to the state. That state can be then shared with all your Carlist components.
Here's a React component:
const {Component} = React;
const json = '["BMW", "Clio", "Merc", "Fiat"]';
// Simulates an API call
function mockFetch() {
return new Promise((res, rej) => {
setTimeout(() => res(json), 1000);
});
}
class App extends Component {
constructor() {
super();
this.state = { cars: [] };
}
componentDidMount() {
// Have an array fetches (you would supply each one a
// different API endpoint in your code)
const arr = [mockFetch(), mockFetch(), mockFetch()];
// Grab the json, `map` over it and parse it
Promise.all(arr).then(data => {
const cars = data.map(arr => JSON.parse(arr));
// Then set the new state
this.setState(prev => ({ ...prev, cars }));
});
}
// You can now send the data to your small functional
// carlist components
render() {
const { cars } = this.state;
if (!cars.length) return <div />;
return (
<div>
<Carlist cars={cars[0]} />
<Carlist cars={cars[1]} />
<Carlist cars={cars[2]} />
</div>
)
}
};
function Carlist({ cars }) {
return (
<ul>{cars.map(car => <div>{car}</div>)}</ul>
);
}
// Render it
ReactDOM.render(
<App />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
And here's equivalent written as a functional component with hooks:
const {useState, useEffect} = React;
const json = '["BMW", "Clio", "Merc", "Fiat"]';
function mockFetch() {
return new Promise((res, rej) => {
setTimeout(() => res(json), 1000);
});
}
function App() {
const [cars, setCars] = useState([]);
// This works in the same way as the previous example
// except we're not setting `this.state` we're setting the
// state called `cars` that we set up with `useState`.
useEffect(() => {
function getData() {
const arr = [mockFetch(), mockFetch(), mockFetch()];
Promise.all(arr).then(data => {
const cars = data.map(arr => JSON.parse(arr));
setCars(cars);
});
}
getData();
}, []);
if (!cars.length) return <div />;
return (
<div>
<Carlist cars={cars[0]} />
<Carlist cars={cars[1]} />
<Carlist cars={cars[2]} />
</div>
);
};
function Carlist({ cars }) {
return (
<ul>{cars.map(car => <div>{car}</div>)}</ul>
);
}
// Render it
ReactDOM.render(
<App />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

How to test for props initial values?

I have a header component which listens for loggedInUser data from Redux store. I want to unit test for redux prop values. Like i have mocked a redux store for initial values and want to test for those values in props of the connected component.
import React, { useState } from 'react';
import { connect } from 'react-redux';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faUser, faShoppingCart } from '#fortawesome/free-solid-svg-icons';
import { NavLink, useHistory } from 'react-router-dom';
import Cart from './../Cart/Cart.component';
import { signOutStart } from './../../redux/user/user.actions';
import './Header.styles.scss';
export const Header = ({ noOfItemsInCart, loggedInUser, signOut }) => {
const [isUserDropDownVisible, setUserDropDownVisibility] = useState(false);
const [isCartDropDownVisible, setCartDropDownVisibility] = useState(false);
const history = useHistory();
console.log(loggedInUser);
return (
<header className = 'header' id = 'header'>
<NavLink to = '/'><p className = 'title'>Kart</p></NavLink>
{loggedInUser ? (
<div className = 'header__options' id = 'header__options'>
<div className = 'cart__options'>
<FontAwesomeIcon
icon={faShoppingCart}
onClick = {() => {
setUserDropDownVisibility(false);
setCartDropDownVisibility(prevState => {return !prevState})}
} />
<span><sup>{noOfItemsInCart}</sup></span>
{isCartDropDownVisible ? (
<div className="dropdown">
<Cart />
</div>
) : null}
</div>
<div className = 'user__options'>
<FontAwesomeIcon
icon={faUser}
onClick = {() => {
setCartDropDownVisibility(false);
setUserDropDownVisibility(prevState => {return !prevState})}
} />
{isUserDropDownVisible ? (
<div className="dropdown" onClick = {() => setUserDropDownVisibility(false)}>
<NavLink to = '/orders'>My Orders</NavLink>
<span onClick = { async () => {
await signOut();
history.push('/auth');
} }>Logout</span>
</div>
) : null}
</div>
</div>
) : null}
</header>
)
}
const mapStateToProps = state => {
return {
loggedInUser: state.user.loggedInUser,
noOfItemsInCart: state.cart.noOfItemsInCart
}
}
const mapDispatchToProps = dispatch => {
return {
signOut: () => dispatch(signOutStart())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Header);
I had implemented an unit test as follows, by using a shallow render of component and tried accessing the props using .props()
import React from 'react';
import { shallow } from 'enzyme';
import configureMockStore from 'redux-mock-store';
import Header from './Header.component';
const mockStore = configureMockStore();
describe('<Header />', () => {
let wrapper, store;
beforeEach(() => {
const initialState = {
user: {
loggedInUser: 'user1',
error: null
},
cart: {
noOfCartItemsInCart: 0
}
}
store = mockStore(initialState);
wrapper = shallow(
<Header store = {store} />
)
});
it('should have valid props', () => {
expect(wrapper.props().loggedInUser).toBe('user1');
})
})
I am getting prop values a undefined or null values. How to test for prop values to an redux connected component?
Have you tried this from the docs?
wrapper.instance().props

Why does the component not re-render after callback?

Given the following two components, I expect the EntryList component to re-render after the state changes in the handleEnttryDelete after the button in EntryForm is clicked. Currently the state changes, but the UI isn't updating itself:
import React, { useState } from "react";
import Button from "#material-ui/core/Button";
import { render } from "#testing-library/react";
const EntryList = (props) => {
const [entryList, setEntryList] = useState(props.data);
const handleEntryDelete = (entry) => {
const newState = entryList.filter(function (el) {
return el._id != entry._id;
});
setEntryList(() => newState);
};
return (
<div>
{entryList.map((entry) => {
return (
<EntryForm entry={entry} handleEntryDelete={handleEntryDelete} />
);
})}
</div>
);
};
const EntryForm = (props) => {
const [entry, setEntry] = useState(props.entry);
return (
<div>
<Button onClick={() => props.handleEntryDelete(entry)}>
{entry._id}
</Button>
</div>
);
};
export default EntryList;
Your code probably works, but not as intended. You just have to use key while mapping arrays to components.
Therefore, React can distinguish which elements should not be touched during reconciliation when you delete one of the nodes
<div>
{entryList.map((entry) => {
return <EntryForm key={entry._id} entry={entry} handleEntryDelete={handleEntryDelete} />;
})}
</div>;

React how to create Notifications without npm

Hi i just started learning react.
Is it possible to do this without classes (functional programming)?
Index.js has a button with an axios call.
When the answer came, a notification should appear and disappear in a second.
App.js
import React from 'react';
import {BrowserRouter, Route} from 'react-router-dom';
import Index from './components/index/index';
import Notifications from './components/notifications/notifications';
const App = (props) => {
return (
<BrowserRouter>
<Route exact path="/" render={ () => <Index notification={ <Notifications/> } /> } />
</BrowserRouter>
);
}
export default App;
Index.js
import React from 'react';
const axios = require('axios');
const Index = (props) => {
let getData = () => {
axios.get('url')
.then(function (response) {
<Notification text={ response.data }/> );
})
.catch(function (error) {
console.log(error);
});
}
return (
<button onClick={ () => getData() }>Get data</button>
);
}
export default Index;
Notification.js
import React from 'react';
const Notification = (props) => {
return (
<div>
<div>
<p>props.text</p>
</div>
</div>
);
//and delete after 1 second
}
export default Notification;
Please show examples of functional solutions :)
In your axios.then, you can store the result in state, and set a timeout to clear the state 1s later. Then you render Notification if there is something in state
const Index = (props) => {
const [text, setText] = useState();
let getData = () => {
axios.get('url')
.then(function (response) {
setText(response.data);
setTimeout(() => setText(), 1000);
})
.catch(function (error) {
console.log(error);
});
}
return (
<>
<button onClick={() => getData()}>Get data</button>
{text &&
<Notification text={text} />
}
</>
);
}
To render a notification in the screen, normally I would use React Portals
In order to do that your Notification component need to render to DOM through Portal
const notificationRoot = document.getElementById('notification-root'); // Create the element in your main html file in order for your notification to "portal" in
const Notification = (props) => {
return (
<div>
<div>
<p>props.text</p>
</div>
</div>
);
};
const DisplayNotification = () => {
const domNode = usememo(() => document.createElement('div'), []);
useEffect(() => {
notificationRoot.appendChild(domNode);
return () => {
notificationRoot.removeChild(domNode);
}
}, [])
return ReactDOM.createPortal(
<Notification />,
domNode
); // Portal to your node
}
By rendering DisplayNotification, your Notification should pop up.
You should use redux for achieve this, when you receive data from API, dispatch a redux action who return a true/false boolean.
The benefit of this proposal solution, after you developing system, you need to call only one function, and dispatch this into your store that's it !!
Place you <Notification /> component at top of your app
Like :
const App = (props) => {
return (
<Provider store={store}>
<Notification />
<BrowserRouter>
<Route exact path="/" render={/* YOUR HOMEPAGE COMPONENT */} />
</BrowserRouter>
</Provider>
);
}
Please look redux solution here : https://redux.js.org/introduction/getting-started
Inside your <Notification />
Don't forget to connect at redux you should use the connect() is an HOC (High Order Component)
import React from 'react';
import { connect } from 'redux'
const Notification = (props) => {
return (
<div>
<div>
<p>props.text</p>
</div>
</div>
);
//and delete after 1 second
}
const mapStateToProps = (state) => {
/* Get your state from your store notification for example */
return {}
}
export default connect(mapStateToProps)(Notification);

Categories

Resources