I've made this react app in which there's a Parent component as App.js and it has three child components.The first child component has some states which are being changed by the other two child components present in parent component.
So first I'm passing the states in parent component and then passing them into other two child components as props. Here's the piece of my code.
Here's my App.js
function App() {
var firstvalueoftextarea,
setfirstvalueoftextarea,
secondvalueoftextarea,
setsecondvalueoftextarea;
var PanelOfResult = function(
firstvalueoftextarea,
setfirstvalueoftextarea,
secondvalueoftextarea,
setsecondvalueoftextarea
) {
firstvalueoftextarea = firstvalueoftextarea;
setfirstvalueoftextarea = setfirstvalueoftextarea;
secondvalueoftextarea = secondvalueoftextarea;
setsecondvalueoftextarea = setsecondvalueoftextarea;
};
return (
console.log("Rendering of component"),
(
<div className="App">
<ResultPanel PanelOfResult={PanelOfResult} />
<SecondChildComponent
valueoftextarea={firstvalueoftextarea}
setvalueoftextarea={setfirstvalueoftextarea}
/>
<ThirdChildComponent
className="Symbols"
firstvalueoftextarea={firstvalueoftextarea}
setfirstvalueoftextarea={setfirstvalueoftextarea}
secondvalueoftextarea={secondvalueoftextarea}
setsecondvalueoftextarea={setsecondvalueoftextarea}
/>
</div>
)
);
}
export default App;
Here's my ResultPanel.js
import React from "react";
function ResultPanel(props) {
const [firstvalueoftextarea, setfirstvalueoftextarea] = React.useState(
"Hello World"
);
const [secondvalueoftextarea, setsecondvalueoftextarea] = React.useState("");
props.PanelOfResult(
firstvalueoftextarea,
setfirstvalueoftextarea,
secondvalueoftextarea,
setsecondvalueoftextarea
);
return (
<div>
<h1>{firstvalueoftextarea}</h1>
</div>
);
}
export default ResultPanel;
The states of the ResultPanel Component can be changed by SecondChildComponent and ThirdChildComponent and those component code is working fine.
When SecondChildComponent tries to change the state of ResultPanel I'm getting the error- TypeError: this.props.setvalueoftextarea is not a function
Can anyone help where I'm mistaking? I'm not certain where to call PanelOfResult callback method in ResultPanel component.
There's a lot of issues here, one of the biggest being how you set your variables for state.
First, move your state declarations to the parent component, and delete your vars. The whole PanelOfResult is unnecessary and a bad pattern of doing things.
function App() {
const [firstvalueoftextarea, setfirstvalueoftextarea] = React.useState(
"Hello World"
);
const [secondvalueoftextarea, setsecondvalueoftextarea] = React.useState("");
Then, instead of passing the callback, pass the needed values:
<ResultPanel firstvalueoftextarea={firstvalueoftextarea} />
This will stop your error about not being a function, and eliminate the need for a callback.
In general you should try not to use var, but let or const.
Also, try naming variables better. Either camel case or underscore would make your code much more readable.
Related
I am adding React to an already existing front end and am unsure how to communicate data between components.
I have a basic text Input component and a Span component, mounted separately. When the user types into the input, I want the text of the span to change to what is input.
Previously I would start a React project from scratch and so have the Input and Span share an App component as a parent. I'd use a prop function to lift the text state from the Input to the App and pass it down the value to the Span as a prop. But from scratch is not an option here.
I've considered:
Redux etc. As I'm introducing React piece by piece to this project and some team members have no React experience, I want to avoid using Redux or other state management libraries until very necessary, and it seems overkill for this simple case.
React Context API. This doesn't seem correct either, as my understanding was that context API should be kept for global data like "current authenticated user, theme, or preferred language" shared over many components, not just for sharing state between 2 components.
UseEffect hook. Using this hook to set the inner HTML of the Span component i.e
function Input() {
const inputProps = useInput("");
useEffect(() => {
document.getElementsByClassName('page-title')[0].innerHTML = inputProps.value;
})
return (
<div>
<h3>Name this page</h3>
<input
placeholder="Type here"
{...inputProps}
/>
</div>
);
}
Which sort of negates the whole point of using React for the Span?
I've gone with the UseEffect hook for now but haven't found any clear answers in the React docs or elsewhere online so any advice would be helpful.
Thanks.
Input.jsx
import React, { useState, useEffect } from 'react';
function useInput(defaultValue) {
const [value, setValue] = useState(defaultValue);
function onChange(e) {
setValue(e.target.value);
}
return {
value,
onChange
}
}
function Input() {
const inputProps = useInput("");
useEffect(() => {
document.getElementsByClassName('page-title')[0].innerHTML = inputProps.value;
})
return (
<div>
<h3>React asks what shall we name this product?</h3>
<input
placeholder="Type here"
{...inputProps}
/>
</div>
);
}
export default Input;
PageTitle.jsx
import React from 'react';
function PageTitle(props) {
var title = "Welcome!"
return (
<span>{props.title}</span>
)
}
;
export default PageTitle
Index.js
// Imports
const Main = () => (
<Input />
);
ReactDOM.render(
<Main />,
document.getElementById('react-app')
);
ReactDOM.render(
<PageTitle title="Welcome"/>,
document.getElementsByClassName('page-title')[0]
);
In React, data is supposed to flow in only one direction, from parent component to child component. Without getting into context/redux, this means keeping common state in a common ancestor of the components that need it and passing it down through props.
Your useEffect() idea isn't horrible as a kind of ad hoc solution, but I would not make PageTitle a react component, because setting the value imperatively from another component really breaks the react model.
I've used useEffect() to set things on elements that aren't in react, like the document title and body classes, as in the following code:
const siteVersion = /*value from somewhere else*/;
//...
useEffect(() => {
//put a class on body that identifies the site version
const $ = window.jQuery;
if(siteVersion && !$('body').hasClass(`site-version-${siteVersion}`)) {
$('body').addClass(`site-version-${siteVersion}`);
}
document.title = `Current Site: ${siteVersion}`;
}, [siteVersion]);
In your case, you can treat the span in a similar way, as something outside the scope of react.
Note that the second argument to useEffect() is a list of dependencies, so that useEffect() only runs whenever one or more changes.
Another side issue is that you need to guard against XSS (cross site scripting) attacks in code like this:
//setting innerHTML to an unencoded user value is dangerous
document.getElementsByClassName('page-title')[0].innerHTML = inputProps.value;
Edit:
If you want to be even more tidy and react-y, you could pass a function to your input component that sets the PageTitle:
const setPageTitle = (newTitle) => {
//TODO: fix XSS problem
document.getElementsByClassName('page-title')[0].innerHTML = newTitle;
};
ReactDOM.render(
<Main setPageTitle={setPageTitle} />,
document.getElementById('react-app')
);
//inside Main:
function Input({setPageTitle}) {
const inputProps = useInput("");
useEffect(() => {
setPageTitle(inputProps.value);
})
return (
<div>
<h3>React asks what shall we name this product?</h3>
<input
placeholder="Type here"
{...inputProps}
/>
</div>
);
}
You can create a HOC or use useContext hook instead
I want to have a global object that is available to my app where I can retrieve the value anywhere and also set a new value anywhere. Currently I have only used Context for values that are related to state i.e something needs to render again when the value changes. For example:
import React from 'react';
const TokenContext = React.createContext({
token: null,
setToken: () => {}
});
export default TokenContext;
import React, { useState } from 'react';
import './App.css';
import Title from './Title';
import TokenContext from './TokenContext';
function App() {
const [token, setToken] = useState(null);
return(
<TokenContext.Provider value={{ token, setToken }}>
<Title />
</TokenContext.Provider>
);
}
export default App;
How would I approach this if I just want to store a JS object in context (not a state) and also change the value anywhere?
The global context concept in React world was born to resolve problem with passing down props via multiple component layer. And when working with React, we want to re-render whenever "data source" changes. One way data binding in React makes this flow easier to code, debug and maintain as well.
So what is your specific purpose of store a global object and for nothing happen when that object got changes? If nothing re-render whenever it changes, so what is the main use of it?
Prevent re-render in React has multiple ways like useEffect or old shouldComponentUpdate method. I think they can help if your main idea is just prevent re-render in some very specific cases.
Use it as state management libraries like Redux.
You have a global object (store) and you query the value through context, but you also need to add forceUpdate() because mutating the object won't trigger a render as its not part of React API:
const globalObject = { counter: 0 };
const Context = React.createContext(globalObject);
const Consumer = () => {
const [, render] = useReducer(p => !p, false);
const store = useContext(Context);
const onClick = () => {
store.counter = store.counter + 1;
render();
};
return (
<>
<button onClick={onClick}>Render</button>
<div>{globalObject.counter}</div>
</>
);
};
const App = () => {
return (
<Context.Provider value={globalObject}>
<Consumer />
</Context.Provider>
);
};
While refactoring some code at work, I ran into a circular dependency with nested React components. The feature was basically "Use a switch to dynamically render components nested in other components, including themselves".
I solved the problem by creating a component "registry" with two methods, registerBlock and an HOC BlockRegistry.
src/BlocksNode.js
// src/BlocksNode.js - accepts and renders a block passed from the API
import BlockRegistry, { registerBlock } from './BlockRegister'
import ComponentOne from './ComponentOne'
import ComponentTwo from './ComponentTwo'
// accepts a name and a component
registerBlock('componentOne', ComponentOne)
registerBlock('componentTwo', ComponentTwo)
// reads a block from the API and uses the type passed from props
const BlocksNode = (props) => {
const { type, blocks } = props
return <BlockRegistry type={type} blocks={blocks} />
}
export default BlocksNode
src/BlockRegister.js
const components = {}
export function registerBlock(name, Component) {
components[name] = Component
}
const BlockRegistry = (props) => {
const { type, ...rest } = props
const Component = components[type]
return <Component {...rest} />
}
export default BlockRegistry
src/ComponentOne.js
import BlockRegistry from './BlockRegister'
function ComponentOne(props) {
const { blocks } = props
return (
<div>
{blocks.map((block) => {
const { type, blocks } = block
return <BlockRegistry type={type} blocks={blocks} />
})}
</div>
)
}
export default ComponentOne
ComponentOne.js can pass any number of other blocks, including ComponentTwo.js or itself. I've simplified some of the logic, but the gist is there.
This solution works great. But I don't understand how it's able to function. Logically I'd expect the nested component's scope to not include the top-level, registered components. How is scope being handled so that nested components are working without new, nested calls to registerBlock()?
For instance, how does <BlockRegistry /> in ComponentOne.js find a match for block.type === 'componentTwo'? I'd expect to need to re-register it, ie. by doing registerBlock('componentTwo', ComponentTwo) inside ComponentOne.js. The fact that it works without the call seems strange.
I have a Table component that I want ref to be attached to.
Use: Table.js
class Table extends React.Component {
constructor(props) {
super(props);
this.state = {
rows: 1,
dataLength: props.dataLength,
}
this.tableRef = React.createRef();
}
componentDidUpdate() {
//using ref
this.tableRef.current ..... //logic using ref
this.state.rows ..... //some logic
}
render() {
<TableContainer ref={this.tableRef} />
<CustomPagination />
}
}
This works fine, but now my requirement has changed, and I want to reuse the Table component with pagination applied to all the Tables in my App. I have decided to make a HOC withCustomPagination.
Use: withCustomPagination.js HOC
import CustomPagination from 'path/to/file';
const withCustomPagination = tableRef => Component => {
return class WithCustomPagination extends React.Component {
constructor(props) {
super(props);
this.state = {
rows: 1,
dataLength: props.dataLength,
}
}
componentDidUpdate() {
tableRef.current.state ..... //logic using ref, Error for this line
this.state.rows ..... //some logic
}
render() {
return (
<Component {...state} />
<CustomPagination />
)
}
}
}
export default withCustomPagination;
New Table.js:
import withCustomPagination from '/path/to/file';
const ref = React.createRef();
const Table = props => (
<TableContainer ref={ref} />
);
const WrappedTable = withCustomPagination(ref)(Table);
HOC withCustomPagination returns a class WithCustomPagination that has a componentDidUpdate lifecycle method that uses Table ref in the logic. So I try to pass ref created in Table.js as argument to withCustomPagination, i.e curried with ref and Table stateless component.
This use of ref is wrong and I get error: TypeError: Cannot read property 'state' of null.
I tried using Forwarding Refs, but was unable to implement it.
How do I pass the Table ref to withCustomPagination and be able to use it in HOC?
In this case you can use useImperativeHandle
It means you have to forward ref and specify which function or object or,...
you want to share with ref inside your functional component.
Here is my Hoc example :
import React from 'react';
import { View } from 'react-native';
export function CommonHoc(WrappedComponent) {
const component = class extends React.Component {
componentDidMount() {
this.refs.myComponent.showAlert();
}
render() {
return (
<>
<WrappedComponent
ref='myComponent'
{...this.state}
{...this.props}
/>
</>
);
}
};
return component;
}
and it's my stateless component
const HomeController=(props,ref)=> {
useImperativeHandle(ref, () => ({
showAlert() {
alert("called");
},
}));
return (
<Text>home</Text>
);
};
export default CommonHoc(forwardRef(HomeController));
Either restructure your code to not use a HOC for this or try using React.forwardRef:
Refs Aren’t Passed Through
While the convention for higher-order components is to pass through
all props to the wrapped component, this does not work for refs.
That’s because ref is not really a prop — like key, it’s handled
specially by React. If you add a ref to an element whose component is
the result of a HOC, the ref refers to an instance of the outermost
container component, not the wrapped component.
The solution for this problem is to use the React.forwardRef API
(introduced with React 16.3). Learn more about it in the forwarding
refs section.
via Higher-Order Components: Refs Aren’t Passed Through
In the forwarding refs section there are code examples you could use to pass refs down, but trying to yank them up will fail in your case with:
Warning: Stateless function components cannot be given refs. Attempts to access this ref will fail.
In a project we took a different approach. There's an EnhancedTable component that handles all of the pagination logic and in itself has the dumb table component and the pagination component. It works pretty well but this means you would have to drill props (or use a store lib like Redux or Mobx) and add new ones that will handle pagination options. This will result in some refactoring of Table uses and you'll have to be more explicit but I would take it as a boon rather than a hindrance.
I was able to solve a simmilar issue that brought me to this thread without using forwardRef or useImperativeHandle.
By creating the ref at a higher level, and passign it down into the component and sub components that I needed to act on with the ref.
/** Parent Component has access to ref and functions that act on ref **/
import { useRef } from 'react';
const formRef = useRef(); // ref will have dom elements need accessing
const onClickFunction=()=>{ //sample function acts on ref
var inputs = formRef.current.querySelectorAll('input')
/* Act on ref here via onClick function, etc has access to dom elements
in child component and childs child components */
};
return(
<ComponentGetsAttachedRef formRef={formRef} />
//^ref sent down to component and its children
<ComponentNeedingRef onClickFunction={onClickFunction}/>
//^function with access to ref sent down to component
)
/** Child component needs to act on ref**/
export const ComponentNeedingRef = ({ onClickFunction}) =>{
return(
<button onClick={onClickFunction}>
)
}
/* Child component recieves ref and passes it down */
export const ComponentGetsAttachedRef = ({ formRef}) =>{
//ref comes in as prop gets attached to props or utilized internally
return (
<ChildsChildComponent formRef={formRef}/> //sub component passed ref down
)
}
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 --->