How to pass particular property to all child components in react? - reactjs

I am absolute beginner in react. I have to pass particular property to all the components inside the div without passing them into individual,
For Example, Instead of doing this:
function App() {
return (
<div>
<Component1 props = {propObject}/>
<Component2 props = {propObject}/>
<Component3 props = {propObject}/>
</div>
);
}
How can I achieve something like this? :
function App() {
return (
<div props={propObject}>
<Component1 />
<Component2 />
<Component3 />
</div>
);
}

React Context
provides a way to share values to child components without having to explicitly pass a prop through every level of the tree.
You need to import React.context from react
syntax:
// context.js
import React, { createContext } from "react"
export const MyContext = React.createContext(null);
Now You need to create a provider of this context, to pass the current context value to the tree below. So you need to wrap all of your child component by the React.Provider.
syntax:
// MainComponent.js
import { MyContext } from "./context"
import { ChildComponent1, ChildComponent2, ChildComponent3 } from "./childcomponent"
const MainComponent = () => {
const myObject = {name: "John"}
return (
<MyContext.Provider props={myObject}>
<ChildComponent1 />
<ChildComponent2 />
<ChildComponent3 />
</MyContext.Provider>
)
}
Now all the child component of this MainComponent has access of this props value which is provided by MyContext.Provider. Now you can simply get that values to your child components.
syntax:
// childcomponent.js
import React, { useContext } from "react"
import { MyContext } from "./context"
export const ChilcComponent1 = () => {
const props = useContext(MyContext)
return (
<p>
My name is: {props?.key}
</p>
)
}

You can use from ThemeProvider:
function App() {
return (
<ThemeProvider props = {propObject}>
<Component1 />
<Component2 />
<Component3 />
</ThemeProvider >
);
}

Related

useContext return getState undefined in component children

I have a problem accessing the context within a child component, which is returning me undefined
router.js
class Home extends Component {
render() {
let { url } = this.props.match
return (
<div className='container-main'>
<Menu baseUrl={url} />
<Switch>
<Route exact path={`${url}/tasksVision`} component={Home} />
</Switch>
</div>
)
}
}
export default Home
index.js
import React from 'react';
import { PedidosProvider } from '../../../context/components/pedidos-pedidosVolume';
import Modal from './modal';
const Pedido = (props) => (
<PedidosProvider {...props}>
<Modal />
</PedidosProvider>
)
export default Pedido;
modal.js
this const "test" does works when called on that component
const Modal = () => {
const {
test
} = useContext(PedidosContext)
return (
<div>
{test} //this is working
<Orders/>
</div>
)
}
orders.js
this const "test" does not work when called on that component
import React, { useContext } from 'react'
import { PedidosContext } from '../../../context/components/pedidos-pedidosVolume'
const Teste = () => {
const { test } = useContext(PedidosContext) // this return: Cannot read property 'test' of undefined
return (
<h1>{test}</h1>
)
}
export default Teste
I haven't worked with the context API a lot but I think you are getting an error because you are destructuring the PedidosContext.
const { test } = useContext(PedidosContext)
should be
const test = useContext(PedidosContext)
Altough it can be that my answer is not what you are looking for. However, in the docs it is used the same way as I just described.

React complains element type is invalid when trying to use context

