React.js - passing functions between components using Context API - not working - reactjs

I am trying to pass a function between two components but even though I do not have any errors, the function that I am passing wont show or to be precise it is not working. I have two files and one of them is creating a context while the other is using it (obviously). Now, they are not shown in App.js (which is rendered in index.js, usual stuff) they are in the seperate folder. I am using React Router to show one the pages (News.js).
Here are the files:
NewsContext.js
import React, { useContext, createContext, useState, useEffect } from "react";
export const newsK = React.createContext();
export const NewsContext = (props) => {
const working = () => {
console.log("it is working");
};
return <newsK.Provider value={working}>{props.children}</newsK.Provider>;
};
export default NewsContext;
News.js
import React, { useContext, useState, useEffect } from "react";
import { newsK } from "./NewsContext";
import { NewsContext } from "./NewsContext";
const News = () => {
const data = useContext(newsK);
return (
<NewsContext>
<div>
<button onClick={data}></button>
</div>
</NewsContext>
);
};
export default News;
When i pressed the button, it wont do anything. Any tips?

You're trying to use context outside the NewsContext component
The solution for this will be to create a component that will call useContext inside NewsContext.
I.e.
const WrappedButton = () => {
const data = useContext(newsK)
return <button onClick={data} />
}
And then put it inside the NewsContext:
<NewsContext>
<div>
<WrappedButton />
</div>
</NewsContext>

Related

How to render new value of array in react

Here I am getting my data from child component and trying to add it into existing array and also I am trying to display it in console using map but the moment I try to to do so I get nothing in console:
import "./App.css";
import Home from "./Components/Home";
import {useState} from 'react';
let App = () => {
let DataList = ["Apple","Banana"];
const newList = (data)=>{
DataList = [...DataList,data];
}
return (
<div>
<Home newList = {newList}/>
{ DataList.map((val)=>(
console.log(val)
))
}
</div>
)
};
export default App;
You need to use React hooks to make it work, then only your component will rerender
import "./App.css";
import Home from "./Components/Home";
import {useState} from 'react';
let App = () => {
const [DataList, setDataList] = useState(["Apple","Banana"]);
const newList = (data)=>{
let temp = [...DataList,data];
setDataList(temp);
}
return (
<div>
<Home newList = {newList}/>
{ DataList.map((val)=>(
console.log(val)
))
}
</div>
)
};
export default App;
You imported this for a reason, use it:
import {useState} from 'react';
React won't detect changes to any random variable you declare. So there's no reason for it to re-render the component in the code you have.
State values are fundamental to React. It's how you persist data across renders, and it's how React knows to re-render any given component. Store your data in state:
const [dataList, setDataList] = useState(["Apple","Banana"]);
And use that setter function to update the state:
const newList = (data)=>{
setDataList([...dataList, data]);
};
When React sees that state was updated, it will queue a re-render of the component (in this case re-invoking the App function internally to the framework) so the new state can be used.

Have to click twice to update state (useState hook) with onClick event

I've seen more questions being post here about this, but the solutions did not work for me.
The problem is that I: have to click twice to update the state of a constant defined using the useState hook
How can I make this work only having to click once?
Please see the code on CodeSandbox, or the code below:
App.js
import React, { useState } from "react";
import "./styles.css";
import Row from "./Row";
export default function App() {
const [adults, setAdults] = useState(1);
return <Row adults={adults} setAdults={setAdults} />;
}
Row.jsx
import React, { useEffect } from "react";
export default function({ adults, setAdults }) {
useEffect(() => {
console.log(adults);
}, [adults]);
return (
<>
<button onClick={() => setAdults(adults++)}>{adults}</button>
</>
);
}
Either do:
setAdults(adults+1)
or:
setAdults(++adults)
Be careful using ++ because someObject.counter++ will mutate someObject.

Context is not available right after page refresh in React

