I have a LessonThemes component. I use a hook then I use the context to apply logic inside the Test component, the problem is that I don't understand how to get the color value from the hook and apply it inside the context. export const CounterContext = createContext ([ ]); I need to apply value color inside CounterContext
LessonThemes.jsx
import React, {useState, useEffect, createContext} from "react";
import ThemeContext from "./ThemeContext";
export default function LessonThemes(props) {
const [color, setColor] = useState(localStorage.getItem("color"));
const [themes, setThemes] = useState([
{ name: "G", color: "green" },
{ name: "R", color: "red" },
{ name: "B", color: "blue" },
])
useEffect(() => {
localStorage.setItem("color", color);
})
const SideBarPageContent = (SideBarPageContentBackground) => {
localStorage.setItem('color', SideBarPageContentBackground);
setColor(SideBarPageContentBackground);
}
return (
<CounterContext.Provider value={[color, setColor]}>
{
themes.map((theme, index) => {
return (
<label key={index}>
<input
onChange={() => SideBarPageContent(theme.color)}
type="radio"
name="background"
/>{theme.name}</label>
);
})
}
</CounterContext.Provider>
);
}
export const CounterContext = createContext([]);
Lesson.js
import React, { useContext } from "react";
import "./css/Sidebar.css";
export default function Test(props) {
const [color] = useContext(CounterContext);
return (
<div className="page-wrapper chiller-theme toggled">
<LessonThemes />
<div style={{background: color}}>
<i className="fas fa-bars"></i>
</div>
</div>
);
}
There doesn't seem to be anything wrong with the way you are doing it, but it's not clear on how you need to consume that Context.Provider.
I'm assuming you've made an example with minimal functionality, because from your example code, there is no need to the Context to be there in the first place, since you are not rendering any component "under" the context provider.
This is how you would consume it from a nested component. I.e: a component under the <YourContext.Provider/>.
const CounterContext = React.createContext([]);
function LessonThemes(props) {
const [color, setColor] = React.useState("initialColor");
const [themes, setThemes] = React.useState([
{ name: "G", color: "green" },
{ name: "R", color: "red" },
{ name: "B", color: "blue" },
])
const inputItems = themes.map((theme,index) => (
<React.Fragment>
<label key={index}>
<input
onChange={() => setColor(theme.color)}
type="radio"
name="background"
/>
{theme.name}
</label>
</React.Fragment>
));
return (
<CounterContext.Provider value={[color, setColor]}>
{inputItems}
<NestedComponent/>
</CounterContext.Provider>
);
}
// NESTED COMPONENT THAT WILL CONSUME THE CONTEXT
function NestedComponent() {
const [color,setColor] = React.useContext(CounterContext);
return(
<div>
<div>I am NestedComponent</div>
<div><b>color:</b>{color}</div>
</div>
);
}
ReactDOM.render(<LessonThemes/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"/>
Related
Counter File
import React, { useState } from "react";
import Widget from "./widget";
const Counter = () => {
const [form, setForm] = useState(<></>)
const [text, setText] = useState("")
const onCounterChange =() => {
setText(text)
}
const formLoad =() =>{
setForm(
<Widget
onCounterChange={onCounterChange}
children={
<input type="text" onChange={(e) =>{
setText(e.target.value)
}}/>
}
/>
)
}
return (
<div>
{text}
<button onClick={formLoad}>
load widget
</button>
{form}
</div>
)
}
export default Counter
Widget File
import React from 'react'
export default function Widget(props) {
return (
<div className="buttons">
{props.children}
<button onClick={props.onCounterChange}>Save</button>
</div>
)
}
I have created small text printing page . for some purpose I have added children in a diff component and handling widget in a state , so when I try to change the data , text state is changing but when I click save text state becomes empty
As I mentioned in my comment putting a component in state probably isn't the best way of approaching this. Instead I would have a boolean state that allows you to toggle the component on/off.
const { useState } = React;
function Example() {
const [ showWidget, setShowWidget ] = useState(false);
const [ text, setText ] = useState('');
function handleChange(e) {
setText(e.target.value);
}
function handleClick() {
setShowWidget(!showWidget);
}
function handleSave() {
console.log(`Saved state: ${text}`);
}
return (
<div>
<p className="text">Current state: {text}</p>
<button onClick={handleClick}>
Load widget
</button>
{showWidget && (
<Widget>
<input
type="text"
onChange={handleChange}
/>
<button onClick={handleSave}>Save</button>
</Widget>
)}
</div>
);
}
function Widget({ children }) {
return <div className="widget">{children}</div>;
}
ReactDOM.render(
<Example />,
document.getElementById('react')
);
.widget { margin-top: 1em; }
.text { margin-bottom: 1em; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>
My App.js have this structure.
return (
<Container fluid="true" className="h-100">
<Header />
<Row className="contentRow">
<CustomerList />
<DetailPage />
</Row>
</Container>
);
There are many elements in CustomerList. With a click I want to send the ID of the element to DetailPage and display the details of the associated element. But I am still quite new in react and don't really know how to pass the data. Or if I even need to change something in the structure of the components.
You need to define a new state variable in your component.
Then please pass it with the setter function into CustomerList.
Define state variable.
const [id, setId] = useState(null);
Then pass setter function into <CustomerList />
<CustomerList setId={setId} />
// on CustomerList click event
const onClick = (event) => {
// your logic and use setId from props.
// This is just an example.
props.setId(event.target.value);
}
Finally, pass id state variable into <DetailPage /> so that your DetailPage component uses in its props.
<DetailPage id={id} />
Usage in Detailpage:
const DetailPage = (props) => {
const id = props.id;
// use id for your purpose.
}
You can use the event.target.id property. add an onclick function:
onClick={(e) => handleClick(e)}
handleClick = (e) => {
//access e.target.id here
}
See if this help you:
import React, { useState } from "react";
const Header = () => <div />;
const CustomerList = ({ onChange }) => (
<ul>
{["item1", "item2", "item3", "item4"].map((item) => (
<li onClick={() => onChange(item)} key={item}>
{item}
</li>
))}
</ul>
);
const DetailPage = ({ selectedItem }) => <div>{selectedItem}</div>;
const Component = () => {
const [selectedItem, setSelectedItem] = useState(null);
const handleChange = (item) => {
setSelectedItem(item);
};
return (
<div> {/* Container */}
<Header />
<div className="contentRow"> {/* Row */}
<CustomerList onChange={handleChange} />
<DetailPage selectedItem={selectedItem} />
</div>
</div>
);
};
When you click some item, we set the state in parent component, and then send to DetailPage, in DetailPage, you can use this selectedItem to show the info.You also can replace ["item1", "item2", "item3", "item4"] with an array of objects.
App.js
import "./styles.css";
import React, { useState } from "react";
import CustomersList from "./CustomersList";
import { DetailPage } from "./DetailPage";
export default function App() {
const [listOfElements, setListOfElements] = useState([
{ name: "abc", id: "0" },
{ name: "def", id: "1" },
{ name: "ghi", id: "2" },
{ name: "jkl", id: "3" },
{ name: "mno", id: "4" }
]);
const [selectedId, setSelectedId] = useState(1);
const [customerDEatiledinfo, setCuatomerDetailedInfo] = useState({
name: "sample"
});
const idSelectedHandler = (id) => {
const idd = +id;
const newState = listOfElements[idd];
setCuatomerDetailedInfo(newState);
};
return (
<div className="App">
<CustomersList customers={listOfElements} selectId={idSelectedHandler} />
<DetailPage customer={customerDEatiledinfo} />
</div>
);
}
CustomersList.js
export const CustomersList = (props) => {
const onClickHandler = (id) => {
props.selectId(id);
};
return (
<div>
{props.customers.map((customer) => {
return (
<div key={customer.id} onClick={()=>onClickHandler(customer.id)}>
{customer.name}
</div>
);
})}
</div>
);
};
export default CustomersList;
DeatilPage.js
export const DetailPage = (props) => {
return <div style={{ color: "blue" }}>
<br/>
DetailPage
<p>{props.customer.name}</p></div>;
};
I use hooks inside the LessonThemes component, using the context, I try to access the color value inside the Test component, but I get an error
Object is not iterable (cannot read property Symbol (Symbol.iterator))
LessonThemes.jsx
import React, {useState, useEffect, createContext} from "react";
import ThemeContext from "./ThemeContext";
export const CounterContext = createContext();
export default function LessonThemes(props) {
const [color, setColor] = useState(localStorage.getItem("color"));
const [themes, setThemes] = useState([
{ name: "G", color: "green" },
{ name: "R", color: "red" },
{ name: "B", color: "blue" },
])
useEffect(() => {
localStorage.setItem("color", color);
})
const SideBarPageContent = (SideBarPageContentBackground) => {
localStorage.setItem('color', SideBarPageContentBackground);
setColor(SideBarPageContentBackground);
}
return (
<CounterContext.Provider value={[color, setColor]}>
{
themes.map((theme, index) => {
return (
<label key={index}>
<input
onChange={() => SideBarPageContent(theme.color)}
type="radio"
name="background"
/>{theme.name}</label>
);
})
}
</CounterContext.Provider>
);
}
Test.jsx
export default function Test(props) {
const [color] = useContext(LessonThemes);
return (
<div>
<div className="sidebar-brand-container">
<LessonThemes />
</div>
<div>
<span style={{ background: color }} href="#">Theme</span>
</div>
</div>
);
}
LessonThemes is a react component, it's providing the context to its children. CounterContext is the context you need to access in Test.
import { CounterContext } from '../path/to/CounterContext';
export default function Test(props) {
const [color] = useContext(CounterContext);
return (
<div>
<div className="sidebar-brand-container">
<LessonThemes />
</div>
<div>
<span style={{ background: color }} href="#">Theme</span>
</div>
</div>
);
}
You should probably also define an initial context value in case Test isn't being rendered into a React DOMTree with a CounterContext as an ancestor.
export const CounterContext = createContext([]);
I use a color theme in my project for this, I used a React context to pass the value in several components, everything worked fine for me until I decided to add another property inside the object in order to apply different colors to different components, for example a ThemeBackground property that takes green as a value it will be applied to the RoutesPage component and a side property that takes an orange color it will be applied to the SideBar component. The problem is that I cannot apply the side property for the SideBar component, I tried several options, but I did not succeed right now, I will show you everything in more detail in the pictures so that you clearly understand the problem and then I will provide you with the code
Notice the ThemesBackground property is successfully applied to the content but the problem is that I want to apply the side property to my sidebar at the moment I imported the ThemeBackground property for my sidebar so my sidebar applies red color but I think you already understood the problem in short property ThemeBackground should be applied to content and side property to sidebar
LessonThemes.jsx
import React, { useState, useEffect, createContext } from "react";
import SideBar from "./SideBar";
import RoutesPage from "../pages/Routes";
export const CounterContext = createContext(["color"]);
export default function LessonThemes(props) {
const [BackgroundTheme, SetBackgroundTheme] = useState(localStorage.getItem("color"));
const [themes, setThemes] = useState([
{ name: "G", ThemeBackground: "maroon", side: "orange" },
{ name: "R", ThemeBackground: "red", side: "aqua" },
{ name: "B", ThemeBackground: "blue", side: "pink" },
])
useEffect(() => {
localStorage.setItem("color", BackgroundTheme);
})
const SideBarPageContent = (SideBarPageContentBackground) => {
localStorage.setItem('color', SideBarPageContentBackground);
SetBackgroundTheme(SideBarPageContentBackground);
}
const list = themes.map((theme, index) => {
return (
<label key={index}>
<input
onChange={() => SideBarPageContent(theme.ThemeBackground)}
type="radio"
name="background"
/>{theme.name}</label>
);
})
return (
<CounterContext.Provider value={[BackgroundTheme, SetBackgroundTheme]}>
<SideBar list={list} {...props} />
<RoutesPage path={props.match} />
</CounterContext.Provider>
);
}
SideBar.jsx
import React from 'react';
import {CounterContext} from "./LessonThemes";
import SideBarMenu from "./SideBarMenu";
import '../css/Sidebar.css'
export default function SideBar(props) {
const [BackgroundTheme, SetBackgroundTheme] = React.useContext(CounterContext);
return (
<div className="wrappers">
<nav id="sidebar" className="sidebar-wrapper modal">
<div style={{background: BackgroundTheme}} className={"sidebar-page-content"}>
<div className="sidebar-brand">
<div className="sidebar-brand-container">
<div>
{props.list}
</div>
<div>
<span href="#">Theme</span>
</div>
</div>
</div>
<div className="sidebar-menu">
<SideBarMenu path={props.match.path}/>
</div>
...
</div>
</nav>
</div>
);
}
I don't know if it will be useful to you or not, but in addition I want to demonstrate the RoutesPage component, although everything is in order there
RoutesPage.jsx
import React from "react";
import {Route, Switch} from "react-router-dom";
import '../css/Sidebar.css'
import {CounterContext} from "../components/LessonThemes";
function RoutesPage(props) {
const {path} = props.path;
const routes = [
{
path: `${path}`,
exact: true,
component: () => <h2>Home</h2>
},
{
path: `${path}/Calendar`,
component: () => <h2>Test123</h2>
},
{
path: `${path}/Guardian`,
component: () => <h2>Shoelaces</h2>
}
];
const [BackgroundTheme, SetBackgroundTheme] = React.useContext(CounterContext);
return (
<>
<main style={{background: BackgroundTheme}} className="page-content">
<div className="page-container">
<h2>Pro Sidebar</h2>
<hr/>
<div className="tabs">
<Switch>
{routes.map((route, index) => (
<Route
key={index}
path={route.path}
exact={route.exact}
component={route.component}
/>
))}
</Switch>
</div>
</div>
</main>
</>
);
}
export default RoutesPage;
Is it possible that in the SideBar.jsx, the div style needs to be set to BackgroundTheme not SideBarBackgroundTheme? Also wouldn't it be passing in the object so you would still need to key into it for specific colors? Like BackgroundTheme.side?
The problem is that the side property is never being stored to the value of the context. Your context value is still just a string. The complete theme object exists is the local state of the LessonThemes component only.
In the onChange handler of your input you call SideBarPageContent which in turn calls SetBackgroundTheme, which updates the BackgroundTheme property which you pass to the context provider. The argument that you are passing to this function call is theme.ThemeBackground -- which is the background color only and not the entire object.
You likely want to refactor your code so that the context contains the whole object.
Within the SideBar component, it's unclear where you think that the variable SideBarBackgroundTheme is coming from, but that variable doesn't exist.
LessonThemes.jsx
import React, {useState, useEffect, createContext} from "react";
import SideBar from "./SideBar";
import RoutesPage from "../pages/Routes";
export const CounterContext = createContext([]);
export default function LessonThemes(props) {
const [SideBarTheme, SetSideBarTheme] = useState(localStorage.getItem("SideBarKey"));
const [PageContentTheme, SetPageContentTheme] = useState(localStorage.getItem("PageContentKey"));
const [themes, setThemes] = useState([
{
name: "G",
SideBar: "maroon",
PageContent: "blue",
},
{
name: "R",
SideBar: "gray",
PageContent: "green",
},
])
useEffect(() => {
localStorage.setItem("SideBarKey", SideBarTheme, "PageContentKey", PageContentTheme);
})
const SideBarPageContent = (PageContent, SideBar) => {
localStorage.setItem('PageContentKey', PageContent, 'SideBarKey', SideBar);
SetPageContentTheme(PageContent);
SetSideBarTheme(SideBar);
}
const list = themes.map((theme, index) => {
return (
<label key={index}>
<input
onChange={() => SideBarPageContent(theme.PageContent, theme.SideBar)}
type="radio"
name="background"
/>{theme.name}</label>
);
})
return (
<CounterContext.Provider value={{
SideBarValue: [SideBarTheme, SetSideBarTheme],
PageContentValue: [PageContentTheme, SetPageContentTheme]
}}>
<SideBar list={list} {...props} />
<RoutesPage path={props.match}/>
</CounterContext.Provider>
);
}
SideBar.jsx
export default function SideBar(props) {
const { SideBarValue } = React.useContext(CounterContext);
const [SideBarTheme, SetSideBarTheme] = SideBarValue;
return (
<div className="wrappers">
<nav id="sidebar" className="sidebar-wrapper modal">
<div style={{background: SideBarTheme}} className={"sidebar-page-content"}>
<div className="sidebar-brand">
<div className="sidebar-brand-container">
<div>
{props.list}
</div>
<div>
<span href="#">Theme</span>
</div>
</div>
...
RoutesPage.jsx
import React from "react";
import {Route, Switch} from "react-router-dom";
import '../css/Sidebar.css'
import {CounterContext} from "../components/LessonThemes";
function RoutesPage(props) {
const {path} = props.path;
const routes = [
{
path: `${path}`,
exact: true,
component: () => <h2>Home</h2>
},
{
path: `${path}/Calendar`,
component: () => <h2>Test123</h2>
},
{
path: `${path}/Guardian`,
component: () => <h2>Shoelaces</h2>
}
];
const { PageContentValue } = React.useContext(CounterContext);
const [PageContentTheme, SetPageContentTheme] = PageContentValue;
return (
<>
<main style={{background: PageContentTheme}} className="page-content">
...
I use react-select and I'm new.I have a component called Example
import React from "react";
import Select from "react-select";
class Example extends React.Component {
state = {
selectedOption: null
};
render() {
const { onHandleChange, options } = this.props;
return <Select onChange={onHandleChange} options={options} isMulti />;
}
}
export default Example;
In another file we have a functional Component
import React, { useState } from "react";
import Example from "./Example";
import { regionOptions, ageOptions, bordOptions } from "./Options";
export default function UserProfile() {
const [selectedOption, setSelectedOption] = useState({
region: "",
age: "",
bord: ""
});
const handleChange = (key, selectedOption) => {
setSelectedOption(prev => ({ ...prev, [key]: selectedOption }));
};
console.log(Object.values(selectedOption));
return (
<div>
<Example
id="region"
onHandleChange={value => handleChange("region", value)}
selectedOption={selectedOption.region}
options={regionOptions}
/>
<Example
id="age"
onHandleChange={value => handleChange("age", value)}
selectedOption={selectedOption.age}
options={ageOptions}
/>
<Example
id="bord"
onHandleChange={value => handleChange("bord", value)}
selectedOption={selectedOption.bord}
options={bordOptions}
/>
</div>
);
}
I display the values in the console by the handChange event.
But when the options increase, I can't say which one belongs to which .
I want the console.log instead of the
[Array[n], Array[n], Array[n]]
Something like this will be displayed
[Region[n], age[n], bord[n]]
You can see my code here
https://codesandbox.io/s/upbeat-night-tqsk7?file=/src/UserProfile.js:0-1040
just use
console.log(selectedOption);
instead of
console.log(Object.values(selectedOption));
What you can do is a create a custom hook and make the following changes.
// custom hook
function useFormInput(initial) {
const [value, setValue] = useState(initial);
const handleOnChange = e => {
setValue(e);
};
return {
selectedOptions: value,
onChange: handleOnChange
};
}
then in the code
export default function UserProfile() {
const region = useFormInput(""); // return { selectedOption, onChange }
const age = useFormInput("");
const bord = useFormInput("");
// NB {...region} pass deconstructed return values from custom hook to the component
return (
<div>
<Example id="region" {...region} options={regionOptions} />
<Example id="age" {...age} options={ageOptions} />
<Example id="bord" {...bord} options={bordOptions} />
{JSON.stringify(region.selectedOptions)}
{JSON.stringify(age.selectedOptions)}
{JSON.stringify(bord.selectedOptions)}
</div>
);
}
// your UI component
render() {
const { onChange, options } = this.props;
return <Select onChange={onChange} options={options} isMulti />;
}
working example
https://codesandbox.io/s/optimistic-napier-fx30b?