I'm trying to use React Context to update navbar title dynamically from other child components. I created NavbarContext.js as follows. I have wrapped AdminLayout with NavContext.Provider and use useContext in Course.js to dynamically update navbar title inside useEffect. However, when I'm doing this, react throws the following error on the screen.
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
How can I use context properly so that I can update Header title from Course.js inside its useEffect?
NavbarContext.js
import React, {useState} from 'react'
export default () => {
const [name,setName] = useState("")
const NavContext = React.createContext({
name: "",
changeName: name => setName(name)
})
const NavProvider = NavContext.Provider
const NavConsumer = NavContext.Consumer
return NavContext
}
AdminLayout.js
<NavContext.Provider>
<div className={classes.wrapper}>
<Sidebar
routes={routes}
logoText={"Widubima"}
logo={logo}
image={image}
handleDrawerToggle={handleDrawerToggle}
open={mobileOpen}
color={color}
{...rest}
/>
<div className={classes.mainPanel} ref={mainPanel}>
<Navbar
routes={routes}
handleDrawerToggle={handleDrawerToggle}
{...rest}
/>
{/* On the /maps route we want the map to be on full screen - this is not possible if the content and conatiner classes are present because they have some paddings which would make the map smaller */}
{getRoute() ? (
<div className={classes.content}>
<div className={classes.container}>{switchRoutes}</div>
</div>
) : (
<div className={classes.map}>{switchRoutes}</div>
)}
</div>
</div>
</NavContext.Provider>
Navbar.js
import NavContext from "context/NavbarContext"
export default function Header(props) {
function makeBrand() {
var name;
props.routes.map(prop => {
if (window.location.href.indexOf(prop.layout + prop.path) !== -1) {
name = prop.name;
document.title = name;
}
return null;
});
return name;
}
return (
<AppBar className={classes.appBar + appBarClasses}>
<Toolbar className={classes.container}>
<div className={classes.flex}>
{/* Here we create navbar brand, based on route name */}
<NavContext.Consumer>
{({ name, setName }) => (
<Button
color="transparent"
href="#"
className={classes.title}
style={{ fontSize: "1.5em", marginLeft: "-2%" }}
>
{makeBrand() || name}
</Button>
)}
</NavContext.Consumer>
</Toolbar>
</AppBar>
);
}
Course.js
import React, { useState, useEffect, useContext } from "react";
import NavContext from "context/NavbarContext"
const AdminCourse = props => {
const context = useContext(NavContext);
useEffect(() => {
Axios.get('/courses/'+props.match.params.courseId).then(
res => {
context.changeName("hello")
}
).catch(err => {
console.log(err)
})
return () => {
setCourseId("");
};
});
return (
<GridContainer>
</GridContainer>
);
};
export default AdminCourse;
i think problem is there with your NavbarContext.js.
you are not exporting NavContext also.
you are defining provider, consumer but you are not using them either.
here's how you can solve your problem.
first create context and it's provider in a file as following.
NavContext.js
import React, { useState } from "react";
const NavContext = React.createContext();
const NavProvider = props => {
const [name, setName] = useState("");
let hookObject = {
name: name,
changeName: setName
};
return (
<NavContext.Provider value={hookObject}>
{props.children}
</NavContext.Provider>
);
};
export { NavProvider, NavContext };
in above code first i am creating context with empty value.
the i am creating NavProvider which actually contains value name as a state hook inside it.hookObject exposes state as per your naming conventions in code.
now i for testing purpose i defined two consumers.
one is where we update name in useEffect, that is ,
ConsumerThatUpdates.js
import React, { useContext, useEffect } from "react";
import { NavContext } from "./NavContext";
const ConsumerThatUpdates = () => {
const { changeName } = useContext(NavContext);
useEffect(() => {
changeName("NEW NAME");
}, [changeName]);
return <div>i update on my useeffect</div>;
};
export default ConsumerThatUpdates;
you can update useEffect as per your needs.
another is where we use the name,
ConsumerThatDisplays.js
import React, { useContext } from "react";
import { NavContext } from "./NavContext";
const ConsumerThatDisplays = () => {
const { name } = useContext(NavContext);
return <div>{name}</div>;
};
export default ConsumerThatDisplays;
and finally my App.js looks like this,
App.js
import React from "react";
import "./styles.css";
import { NavProvider } from "./NavContext";
import ConsumerThatDisplays from "./ConsumerThatDisplays";
import ConsumerThatUpdates from "./ConsumerThatUpdates";
export default function App() {
return (
<div className="App">
<NavProvider>
<ConsumerThatDisplays />
<ConsumerThatUpdates />
</NavProvider>
</div>
);
}
hope this helps!!
if you want to know more about how to use context effectively, i recooHow to use React Context effectively

Pass input data from component A to component B using React context