There's WarriorPage component which use context where is data I want to render. After page refresh, firstly I got an empty array from the context and only after a while I got array with my data. That causes error because I'm destructuring object from that array(which is empty in the start). Any sugestions?
WarriorPage
import React, { useContext } from 'react';
import { useParams } from 'react-router-dom';
import AllWarriorsContext from '../../contexts/AllWariorsContext';
export default function WarriorPage() {
let { identy } = useParams();
const { warriorsData } = useContext(AllWarriorsContext);
const {number, name, skill, description} = warriorsData[identy]; // got undefined here after page reload
return(...);
}
In Parent component
import React, { useEffect, useState, useContext } from 'react';
import AllWarriorsContext from '../../contexts/AllWariorsContext';
import WarriorPage from '../WarriorPage/WarriorPage';
export default function Parent() {
const [myWarriorsListContext, setMyWarriorsListContext] = useState([]);
useEffect( () => {
setMyWarriorsListContext(JSON.parse(localStorage.getItem('myWarriorsList')) || []);
},[]);
return(
<AllWarriorsContext.Provider value={{
warriorsData: allWarriorsData
}}>
<WarriorPage />
</AllWarriorsContext>
);
}

On click returns null instead of an object

It's really basic I guess. I'm trying to add onClick callback to my script & I believe I'm missing a value that would be responsible for finding the actual item.
Main script
import React from 'react';
import { CSVLink } from 'react-csv';
import { data } from 'constants/data';
import GetAppIcon from '#material-ui/icons/GetApp';
import PropTypes from 'prop-types';
const handleClick = (callback) => {
callback(callback);
};
const DownloadData = (props) => {
const { callback } = props;
return (
<>
<CSVLink
data={data}
onClick={() => handleClick(callback)}
>
<GetAppIcon />
</CSVLink>
</>
);
};
DownloadData.propTypes = {
callback: PropTypes.func.isRequired,
};
export default DownloadData;
Storybook code
import React from 'react';
import DownloadData from 'common/components/DownloadData';
import { data } from 'constants/data';
import { action } from '#storybook/addon-actions';
export default {
title: 'DownloadData',
component: DownloadData,
};
export const download = () => (
<DownloadData
data={data}
callback={action('icon-clicked')}
/>
);
So right now with this code on click in the storybook I'd get null and I'm looking for an object.
One of the potential issues I can see is that your handleClick function is stored as it is in-memory, when you import the component. That means you're keeping reference of something that doesn't exists and expects to use it when rendering the component with the callback prop.
Each instance of a component should have its own function. To fix it, move the function declaration inside the component. Like this:
const Foo = ({ callback }) => {
// handleClick needs to be inside here
const handleClick = callback => {
console.log("clicked");
callback(callback);
};
return <div onClick={() => handleClick(callback)}>Click me!</div>;
};
Check this example.
If this doesn't fix your problem, then there is something wrong with how you're implementing Storybook. Like a missing context.

Why am I getting "useContext is not a function" or "context is not defined"?

I am trying to use context in my stateless component. I updated my react to v16.8.0 and added useContext, however, I keep getting these two errors and don't know what else to do. Here is my code:
import React, { useState } from "react";
import axios from "axios";
import { LanguageContext } from "./languageContext";
import { useContext } from "react";
function StripeButton() {
const context = useContext(LanguageContext);
const stripe = Stripe("pk_live_5PjwBk9dSdW7htTKHQ3HKrTd");
const [error, setError] = useState();
const handleClick = () => {
stripe
.redirectToCheckout({
...
});
};
return (
<div>
<button
id="UrgentCheckedButtonYES"
className="btn-primary"
onClick={handleClick}
>
{this.context.main.name}
<br />
</button>
<div>{error}</div>
</div>
);
}
export default StripeButton;
StripeButton.contextType = LanguageContext;
You need to import useContext like this:
import { useContext } from 'react';
const { useContext } = React
useContext is exported as method property of React
(Tested this in React 18.)
In App.js File:
import { createContext } from "react";
import ChildComponent from "./components/ChildComponent";
export const Context = createContext("Default Value");
export default function App() {
const value = "My Context Value";
return (
<Context.Provider value={value}>
<ChildComponent />
</Context.Provider>
);
}
In ChildComponent.jsx file:
import { useContext } from "react";
import { Context } from "../App";
function ChildComponent() {
const value = useContext(Context);
return <h1>{value}</h1>;
}
export default ChildComponent;
You can have as many consumers as you want for a single context. If the context value changes,then all consumers are immediately notified and re-rendered.
If the consumer isn't wrapped inside the provider, but still tries to access the context value (using useContext(Context) or <Context.Consumer>), then the value of the context would be the default value argument supplied to createContext(defaultValue) factory function that had created the context.

Resources