Push state of reactive value to dependencies - reactjs

I have an object: dynamicJSON that is changing. I would like to pass this object down to multiple dependencies: componentA, componentB. I also want the parts of the dependencies using the object to render when the object is changed.
I tried the useContext Hook, but received a dependency cycle error. What is the proper way to pass reactive values down to dependencies in react?
App.js
import { componetA } from "compA"
import { componetB } from "compB"
import { fetchLatestValue} from "api/fetchLatestValue"
import { useEffect } from "react"
export default function App() {
const dynamicJSON = ???;
useEffect(() => {
let timeoutId;
async function getlatestValue() {
try {
const data = await fetchLatestValue();
// update dynamicJSON here.
} catch (error) {
}
timeoutId = setTimeout(getlatestValue, 1000 * 1);
}
getlatestValue();
return () => {
clearTimeout(timeoutId);
};
}, []);
return (
<componetA />
<componetB />
);
}
compA
export default function componentA() {
const dynamicJSON = ???;
return(
<div>
{dynamicJSON.value}
</div>
)
};

Have you tried useEffect() with a dependency array? If anything in the dependency array is changed, the hook function will be triggered.
Reference: https://reactjs.org/docs/hooks-reference.html#conditionally-firing-an-effect
Sorry I mis-read your question, you should pass dynamicJSON into both components as a prop. Make dynamicJSON a state is also a good idea.
Rule of thumb: if a prop or state of a component is changed, then this component is rerendered.
import { ComponentA } from "compA";
import { ComponentB } from "compB";
import { useEffect, useState } from "react";
export default function App() {
const [dynamicJSON, setDynamicJSON] = useState({});
//...omit
return (
<ComponentA dynamicJSON={dynamicJSON}/>
<ComponentB dynamicJSON={dynamicJSON}/>
);
}
CompA.js
export default function ComponentA(props) {
const { dynamicJSON } = props;
return(
<div>
{dynamicJSON.value}
</div>
)
};

Related

Reactjs refactoring a component to a function