My request is very simple:
Could you please provide me with an example where an input data is passed from component A to component B using context API.
Requirements: there should be an input value entered in component A. We send the input value over to component B using context.
A and B are sibling components.
You can do this way.
Make a function in Context.js state which set the state of your input field.
//context.js state
state = {
inputFieldName: null,
setInputField: () => {
this.setState() //set value for inputFieldName here
}
}
Call that setInputField function on onChange in component A using Context and you can get inputFieldName state from Context in component B.
The required properties of the context:
1. A string property which stores the user's input
2. A method which updates the user's input.
And in your case the component A produces the input and calls the method to update it to context. The component B consumes the changes of the input from the context. it ends up with such a prototype.
import React, { useContext, useState } from "react";
import ReactDOM from "react-dom";
const MyContext = React.createContext(null);
function A() {
const { onChange } = useContext(MyContext);
const [input, setInput] = useState(null);
return (
<input
type="text"
value={input}
onChange={e => {
setInput(e.target.value);
onChange(e.target.value);
}}
/>
);
}
function B() {
const { input } = useContext(MyContext);
return <div>{input}</div>;
}
function App() {
const [input, setInput] = useState(null);
return (
<MyContext.Provider value={{ input, onChange: setInput }}>
<div>
<A />
<B />
</div>
</MyContext.Provider>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Here is my try, hope it will help you
componentA.jsx
import React from "react";
const ComponentA = props => {
return (
<div>
<h1>{`ComponentA: ${props.data}`}</h1>;
<button onClick={() => props.onValueChange("value changed by ComponentA")}>
click
</button>
</div>
);
};
export default ComponentA;
componentB.jsx
import React from "react";
const ComponentB = props => {
return <h1>{`ComponentB: ${props.data}`}</h1>;
};
export default ComponentB;
App.js
import React, { Component } from "react";
import ComponentB from "./componentA";
import ComponentA from "./componentB";
class App extends Component {
state = {
data: "common value from parent"
};
handleChange = input => {
this.setState({ data: input });
};
render() {
return (
<div>
<ComponentB data={this.state.data}></ComponentB>
<ComponentA
data={this.state.data}
onValueChange={this.handleChange}
></ComponentA>
</div>
)}
}

Pass context between siblings using context in React

I have the following code where I am trying to get a value from one component to its sibling using the context api.
import React from "react";
import ReactDOM from "react-dom";
function App() {
return (
<div>
<TheButton />
<Display />
</div>
);
}
export const NumberContext = React.createContext();
function TheButton() {
return (
<NumberContext.Provider value={"test"}>
<button>Click me</button>
</NumberContext.Provider>
);
}
function Display() {
const context = React.useContext(NumberContext);
return <div>The answer {context}.</div>;
}
ReactDOM.render(<App />, document.querySelector("#root"));
As you can see I am passing the 'test' value in the provider, but when the page renders all I see is "The answer ."
Here is a a codesandbox for the issue https://codesandbox.io/s/pedantic-forest-zjlc2
Despite the fact that context gives you a decoupled way of passing props the Provider still must be on a higher hierarchy. Provide your context from your App and consume it from children.
export const NumberContext = React.createContext();
function App() {
const [foo, changeFoo] = useState('foo')
return (
<NumberContext.Provider value={{ foo, changeFoo }}>
<TheButton />
<Display />
</NumberContext.Provider>
);
}
function TheButton() {
const { changeFoo } = useContext(NumberContext)
return (
<button onClick={() => changeFoo('bar')}>Click me</button>
);
}
function Display() {
const context = React.useContext(NumberContext);
return <div>The answer {context.foo}.</div>;
}

Can I pass ref with function component?

-I am using function component.
-for now I am using 3 components here, from that One is parent component and another 2 are child components.
-I need to access one child component methods or state to another child methods. I already done with class components with CreateRef but for now I need to use with function components but I am getting Null inside 'ref.current'.
export function SideBySideList(props) {
const ref = React.createRef();
//this is call inside ListPage after sucess
function updateRightList(id) {
ref.current.state.actualSearchedModel.Id = id
ref.current.fetchDataAndUpdate();
}
function itemClicked(id) {
updateRightList(id);
}
return (
<>
<div className="col-12 no-padding">
<div className={props.leftListLayoutClass}>
<ListPage
updateRightList={updateRightList}
/>
</div>
<div className={props.rightListLayoutClass}>
<ListPage
ref={ref}
/>
</div>
</div>
<>
);
}
According to the official documentation:
You may not use the ref attribute on function components because they
don’t have instances
So if your ListPage is functional component, you have to convert it to the class component. Or your ref must refer to the DOM element inside of ListPage.
function ListPage ({ref}) {
return <div ref={ref}>Hello!</div>
}
UPDATED:
function ParentComponent () {
const [state, setState] = React.useState(null);
const onChildMount = React.useCallback((dataFromChild) => {
setState(dataFromChild);
});
return (
<div>
<pre>{JSON.stringify(state, null, 2)}</pre>
<ChildComponent onMount={onChildMount} />
</div>
)
}
function ChildComponent (props) {
const thisShouldBePassedToTheParent = "from child with love";
React.useEffect(() => {
props.onMount(thisShouldBePassedToTheParent);
}, []);
return (
<div>child component</div>
)
}
ReactDOM.render(<ParentComponent />, document.querySelector("#root"));
<script src="https://unpkg.com/react#16.9.0/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#16.9.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
With functional components you can use refs like this:
// Import our hooks
import React, { useRef, useEffect } from 'react';
// create our ref
const myInput = useRef();
// This is equivalent to our componentDidMount, this will focus
useEffect(() => myInput.current && myInput.current.focus());
// Parse our ref to our textField
<Textfield inputRef={myInput} />
Here you can read docs https://reactjs.org/docs/hooks-reference.html#useref
Also you may use refs like this directly:
<TextField inputRef={input => input && input.focus()} />
You can read full Article here: https://medium.com/javascript-in-plain-english/react-refs-both-class-and-functional-components-76b7bce487b8
If someone looking for solution where Parent is class component and Child is functional component, and want to get data from child (state, function)
Class component:
class Parent extends React.Component {
constructor(){
this.setReferenceToElement = element => {
this.fromChild = element;
}
}
handleClick(){
console.log(this.fromChild());
}
render(){
return (
<div>
<Child setRef={this.setReferenceToElement} />
<button onClick={handleClick}> Get state from child </button>
</div>
)
}
}
Functional component:
function Child(props){
// ... it has some state
props.setRef(state => state);
return <div> Test </div>
}

Resources