How to call a method defined in others components - javascript

Assuming we have two functional components App and Product. From the Product component you can easily call a method defined in the parent component (App) by simply passing the method as props (methodA={methodA}). Can someone please tell me how to call a method defined in the Cart component from the Product component?
//App.js
import React from "react";
import Cart from "./Cart";
import Order from "./Order";
import Product from "./Product";
const App = props => {
const { order } = props;
const methodA = props => {
console.log(methodA);
}
return (
<div className="container">
<Cart>
{order.map((products, i) => {
return (
<Order key={i}>
{products.map((items, i) => (
<Product item={items} key={i} methodA={methodA} />
))}
</Order>
);
})}
</Cart>
</div>
);
};
export default App;
//Product.js
import React from 'react';
const Product = props => {
return <div className="delete" onClick={props.methodA}></div>;
};
export default Product;

That's why Redux is developed for easier state management. You should have Redux store and actions dispatched within this store. Once you have set up your redux store and actions, you can easily dispatch the same action from multiple components.

You have a few options:
Pass the products to the cart and let it render them (the best way in this case)
Use some state management like context or redux (in this case, an context passed by the cart component would be perfect, then the product component consume and use it functions)

Related

Problem when pass data in ReactJS with props and state

I have the problem when I try to pass the props through the function component .In parent component I have a state of currentRow with return an array with object inside, and I pass it to child component. It return a new object with an array inside it. What can I do to avoid it and receive exact my currentRow array.
there is example of the way I do it
Parent component
import React, { useState } from "react";
import ToolBar from "./Toolbar";
function Manage() {
const [currentRow, setCurrentRow] = useState();
console.log("from manage", currentRow);
return (
<div>
<ToolBar currentRow={currentRow} />
</div>
);
}
export default Manage;
Child Componet
import React from 'react'
function ToolBar(currentRow) {
console.log("from toolbar", currentRow);
return(
<div></div>
);
}
export default ToolBar
And this is my Log
enter image description here
Try accessing it like below:
import React from 'react'
function ToolBar({currentRow}) {
console.log("from toolbar", currentRow);
return(
<div></div>
);
}
export default ToolBar
A React component's props is always an object. The reason for this is that otherwise it would be impossible to access the properties of a component which received multiple props.
example:
<SomeComponent prop1={prop1} prop2={prop2} />
---
const SomeComponent = (props) => {
console.log(props.prop1);
console.log(props.prop2);
}
So in order to resolve your issue, you could destructure the props object in your ToolBar component like this:
const ToolBar = ({ currentRows }) => {
...
}
Just keep in mind that a component will always receive its props as an object. There is no way to change that as of right now.

React Hooks - Preventing child components from rendering

