I've been studying react and developing an app, but i got a problem using context. In one component I create the context and provide its value, but when I try to use the current value of context in another component, I have the default value. Code:
Component One:
export const OwnerInformationContext = React.createContext({})
function NameChoose() {
...
const [ownerInformation,setOwnerInformation] = useState({})
function onpressSubmitButton(e : FormEvent) {
e.preventDefault();
...
setOwnerInformation({name:'name',roomId:'id',owner:'true'})
}
return(
<div className="page-container">
<OwnerInformationContext.Provider value={ownerInformation} />
...
<form onSubmit={onpressSubmitButton}>
...
</form>
...
);
}
export default NameChoose;
So when i try to use by:
import { OwnerInformationContext } from '../NameChoose/index'
function ComponentTwo(){
const consumeOwnerContext = useContext(OwnerInformationContext)
useEffect(() => {
console.log(consumeOwnerContext)
}, [])
return <h1>test</h1>
}
I got the default value provide in component one, that's {}.
It looks like your context provider is not actually wrapping any components, as it has a self-closing tag:
<OwnerInformationContext.Provider value={ownerInformation} />
It should be:
<OwnerInformationContext.Provider value={ownerInformation}>
{/* Your child components here will have access to the context */}
</OwnerInformationContext.Provider>
You are using useEffect as ComponentDidMount meaning only at start(mount) the value will be console log.
you should give consumeOwnerContext as a dependency to useEffect like this
useEffect(()=>{
console.log(consumeOwnerContext);
},[consumeOwnerContext]);
And rename consumeOwnerContext to consumeOwnerValue, because you are getting the value out of the context using useContext.
After that when you will click on submit button you should have ComponentTwo console log it.
import React, { useState, useEffect, useContext } from "react";
export const OwnerInformationContext = React.createContext({});
function ComponentTwo() {
const consumeOwnerContext = useContext(OwnerInformationContext);
useEffect(() => {
// You are using consumeOwnerContext inside useEffect, in that case add
// it as dependency if you want to see the updated consumeOwnerContext value
console.log(consumeOwnerContext);
}, [consumeOwnerContext]);
return <div>test</div>;
};
function NameChoose() {
const [ownerInformation, setOwnerInformation] = useState({});
function onpressSubmitButton(e) {
e.preventDefault();
setOwnerInformation({ name: "name",roomId: "id",owner: "true",});
}
return (
// The 'OwnerInformationContext.Provider' has to wrap the component
// that will use its context value. In your case, ComponentTwo
// has to be a child of NameChoose.
<OwnerInformationContext.Provider value={ownerInformation}>
<div className="page-container">
<form onSubmit={onpressSubmitButton}>
<button type="submit">Submit</button>
</form>
</div>
<ComponentTwo />
</OwnerInformationContext.Provider>
);
}
export default NameChoose;
Related
The issue I got is that the fetched Data from API is not saved to a variable. Please look at the fearvalue, it's being called later and the value of that is an empty string.
APY component
export let fearvalue = [];
export const fearAndGreed = () => {
// 1. Create a new XMLHttpRequest object
let bitcoinAPY = new XMLHttpRequest();
// 2. Configure it: GET-request for the URL /article/.../load
bitcoinAPY.open("GET", "https://api.alternative.me/fng/?limit=10&date_format=us", false)
bitcoinAPY.onload = () => {
const data = JSON.parse(bitcoinAPY.response);
/*const saveStaticDataToFile = () => {
let blob = new Blob(['Welcome'],
{type: 'text/plain;charset=utf-8'});
saveStaticDataToFile(blob, "static.txt")
}*/
console.log(data)
fearvalue = data.data[0];
}
// 3. Send the request over the network
bitcoinAPY.send();
}
window.addEventListener('load', fearAndGreed)
fearvalue is being called in this component and it is a blank value. Can anyone help me with saving data to this variable?
import './App.css';
import './Apy_TAPI';
import './Bitcoin Fear&Greed';
import { DataFormatting } from './DataFormatting.js';
import { fearvalue } from './Bitcoin Fear&Greed';
import './App.css';
import './Apy_TAPI';
import './Bitcoin Fear&Greed';
import { DataFormatting } from './DataFormatting.js';
import { fearvalue } from './Bitcoin Fear&Greed';
function App() {
const test1 = "test1"
console.log(fearvalue)
return (
<div className="App">
<header className="App-header">
<p>
Bitcoin analyst tool
</p>
</header>
<div className='Text'>
<h1>
<img className="Image" src="https://alternative.me/crypto/fear-and-greed-index.png" alt="Latest Crypto Fear & Greed Index" />
</h1>
<h2>
https://bitinfocharts.com/pl/bitcoin/address/1P5ZEDWTKTFGxQjZphgWPQUpe554WKDfHQ <br />
<br />
{fearvalue} <br />
</h2>
</div>
</div>
);
}
export default App;
You need to save the response in a proper way. in React.js, you could use useState to create a state variable and set the response in it.
First, you need to save the response in a state variable:
import React, {useState} from 'react';
export const fearAndGreed = () => {
const [fearValue, setFearValue] = useState() // initialize with proper value according to your data type, it could be a empty array [] or an object {}.
let bitcoinAPY = new XMLHttpRequest();
bitcoinAPY.open("GET", "https://api.alternative.me/fng/?limit=10&date_format=us", false)
bitcoinAPY.onload = () => {
const data = JSON.parse(bitcoinAPY.response);
setFearValue(data.data[0]) // ------> set the fearValue here
}
bitcoinAPY.send();
}
window.addEventListener('load', fearAndGreed)
So far, the first part is done. but you need the fearValue in the other component. to achieve this, there are some solutions like using a Global State Manager like Redux or ContextApi. without them, your implementation would be tricky since you can't use the lifting state up technique because you didn't use fearAndGreed as a child component in the parent component (App).
In such cases, you can implement a custom hook with fearAndGreed function. since the function invokes once after the page loading, you can implement this by calling the API after your components did mount.
Let's make a custom hook with the current fearAndGreed function in this way:
import {useEffect, useState} from 'react';
export const useFearAndGreed = () => {
const [fearValue, setFearValue] = useState();
useEffect(() => {
let bitcoinAPY = new XMLHttpRequest();
bitcoinAPY.open("GET", "https://api.alternative.me/fng/?limit=10&date_format=us", false)
bitcoinAPY.onload = () => {
const data = JSON.parse(bitcoinAPY.response);
setFearValue(data.data[0]) // ------> set the fearValue here
}
bitcoinAPY.send();
}, [])
return fearValue;
}
Explanation:
With a few changes, fearAndGreed function becomes a custom hook useFearAndGreed.
The API will call in the useEffect after the component did mount (with an empty array of dependencies).
The hook will return the fearValue on every change.
Now, time to use this custom hook inside of the App component:
function App() {
const fearValue = useFearAndGreed()
return (
<div>
{fearValue}
</div>
);
}
export default App;
Note: I removed the other parts of implementation in the App component to simplify it. you should include your own implementation within.
Now, every time the App component did mount, the useFearAndGreed will be invoked and return the fearValue which can be saved in a constant fearValue to use in the div element.
I want to pass the setState method of the component (SnackBar) to all the child components of the _app.js. If I pass the setState method of SnackBar to all the child components of _app.js then it will be a very tedious task. Because, there are approx 4 levels of hierarchy from _app.js to the single component node. It includes,
_app.js -> pages -> layouts -> sections -> components
The snippet of _app.js is here.
function MyApp({ Component, pageProps }) {
const [ toastOpen, setToastOpen ] = React.useState({
msg: '',
open: false
});
React.useEffect(() => {
pageProps = { ...pageProps, setToastOpen };
}, []);
return (
<React.Fragment>
<ToastMessage
message={ toastOpen.msg }
setOpenState={ setToastOpen }
openState={ toastOpen.open }
/>
<Component {...pageProps} />
</React.Fragment>
)
}
Is there any way that I can directly import the setToastOpen method in the child component and use it whenever I need it?
React have a special Feature called Context Api , using that you can skip the props chain passed into your components..
I recomend you to checkout below resources to learn about context Api -
https://reactjs.org/docs/context.html
https://www.freecodecamp.org/news/react-context-in-5-minutes
Example of ContextAPI
Create a seperate file for Context Toast-context.js , You can use any name you want.
import React, { useState } from "react"
const ToastContext = React.createContext({
message: "",
toastOpen: false,
toggleToast: () => { },
changeMessage: () => { }
})
export const ToastContextProvider = ({children}) => {
/*you need to use
this component to wrap App.js so that the App.js and all of its children
and their children components and so on will get the access to the
context*/
const [toastOpen, setToastOpen] = useState(false);
const [message, setMessage] = useState("");
const toggleToast = () => {
setToastOpen(true)
}
const changeMessage = (message) => {
setMessage(message);
}
return (
<ToastContext.Provider value={
toastOpen,
message,
toggleToast,
changeMessage
}>
{children}
</ToastContext.Provider>
)
}
now in the App.js file you need to wrap your components with ToastContextProvider component
import React, { useContext } from "react";
import { ToastContextProvider } from "./Toast-context";
import { ToastContext } from "./Toast-context";
function MyApp({ Component, pageProps }) {
const { message, toastOpen, toggleToast, changeMessage } =
useContext(ToastContext);
return (
<ToastContextProvider>
{toastOpen && <div className="toast">{message}</div>}
</ToastContextProvider>
);
}
just import the context using useContext Hook in any component you want. you don't need to wrap with <ToastContextProvider> in every component.
just use useContext hook and then you can see the state and as well as call the functions methods to change the state.
Also make sure to refer the above links to learn more about Context Api. Thank You
I have the next code, where I import NextButton and GroupButton from TitleHeader,
those components are simple buttons
After that, I declared a simple array ButtonsArray and filled it with those components in the useEffect segment, in adition, I 'bind' the Button function to the button component.
Example :
<NextButton function={ShowSearchBar}/>
Then, my other component TitleHeader receives the array and render the components inside it using a map function
My issue is, if I use the const array ButtonsArray with the components loaded as props in TitleHeader, when press the NextButton in the UI to confirm everything is working something weird happens
The only job of NextButton is execute ShowSearchBar function whose have to switch a const from true to false and vice versa but it doest not work,
If i debug the program, when I press the button, the program enters to the ShowSearchBar function but ALWAYS allowFind is false
Note: if I declare the array directly in the TitleHeader params everything works fine
import React, { useState, useEffect } from "react";
import { TitleHeader, NextButton, GroupButton } from "../Common/TitleHeader";
export const ACATG001 = () => {
const [allowFind, setAllowFind] = useState(false);
const [allowGroup, setAllowGroup] = useState(false);
const [ButtonsArray, setButtonsArray] = useState([]);
useEffect(() => {
setButtonsArray([
<NextButton function={ShowSearchBar} />,
<GroupButton function={ShowGroupBar} />,
]);
}, []);
function ShowSearchBar() {
setAllowFind(!allowFind);
}
return (
<GeneralContainer>
//doesnt work (using a const type array and filled in UseEffect)
<TitleHeader
Title={t("TTER001")}
BarSize="300px"
Embedded={false}
ButtonsArray={ButtonsArray}
/>
//Works declaring the array and the items inline
<TitleHeader
Title={t("TTER001")}
BarSize="300px"
Embedded={false}
ButtonsArray={[
<NextButton function={ShowSearchBar} />,
<GroupButton function={ShowGroupBar} />,
]}
/>
</GeneralContainer>
);
};
Second JS TitleHeader
import React, { Component } from "react";
import { Button } from "primereact/button";
export class TitleHeader extends Component {
constructor() {
super();
}
componentDidMount() {}
render() {
let TitleDesing;
TitleDesing = (
<div className="Buttons-Group">
{this.props.ButtonsArray.map((component, index) => (
<React.Fragment key={index}>{component}</React.Fragment>
))}
</div>
);
return TitleDesing;
}
}
export const NextButton = (props) => {
return (
<Button
id="nextButton"
label="test"
tooltip="Next"
className="p-button-rounded p-button-text"
onClick={props.function}
>
<CgChevronRight size="20PX" color=" #d6f1fa" />{" "}
</Button>
);
};
If the update you do to a state depends only on its current value, always use the function callback version of the dispatcher, this will guarantee you don't use a stale value
function ShowSearchBar() {
setAllowFind((previousAllowFind) => !previousAllowFind)
}
I'm trying to port from class component to react hooks with Context API, and I can't figure out what is the specific reason of getting the error.
First, my Codes:
// contexts/sample.jsx
import React, { createContext, useState, useContext } from 'react'
const SampleCtx = createContext()
const SampleProvider = (props) => {
const [ value, setValue ] = useState('Default Value')
const sampleContext = { value, setValue }
return (
<SampleCtx.Provider value={sampleContext}>
{props.children}
</SampleCtx.Provider>
)
}
const useSample = (WrappedComponent) => {
const sampleCtx = useContext(SampleCtx)
return (
<SampleProvider>
<WrappedComponent
value={sampleCtx.value}
setValue={sampleCtx.setValue} />
</SampleProvider>
)
}
export {
useSample
}
// Sends.jsx
import React, { Component, useState, useEffect } from 'react'
import { useSample } from '../contexts/sample.jsx'
const Sends = (props) => {
const [input, setInput ] = useState('')
const handleChange = (e) => {
setInput(e.target.value)
}
const handleSubmit = (e) => {
e.preventDefault()
props.setValue(input)
}
useEffect(() => {
setInput(props.value)
}, props.value)
return (
<form onSubmit={handleSubmit}>
<input value={input} onChange={handleChange} />
<button type="submit">Submit</button>
</form>
)
}
Error I got:
Invariant Violation: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: 1. You might have mismatching versions of React and the renderer (such as React DOM) 2. You might be breaking the Rules of Hooks 3. You might have more than one copy of React in the same app See https://reactjs.org/warnings/invalid-hook-call-warning.html for tips about how to debug and fix this problem.
Explanation for my code:
I used Context API to manage the states, and previously I used class components to make the views. I hope the structure is straightforward that it doesn't need any more details.
I thought it should work as well, the <Sends /> component gets passed into useSample HoC function, and it gets wrapped with <SampleProvider> component of sample.jsx, so that <Sends /> can use the props provided by the SampleCtx context. But the result is failure.
Is it not valid to use the HoC pattern with React hooks? Or is it invalid to hand the mutation function(i.e. setValue made by useState()) to other components through props? Or, is it not valid to put 2 or more function components using hooks in a single file? Please correct me what is the specific reason.
So HOCs and Context are different React concepts. Thus, let's break this into two.
Provider
Main responsibility of the provider is to provide the context values. The context values are consumed via useContext()
const SampleCtx = createContext({});
export const SampleProvider = props => {
const [value, setValue] = useState("Default Value");
const sampleContext = { value, setValue };
useEffect(() => console.log("Context Value: ", value)); // only log when value changes
return (
<SampleCtx.Provider value={sampleContext}>
{props.children}
</SampleCtx.Provider>
);
};
HOC
The consumer. Uses useContext() hook and adds additional props. Returns a new component.
const withSample = WrappedComponent => props => { // curry
const sampleCtx = useContext(SampleCtx);
return (
<WrappedComponent
{...props}
value={sampleCtx.value}
setValue={sampleCtx.setValue}
/>
);
};
Then using the HOC:
export default withSample(Send)
Composing the provider and the consumers (HOC), we have:
import { SampleProvider } from "./provider";
import SampleHOCWithHooks from "./send";
import "./styles.css";
function App() {
return (
<div className="App">
<SampleProvider>
<SampleHOCWithHooks />
</SampleProvider>
</div>
);
}
See Code Sandbox for full code.
Higher order Components are functions that takes a Component and returns another Component, and the returning Components can be class component, a Functional Component with hooks or it can have no statefull logic.
In your example you're returning jsx from useSample.
const useSample = (WrappedComponent) => {
const sampleCtx = useContext(SampleCtx)
return ( // <-- here
<SampleProvider>
<WrappedComponent
value={sampleCtx.value}
setValue={sampleCtx.setValue} />
</SampleProvider>
)
}
if you want to make a HOC what you can do is something like this
const withSample = (WrappedComponent) => {
return props => {
const sampleCtx = useContext(SampleCtx)
<WrappedComponent
value={sampleCtx.value}
setValue={sampleCtx.setValue} {...props} />
}
}
Where's the right place to put code that interacts with the DOM in a gatsby site? I want to toggle the visibility of some components by adding/removing a class when another element is clicked.
The gatsby-browser.js file seems like it should contain this code but the API doesn't seem to call any of the functions after the DOM has loaded.
Similarly, using Helmet calls it too soon. Using window.onload never seems to trigger at all regardless of where it's included.
window.onload = function () {
// add event listener to the toggle control
}
Is there an event I can use to run my code when the DOM is ready?
Do you really need to wait for the DOM to be ready? When working in react you need to change the way you think about these things. For example you could add an on click that changes state and then reflect the state change in your classname prop.
Code Example:
import React, { useState } from "react"
const MyApp = () => {
const [visible, setVisible] = useState(true) // true is the initial state
return (
<div>
<div className={visible ? "visible-class" : "hidden-class"}>
My content
</div>
<button onClick={() => setVisible(!visible)}>Click me!</button>
</div>
)
}
export default MyApp
Or you could take it a step further and not even render that content to the DOM until you want to.
Example:
import React, { useState } from "react"
const MyApp = () => {
const [visible, setVisible] = useState(true) // true is the inital state
return (
<div>
<button onClick={() => setVisible(!visible)}>Click me!</button>
{visible && <div>My content here</div>}
</div>
)
}
export default MyApp
You can use the React cyclelife with componentDidMount().
This need to update your component like that :
import React from 'react'
class YourComponent extends React.Component {
componentDidMount() {
// Your Javascript function here
}
render() {
return(
<div className="YourComponentHere"></div>
)
}
}
export default YourComponent
Hope that help you!
If your component is a functional component, try using React Hook useEffect, which will guarantee the execution after the component is rendered.
import React, { useEffect } from 'react'
const MyComponent = () => {
useEffect(() => {
console.log("Document loaded");
});
return (
<main>
<text>Pretty much the component's body code around here</text>
</main>
)
}
export default MyComponent