I am trying to convert the following Component into Function.
I need it to accept or access one parameter, do a database lookup and return a String.
I get the following compile errors implying I have incorrect syntax for a function. Any help completing the new function would be appreciated!
Errors:
src\Services\getAssetTypeNameFunction.js
Line 5:45: React Hook "useState" is called in function "getAssetTypeNameFunction" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter react-hooks/rules-of-hooks
Line 11:13: 'assettype' is not defined
New Function Code
...
import React, { useState} from 'react';
import AssetTypeService from './AssetTypeService'
const getAssetTypeNameFunction = () =>{
const [assettype_assettypeId,setData] = useState('assettype_assettypeId')
AssetTypeService.getAssetTypeById(assettype_assettypeId).then( (res) =>
{let assettype = res.data;
});
return (
<ul>
{assettype.assettypeName}
</ul>
);
}
export default getAssetTypeNameFunction;
...
Old Component Code that worked..
...
**import React, { Component } from 'react';
import AssetTypeService from '../Services/AssetTypeService'
class GetAssetTypeNameComponent extends Component {
constructor (props){
super(props)
this.state = {
assettype:[]
}
}
componentDidMount()
{
AssetTypeService.getAssetTypeById(this.props.datafromparent).then( (res) =>{
let assettype = res.data;
this.setState({isLoading:false});
this.setState({
assettypeName: assettype.assettypeName,
assettypeType: assettype.assettypeType
});
});
}
render() {
return (
<div>
{this.state.assettypeName}
</div>
);
}
}
export default GetAssetTypeNameComponent;**
...
New function code:
...
import React, { useState, useEffect} from 'react';
import AssetTypeService from './AssetTypeService'
function GetAssetTypeNameFunction() {
const [assettype, setAssetType] = useState(null)
useEffect( () => {
AssetTypeService.getAssetTypeById(assettype_assettypeId).then((res) => {
setAssetType(res.data);
});
}, []);
if(!assettype)
return (<ul>loading...</ul>)
return (<ul>assettype.assettypeName</ul>);
}
export default GetAssetTypeNameFunction;
...
In your Componenent you used componentDidMount to fetch data.
You could the useEffect Hook in your function to have the same effect
https://reactjs.org/docs/hooks-effect.html
useEffect takes two parameters, a function that gets called and a dependencies array.
if you pass in an empty array into as dependencies, your function gets only called on after your function gets mounted
const [assettype, setAssetType] = useState(null)
useEffect( () => {
AssetTypeService.getAssetTypeById(assettype_assettypeId).then((res) => {
setAssetType(res.data);
});
}, [])
if(!assettype)
return (<ul>loading...</ul>
return (<ul>assettype.assettypeName</ul>)

React native typescript: usecontext functions not firing from inside child component

I have an issue when i try to use functions from a context inside a child component in a React native android app.
Below is my code for the context, and the form component im using it in (stripped down for brevity).
The "isFormOpen" object can be read no problem from inside any children that is wrapped in the provider, but when i try to call the "toggleForm" function from the same child component, it does nothing, no console errors either.
I have another context which is identical in structure and syntax except for vairable and function names etc, and that works perfectly, so im a bit confused as to why this does not work. I removed the other context, thinking there might be some type of conflict, but didnt solve it.
AccountContext.tsx
import React, { FC, createContext, useContext, useState } from 'react';
interface AccountContextType {
isFormOpen: boolean,
toggleForm: (toggle: boolean) => void
};
export const AccountContext = createContext<AccountContextType>({
isFormOpen: false,
toggleForm: () => null
});
export const AccountContextProvider: FC = props => {
const [formOpen, setFormOpen] = useState<boolean>(false);
const toggleForm = (toggle: boolean) => {
setFormOpen(toggle);
}
const value: AccountContextType = {
isFormOpen: formOpen,
toggleForm
}
return (
<AccountContext.Provider value={value}>
{props.children}
</AccountContext.Provider>
)
}
export const useAccountContext = () => useContext(AccountContext);
TrackUploadForm.js
import React from 'react';
import { SafeAreaView } from 'react-native';
import { Button } from 'react-native-paper';
import { useAccountContext } from '../contexts/AccountContext';
import { AccountContextProvider } from '../contexts/AccountContext';
const TrackUploadForm = () => {
const accountContext = useAccountContext();
return (
<AccountContextProvider>
<SafeAreaView>
<Button onPress={() => accountContext.toggleForm(false)} mode='outlined'>Cancel</Button>
</SafeAreaView>
</AccountContextProvider>
)
};
export default TrackUploadForm;
useAccountContext is called outside the provider
export default function App() {
return (
<AccountContextProvider>
<Content />
</AccountContextProvider>
);
}
const Content = () => {
const accountContext = useAccountContext();
return (
<div className="App">
<h1>{accountContext.isFormOpen ? "true" : "false"}</h1>
<Button onPress={() => accountContext.toggleForm(false)} mode='outlined'>Cancel</Button>
</div>
);
};
accountContext.toggleForm(false) <-- always false, change it to accountContext.toggleForm(!accountContext.isFormOpen)
Together we have
https://codesandbox.io/s/cranky-panini-yo129

How to test code that uses a custom hook based on useContext with react-testing-library and jest

I've created a custom context hook - and I'm struggling to figure out how to pass values to its provider during testing.
My hook:
import React, { createContext, useContext, useState } from 'react';
const Context = createContext({});
export const ConfigurationProvider = ({ children }) => {
// Use State to keep the values
const [configuration, setConfiguration] = useState({});
// pass the value in provider and return
return (
<Context.Provider
value={{
configuration,
setConfiguration,
}}
>
{children}
</Context.Provider>
);
};
export const useConfigurationContext = () => useContext(Context);
export const { Consumer: ConfigurationConsumer } = Context;
This is how it's used in the application:
function App() {
return (
<ConfigurationProvider>
<div className="app">
<ComponentA />
</div>
</ConfigurationProvider>
);
}
And in ComponentA:
const ComponentA = () => {
// Get configuration
const configuration = useConfigurationContext();
return (
<div>{JSON.stringify(configuration)}</div>
)
}
This all works fine - considered that I'm calling setConfiguration from another component and set an object. Now for the testing part:
import React, { Component, createContext } from 'react';
import { render, waitFor } from '#testing-library/react';
import ComponentA from 'componentA';
const config = {
propertyA: 'hello',
};
test('renders the config', async () => {
const ConfigurationContext = createContext();
const { queryByText } = render(
<ConfigurationContext.Provider value={config}>
<ComponentA />
</ConfigurationContext.Provider>
);
expect(queryByText('hello')).toBeInTheDocument();
});
This doesn't work - I'm expecting the value that I'm sending in would be rendered in the div, but the context is an empty object. What am I doing wrong?
Thanks to Carle B. Navy I got the reason why it doesn't work. For other people two wonder what the solution is I fixed it by doing the following:
In my context hook, I changed the last line to export the provider as well:
export const { Consumer: ConfigConsumer, Provider: ConfigProvider } = Context;
Then in my test case, instead of creating a new context, I import the ConfigProvider at the top, and then:
const { queryByText } = render(
<ConfigProvider value={config}>
<ComponentA />
</ConfigProvider>
);
Thanks for helping me solve this and hope this helps someone else.

converting react class to functional component with refs

I'm trying to use a class example from a stackblitz file and convert it to a functional component.
I don't understand how the ref works, or where the event and args that are being used in the onTyping function are coming from. Can anyone explain where those are defined and how I'd translate this to a functional component?
import { render } from 'react-dom';
import './index.css';
import * as React from 'react';
import { AutoCompleteComponent } from '#syncfusion/ej2-react-dropdowns';
import { SampleBase } from './sample-base';
import * as data from './dataSource.json';
export class Default extends SampleBase {
constructor() {
super(...arguments);
this.temp = 'sportsData';
// define the array of string
this.sportsData = data[this.temp];
}
onTyping(args) {
console.log(event.target.value);
}
render() {
return (<div id='combodefault' className='control-pane'>
<div className='control-section'>
<div className='col-lg-12 control-wrappers'>
<div id='default'>
<AutoCompleteComponent id="games" dataSource={this.sportsData} ref={(AutoComplete) => { this.listObj = AutoComplete; }} placeholder="e.g. Basketball" actionBegin={this.onTyping}/>
</div>
</div>
</div>
</div>);
}
}
render(<Default />, document.getElementById('sample'));
anything in the constructor will need to be translated to useState:
this.temp = 'sportsData';
// define the array of string
this.sportsData = data[this.temp];
becomes:
const[temp, setTemp] = useState('sportsData');
const[sportsData, setSportsData] = useState(data[temp]);
setTemp and setSportsData are functions that you use to set the state variable temp and sportsData respectively. For example, the following will set temp to 'NFSSportsData'.
setTemp('NFLSportsData');
As for the ref, you can use the hook useRef.
const listObj = useRef(null);
for component life cycle method componentDidMount, you can use the following convention.
useEffect(()=>{
// your code
}, [])
the empty bracket [] signifies only to run the code once when the component mounts. If you want to code listen to a state variable, and runs every time the variable changes, you can do the following:
useEffect(()=>{
// your code
}, [sportsData])
This code above will run every time state variable sportsData changes.
I don't think there's a way to extend a functional component like you are doing with SampleBase. Looking at the SampleBase class, it's just running a function in the lifecycle component componentDidMount. You can do something like the following:
rendereComplete() {
/**custom render complete function */
}
useEffect(()=>{
setTimeout(() => {
this.rendereComplete();
},[]);
To tie is all together, you have something like the following:
import './index.css';
import * as React from 'react';
import { AutoCompleteComponent } from '#syncfusion/ej2-react-dropdowns';
import * as data from './dataSource.json';
export const Default = ()=> {
const [temp, setTemp] = React.useState('sportsData');
const [sportsData, setSportsData] = useState(data[this.temp]);
const listObj = useRef(null);
const onTyping = (args)=>{
console.log('arg =', args);
}
const rendereComplete() {
/**custom render complete function */
}
useEffect(()=>{
setTimeout(() => {
rendereComplete();
},[]);
return (<div id='combodefault' className='control-pane'>
<div className='control-section'>
<div className='col-lg-12 control-wrappers'>
<div id='default'>
<AutoCompleteComponent id="games" dataSource={sportsData} ref={(AutoComplete) => { listObj = AutoComplete; }} placeholder="e.g. Basketball" actionBegin={onTyping}/>
</div>
</div>
</div>
</div>);
}

how to set value in hooks

I have a problem with hooks in ReactJS
as you see here i defined a prop that should call from child component
but when i want to change the value by calling change component it doesn't work and my state doesn't set.
can someone help me?
don't forget to read the comments
import React, {useState} from "react";
import Collection from "./Collection";
import ReminderPeriod from "./ReminderPeriod";
function SingleReminderPage() {
const [collection, setCollection] = useState(null);
const setSelectedCollection = (e) => {
setCollection(e);
console.log(e); // returns the true value
console.log(collection); // returns null
}
return(
<div>
<Collection onChoosed={(e) => setSelectedCollection(e)}/>
</div>
)
}
export default SingleReminderPage;
Use setState with a callback function
const setSelectedCollection = (e) => {
setCollection((state)=> {...state, e});
}
setCollection(e) - wont update the state immediately.
I want to Understand SetState and Prevstate in ReactJS
This might help you around, the useEffect will be called on each colletion update
import React, { useState, useEffect } from "react";
import Collection from "./Collection";
import ReminderPeriod from "./ReminderPeriod";
function SingleReminderPage() {
const [collection, setCollection] = useState(null);
useEffect(() => {
console.log(collection)
}, [collection])
return (
<div>
<Collection onChoosed={(e) => setCollection(e)} />
</div>
)
}
export default SingleReminderPage;
it seems like the setCollection is called after the logging action to check something like that you can print the collection value on the component itself
import React, {useState} from "react";
import Collection from "./Collection";
import ReminderPeriod from "./ReminderPeriod";
function SingleReminderPage() {
const [collection, setCollection] = useState(null);
const setSelectedCollection = (e) => {
setCollection(e);
console.log(e); // returns the true value
console.log(collection); // returns null
}
return(
<div>
{collection}
<Collection onChoosed={(e) => setSelectedCollection(e)}/>
</div>
)
}
export default SingleReminderPage;

Resources