As a newbie in React, it seems that re-rendering of components is the thing not to do.
Therefore, for example, if I want to create a menu following this architecture :
App is parent of Menu, which have a map function which creates the MenuItem components
menu items come from a data source (here it's const data)
when I click on a MenuItem, it updates the state with the selected MenuItem value
for now it's fine, except that all the components are re-rendered (seen in the various console.log)
Here's the code :
App
import React, { useState} from "react"
import Menu from "./menu";
function App() {
const data = ["MenuItem1", "MenuItem2", "MenuItem3", "MenuItem4", "MenuItem5", "MenuItem6"]
const [selectedItem, setMenuItem] = useState(null)
const handleMenuItem = (menuItem) => {
setMenuItem(menuItem)
}
return (
<div className="App">
<Menu items = {data} handleMenuItem = {handleMenuItem}></Menu>
<div>{selectedItem}</div>
</div>
);
}
export default App;
Menu
import React from "react";
import MenuItem from "./menuItem";
const Menu = (props) => {
return (
<>
{props.items.map((item, index) => {
return <MenuItem key = {index} handleMenuItem = {props.handleMenuItem} value = {item}></MenuItem>
})
}
{console.log("menuItem")}
</>
)
};
export default React.memo(Menu);
MenuItem
import React from "react";
const MenuItem = (props) => {
return (
<>
<div onClick={() => props.handleMenuItem(props.value)}>
<p>{props.value}</p>
</div>
{console.log("render du MenuItem")}
</>
)
};
export default React.memo(MenuItem);
as you might see, I've used the React.memo in the end of MenuItem but it does not work, as well as the PureComponent
If someone has an idea, that'd be great to have some advice.
Have a great day
Wrap your handleMenuItem function with useCallback to avoid rerendering when the function changes. This will create a single function reference that will be used in the MenuItem as props and will avoid rereading since it's the same function instance always.
I have used an empty dependency array in this case which is correct for your use case. If your function has any state references then they should be added to the array.
const handleMenuItem = useCallback((menuItem) => {
setMenuItem(menuItem);
}, []);
There's a lot to unpack here so let's get started.
The way hooks are designed to prevent re-rendering components unnecessarily is by making sure you use the same instance of any unchanged variables, most specifically for object, functions, and arrays. I say that because string, number, and boolean equality is simple 'abc' === 'abc' resolves to true, but [] === [] would be false, as those are two DIFFERENT empty arrays being compared, and equality in JS for objects and functions and arrays only returns true when the two sides being compared are the exact same item.
That said, react provides ways to cache values and only update them (by creating new instances) when they need to be updated (because their dependencies change). Let's start with your app.js
import React, {useState, useCallback} from "react"
import Menu from "./menu";
// move this out of the function so that a new copy isn't created every time
// the App component re-renders
const data = ["MenuItem1", "MenuItem2", "MenuItem3", "MenuItem4", "MenuItem5", "MenuItem6"]
function App() {
const [selectedItem, setMenuItem] = useState(null);
// cache this with useCallback. The second parameter (the dependency
// array) is an empty array because there are no items that, should they
// change, we should create a new copy. That is to say we should never
// need to make a new copy because we have no dependencies that could
// change. This will now be the same instance of the same function each
// re-render.
const handleMenuItem = useCallback((menuItem) => setMenuItem(menuItem), []);
return (
<div className="App">
<Menu items={data} handleMenuItem={handleMenuItem}></Menu>
<div>{selectedItem}</div>
</div>
);
}
export default App;
Previously, handleMenuItem was set to a new copy of that function every time the App component was re-rendered, and data was also set to a new array (with the same entries) on each re-render. This would cause the child component (Menu) to re-render each time App was re-rendered. We don't want that. We only want child components to re-render if ABSOLUTELY necessary.
Next is the Menu component. There are pretty much no changes here, although I would urge you not to put spaces around your = within your JSX (key={index} not key = {index}.
import React from "react";
import MenuItem from "./menuItem";
const Menu = (props) => {
return (
<>
{props.items.map((item, index) => {
return <MenuItem key={index} handleMenuItem={props.handleMenuItem} value={item}/>
})
}
{console.log("menuItem")}
</>
)
};
export default React.memo(Menu);
For MenuItem, let's cache that click handler.
import React from "react";
const MenuItem = (props) => {
// cache this function
const handleClick = useCallback(() => props.handleMenuItem(props.value), [props.value]);
return (
<>
<div onClick={handleClick}>
<p>{props.value}</p>
</div>
{console.log("render du MenuItem")}
</>
)
};
export default React.memo(MenuItem);

React - Passing callbacks from React Context consumers to providers

I have the following context
import React, { createContext, useRef } from "react";
const ExampleContext = createContext(null);
export default ExampleContext;
export function ExampleProvider({ children }) {
const myMethod = () => {
};
return (
<ExampleContext.Provider
value={{
myMethod,
}}
>
{children}
<SomeCustomComponent
/* callback={callbackPassedFromConsumer} */
/>
</ExampleContext.Provider>
);
}
As you can see, it renders a custom component which receive a method as prop. This method is defined in a specific screen, which consumes this context.
How can I pass it from the screen to the provider?
This is how I consume the context (with a HOC):
import React from "react";
import ExampleContext from "../../../contexts/ExampleContext";
const withExample = (Component) => (props) =>
(
<ExampleContext.Consumer>
{(example) => (
<Component {...props} example={example} />
)}
</ExampleContext.Consumer>
);
export default withExample;
And this is the screen where I have the method which I need to pass to the context provider
function MyScreen({example}) {
const [data, setData] = useState([]);
const myMethodThatINeedToPass = () => {
...
setData([]);
...
}
return (<View>
...
</View>);
}
export default withExample(MyScreen);
Update:
I am trying to do this because in my real provider I have a BottomSheet component which renders two buttons "Delete" and "Report". This component is reusable, so, in order to avoid repeating myself, I am using a context provider.
See: https://github.com/gorhom/react-native-bottom-sheet/issues/259
Then, as the bottom sheet component which is rendered in the provider can receive optional props "onReportButtonPress" or "onDeleteButtonPress", I need a way to pass the method which manipulates my stateful data inside the screen (the consumer) to the provider.
You can't, in React the data only flows down.
This is commonly called a “top-down” or “unidirectional” data flow. Any state is always owned by some specific component, and any data or UI derived from that state can only affect components “below” them in the tree.
Your callbacks ("onReportButtonPress", "onDeleteButtonPress") must be available at provider's scope.
<ExampleContext.Provider
value={{
onReportButtonPress,
onDeleteButtonPress,
}}
>
{children}
</ExampleContext.Provider>;
Render SomeCustomComponent in Consumer component. This is the React way of doing things :)

Render array of objects from Redux store as React components

I'm trying to display my initial state from my store. I know my code is not correct but i'm hoping to learn the most simple method for this. Can I simply receive data from the store with props? or do I need some lifecycle event to target data in the store?
Here is my current attempt: I have edited this to include my reducer and I have updated my component as per the comments below.
//store .js
import { createStore, applyMiddleware,compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers'; //the index.js file
const initialState = {
}
const middleware = [thunk]
const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__&& window.__REDUX_DEVTOOLS_EXTENSION__()
)
);
export default store;
Now when trying to map out my props in the below component, I get the error:
this.props.properties.map is not a function
//Properties component that renders a map of my props
import React, { Component } from 'react'
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {getListings} from '../actions/postActions';
class Properties extends Component {
componentDidMount(){
getListings()
}
render() {
const properties = this.props.properties.map(property => (
<div key = {property.id}>
<h3>{property.propertyName}</h3>
<div style={{width: '4em', height:'4em', backgroundColor: 'blue'}}>image</div>
<p>{property.footage}</p>
<p>{property.address}</p>
<p>{property.price}</p>
</div>
))
return (
<div>
<h1>Current Listings</h1>
{properties}
</div>
)
}
}
Properties.propTypes = {
properties: PropTypes.array.isRequired,
newListing: PropTypes.object
}
const mapStateToProps = state =>({
properties: state.properties.properties,
newListing: state.properties.newListing
});
export default connect(mapStateToProps)(Properties)
//my actionsHandler.js
import {GET_LISTINGS, NEW_LISTING} from './types';
export function newProperty(formdata){
return function(dispatch){
dispatch({
type: NEW_LISTING,
payload: formdata
})
}
}
export function getListings(form){
return function(dispatch){
dispatch({
type: GET_LISTINGS,
})
}
}
//my reducer
import {NEW_LISTING, GET_LISTINGS} from '../actions/types';
const initialState={
properties: [
{
id: 1,
propertyName: 'The boogaloo',
footage: '3500 sqft',
address: '123 hill bounty rd',
price:'$ 350,000.00'
}
],
newListing: {}
}
export default function(state=initialState, action){
switch(action.type){
case NEW_LISTING:
return{
...state,
properties:state.properties.push(action.payload)
}
case GET_LISTINGS:
return{
state
}
default:
return state
}
}
use a lifecycle method for updating the component
like shouldComponentUpdate method,
props change Doesn't cause rerender only state change cause rerender,
is such case store the props as state is also a solution (inappropriate)
Your general approach is viable in general, with certain corrections required though:
your state is expected to be an object (not an array as it is currently)
in order for your entire array of objects to appear as a bunch of React elements, you may want to wrap them into some parent component and map() your individual posts as small components that receive corresponding object as props from parent component
You may inquiry the following live-demo as a reference:
//dependencies
const { render } = ReactDOM,
{ createStore } = Redux,
{ connect, Provider } = ReactRedux
//initial state, doomy reducer and basic store (no middleware)
const initialState = {posts:[{id:'0',propertyName:'house',footage:'1800 sqft',address:'108 boogaloo dr. thisplace, CA',price:'$145,300.00'},{id:'1',propertyName:'closet',footage:'110 sqft',address:'23 dirt dr. badplace, NC',price:'$3'},{id:'2',propertyName:'garage',footage:'1000 sqft',address:'10 meadow st. decent, NY',price:'$100,000.00'},{id:'3',propertyName:'fishing shack',footage:'500 sqft',address:'108 fishy dr. forestgrove, NJ',price:'$200,300.00'},{id:'4',propertyName:'huge mansion',footage:'8000 sqft',address:'32 T-Pain st. onlytwentythree, WI',price:'$100.00'},{id:'5',propertyName:'dog house',footage:'200 sqft',address:'2367 goodboy dr. borks, KA',price:'$1000.00'},{id:'6',propertyName:'beehive',footage:'too big to measure',address:'108 stingya dr. Bidibi, NJ',price:'$300.00'},{id:'7',propertyName:'my house',footage:'4000 sqft',address:'42 treeface dr. bigwoods, FL',price:'$190,300.00'},]},
appReducer = (state=initialState, action) => state,
store = createStore(appReducer)
//post ui component
const Post = ({propertyName, footage, address, price}) => (
<div>
<h3>{propertyName}</h3>
<p>footage: {footage}</p>
<p>address: {address}</p>
<p>price: {price}</p>
</div>
)
//wrapper ui component relying on storedPosts property
//that will get connected to global state posts later on
const PostBoard = ({storedPosts}) => (
<div>
{storedPosts.map((post, key) => <Post key={key} {...post} />)}
</div>
)
//connect storedPosts to global state posts
const mapStateToProps = ({posts}) => ({storedPosts: posts}),
PostBoardComponent = connect(mapStateToProps)(PostBoard)
//render Redux Provider
render (
<Provider store={store}>
<PostBoardComponent />
</Provider>,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.5/redux.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.1.3/react-redux.min.js"></script><div id="root"></div>
In redux, you will be defining an initial state in reducer and will do some updation or removal of the data from the state using actions and will store i.e. you are updating the object the initial state object will be updated..In few cases you need the initial state directly. So,you need to make a copy of initial state object and perform the tasks on it. When the new object updated the initial state will not have any effect.
You can update using components by directly dispatching an action from the component i.e. mapdispatchtoProps.
You can access the entire store data in component by connecting it i.e. connect(mapStatetoProps, mapDispacthtoProps).
mapSatettoProps will give you store data as state. You can modify it or you can do whatever you want. state access can be done in component also but you will get updated one.

Understanding React Higher-Order Components

Can someone please explain Higher-order components in React. I have read and re-read the documentation but cannot seem to get a better understanding. According to the documentation, HOCs help remove duplication by creating a primary function that returns a react component, by passing arguments to that function.
I have a few questions on that.
If HOCs create a new enhanced component, can it be possible not to pass in any component as argument at all?
In an example such as this, which is the higher order component, the Button or the EnhancedButton.
I tried creating one HOC like this:
// createSetup.js
import React from 'react';
export default function createSetup(options) {
return class extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.testFunction = this.testFunction.bind(this);
}
testFunction() {
console.log("This is a test function");
}
render() {
return <p>{options.name}</p>
}
}
}
// main.js
import React from 'react';
import {render} from 'react-dom';
import createSetup from './createSetup';
render((<div>{() => createSetup({name: 'name'})}</div>),
document.getElementById('root'););
Running this does not show the HOC, only the div
Can anyone help out with a better example than the ones given?
A HOC is a function that takes a Component as one of its parameters and enhances that component in some way.
If HOCs create a new enhanced component, can it be possible not to pass in any component as argument at all?
Nope, then it wouldn't be a HOC, because one of the conditions is that they take a component as one of the arguments and they return a new Component that has some added functionality.
In an example such as this, which is the higher order component, the Button or the EnhancedButton.
EnhanceButton is the HOC and FinalButton is the enhanced component.
I tried creating one HOC like this: ... Running this does not show the HOC, only the div
That's because your createSetup function is not a HOC... It's a function that returns a component, yes, but it does not take a component as an argument in order to enhance it.
Let's see an example of a basic HOC:
const renderWhen = (condition, Component) =>
props => condition(props)
? <Component {...props} />
: null
);
And you could use it like this:
const EnhancedLink = renderWhen(({invisible}) => !invisible, 'a');
Now your EnhancedLink will be like a a component but if you pass the property invisible set to true it won't render... So we have enhanced the default behaviour of the a component and you could do that with any other component.
In many cases HOC functions are curried and the Component arg goes last... Like this:
const renderWhen = condition => Component =>
props => condition(props)
? <Component {...props} />
: null
);
Like the connect function of react-redux... That makes composition easier. Have a look at recompose.
In short, If you assume functions are analogues to Components, Closure is analogous to HOC.
Try your createSetup.js with:
const createSetup = options => <p>{options.name}</p>;
and your main.js
const comp = createSetup({ name: 'name' });
render((<div>{comp}</div>),
document.getElementById('root'));
A higher-order component (HOC) is an advanced technique in React for reusing component logic. Concretely, a higher-order component is a function that takes a component and returns a new component.
A HOC is a pure function with zero side-effects.
Example: CONDITIONALLY RENDER COMPONENTS
Suppose we have a component that needs to be rendered only when a user is authenticated — it is a protected component. We can create a HOC named WithAuth() to wrap that protected component, and then do a check in the HOC that will render only that particular component if the user has been authenticated.
A basic withAuth() HOC, according to the example above, can be written as follows:
// withAuth.js
import React from "react";
export function withAuth(Component) {
return class AuthenticatedComponent extends React.Component {
isAuthenticated() {
return this.props.isAuthenticated;
}
/**
* Render
*/
render() {
const loginErrorMessage = (
<div>
Please login in order to view this part of the application.
</div>
);
return (
<div>
{ this.isAuthenticated === true ? <Component {...this.props} /> : loginErrorMessage }
</div>
);
}
};
}
export default withAuth;
The code above is a HOC named withAuth. It basically takes a component and returns a new component, named AuthenticatedComponent, that checks whether the user is authenticated. If the user is not authenticated, it returns the loginErrorMessage component; if the user is authenticated, it returns the wrapped component.
Note: this.props.isAuthenticated has to be set from your application’s
logic. (Or else use react-redux to retrieve it from the global state.)
To make use of our HOC in a protected component, we’d use it like so:
// MyProtectedComponent.js
import React from "react";
import {withAuth} from "./withAuth.js";
export class MyProectedComponent extends React.Component {
/**
* Render
*/
render() {
return (
<div>
This is only viewable by authenticated users.
</div>
);
}
}
// Now wrap MyPrivateComponent with the requireAuthentication function
export default withAuth(MyPrivateComponent);
Here, we create a component that is viewable only by users who are authenticated. We wrap that component in our withAuth HOC to protect the component from users who are not authenticated.
Source
// HIGHER ORDER COMPOENTS IN REACT
// Higher order components are JavaScript functions used for adding
// additional functionalities to the existing component.
// file 1: hoc.js (will write our higher order component logic) -- code start -->
const messageCheckHOC = (OriginalComponent) => {
// OriginalComponent is component passed to HOC
const NewComponent = (props) => {
// business logic of HOC
if (!props.isAllowedToView) {
return <b> Not Allowed To View The MSG </b>;
}
// here we can pass the props to component
return <OriginalComponent {...props} />;
};
// returning new Component with updated Props and UI
return NewComponent;
};
export default messageCheckHOC;
// file 1: hoc.js -- code end -->
// file 2: message.js -- code start -->
// this is the basic component we are wrapping with HOC
// to check the permission isAllowedToView msg if not display fallback UI
import messageCheckHOC from "./hoc";
const MSG = ({ name, msg }) => {
return (
<h3>
{name} - {msg}
</h3>
);
};
export default messageCheckHOC(MSG);
// file 2: message.js -- code end -->
// file 3 : App.js -- code start --->
import MSG from "./message.js";
export default function App() {
return (
<div className="App">
<h3>HOC COMPONENTS </h3>
<MSG name="Mac" msg="Heyy !!! " isAllowedToView={true} />
<MSG name="Robin" msg="Hello ! " isAllowedToView={true} />
<MSG name="Eyann" msg="How are you" isAllowedToView={false} />
</div>
);
}
// file 3 : App.js -- code end --->

Categories

Resources