Error when using react context - Cannot destructure property - reactjs

i trying to using Context on my react app
but i got error:
Uncaught TypeError: Cannot destructure property 'selected' of 'Object(...)(...)' as it is undefined.
in my InputsList file
on this line:
const { selected, setSelected } = useContext(ParamsContext);
ParamsContext:
import { createContext } from 'react';
export const ParamsContext = createContext({
selected: [],
setSelected: () => {},
textName: null,
valueName: null
});
InputParams:
import React, { useState } from 'react';
import InputsList from './InputsList';
import { ParamsContext } from './services/ParamsContext';
function InputParams(props) {
const [selected, setSelected] = useState([]);
const textName = "test";
const valueName = "test2";
return (
<div>
<ParamsContext.Provider
selected={selected}
setSelected={setSelected}
textName={textName}
valueName={valueName}>
<InputsList />
</ParamsContext.Provider>
</div>
);
}
export default InputParams;
InputsList:
import React, { useContext } from 'react';
import { ParamsContext } from './services/ParamsContext';
function InputsList(props) {
const { selected, setSelected } = useContext(ParamsContext);
return (
<div>
{selected.length}
</div>
);
}
export default InputsList;
what can i do?

Contexte.Provider accept a value props,
And should be used like:
<ParamsContext.Provider value={{selected, setSelected, textName, valueName}}>
<\ParamsContext.Provider>

Provider accept a value prop, in your case, an object. So it should be:
<ParamsContext.Provider
value={{
selected,
setSelected,
textName,
valueName
}}
>
<InputsList />
</ParamsContext.Provider>
See docs

Related

Problem using React createContext and typescript

I'm working on a simple calculator made on react + typescript using vite. It's my first time using react + typescript so I don't know the right way to use the ContextAPI. I got this error trying to build the aplication with yarn build.
src/components/Screen.tsx:5:11 - error TS2339: Property 'showOnScreen' does not exist on type '{}'.
const { showOnScreen, onDeleteButton } = useContext(AppContext);
AppContext.tsx
import { createContext } from 'react';
export const AppContext = createContext({});
ContextProvider.tsx
import { useState } from 'react';
import { AppContext } from './';
type Props = {
children: React.ReactNode;
};
export const ContextProvider: React.FC<Props> = ({ children }) => {
const [showOnScreen, setShowOnScreen] = useState('0');
const [isLastAResult, setIsLastAResult] = useState(false);
const onDeleteButton = () => {
if (showOnScreen.length === 1 || isLastAResult) {
setShowOnScreen('0');
setIsLastAResult(false);
return;
}
setShowOnScreen(showOnScreen.substring(0, showOnScreen.length - 1));
};
return (
<AppContext.Provider
value={{
onDeleteButton,
showOnScreen,
}}
>
{children}
</AppContext.Provider>
);
};
Screen.tsx
import { useContext } from 'react';
import { AppContext } from '../context';
export const Screen = () => {
const { showOnScreen, onDeleteButton } = useContext(AppContext);
return (
<div className='row bg-white rounded-4'>
<div className='col-12 pe-0 d-flex justify-content-between align-items-center'>
<span className='py-2 fs-4'>{showOnScreen}</span>
<button
className='text-danger rounded-start btn rounded-4 h-100 fs-5'
onClick={onDeleteButton}
>
DEL
</button>
</div>
</div>
);
};
In TypeScript, createContext uses generics ie it is a createContext<T> that automatically adapts to your type when you don't specify anything else. It propagates the type to your variable - AppContext which gets assigned {} as a type.
There are various ways to assign it a type, like:
export const AppContext = createContext({} as { showOnScreen?: bool; });
or
export const AppContext = createContext<{ showOnScreen?: bool; }>({});
or
interface IContext {
showscreen?: bool;
}
export const AppContext = createContext<IContext>({});

How to get a value from code mirror in react js?

I am trying to get a value from the code mirror input field but I don't know why I am not getting the value from code mirror input field
import CodeMirror from "#uiw/react-codemirror";
import { markdown, markdownLanguage } from "#codemirror/lang-markdown";
import { languages } from "#codemirror/language-data";
export default function Editor() {
const [get, set] = useState("");
console.log(get); {/* doesn't display anything */}
return (
<>
<CodeMirror
value={get}
extensions={[
markdown({ base: markdownLanguage, codeLanguages: languages }),
]}
onInput={(e) => set(e.target.value)}
/>
</>
);
}
Try useEffect hook, it calls the function inside whenever variable in second argument array changes, in this specific scenario when get changes.
import CodeMirror from "#uiw/react-codemirror";
import { markdown, markdownLanguage } from "#codemirror/lang-markdown";
import { languages } from "#codemirror/language-data";
import { useEffect } from "react";
export default function Editor() {
const [get, set] = useState("");
useEffect(() => {
console.log(get) //gets called whenever get state changes
}, [get])
return (
<>
<CodeMirror
value={get}
extensions={[
markdown({ base: markdownLanguage, codeLanguages: languages }),
]}
onInput={(e) => set(e.target.value)}
/>
</>
);
}
The onChange handler can output the value as below.
import { markdown, markdownLanguage } from "#codemirror/lang-markdown";
import { languages } from "#codemirror/language-data";
import { useState } from "react";
export default function Editor() {
const [get, set] = useState("");
console.log(get);
return (
<>
<CodeMirror
value={get}
extensions={[
markdown({ base: markdownLanguage, codeLanguages: languages })
]}
onChange={(value) => set(value)}
/>
</>
);
}
Working example

How to update custom hook value

I've started learning typescript and react hooks on my new web project. I have some problem with managing initial value of my custom hook i've made. Cant step over this functionality. How should I update nameInput hook, after data is fetched from my API. For example if I pass custom string as initial value in useTextInput everything is fine, but problem is when im passing undefined first there(when data is fetched from API) and value after is downloaded, and seems like it is not updating.
My question is, how to update in AccountInput inputValue value properly by value coming from API as userAccount.firstName from getUser() method, witch depends to nameInput, witch is not updated after initial value in useTextInput.
Account.tsx
import React, { useEffect, useState} from 'react';
import { connect } from 'react-redux';
import { IApplicationState } from '../../store';
import { actionCreators, reducer, AccountStatusEnum } from '../../store/Account';
import { MDBBtn, MDBInput } from 'mdbreact';
import { AccountInput } from './child-components';
import { useTextInput } from '../../hooks';
type AccountProps = ReturnType<typeof reducer> & typeof actionCreators;
const Account: React.FC<AccountProps> = ({ getUser, resetState, userAccount, status }) => {
const nameInput = useTextInput(userAccount.firstName);
const [isInputInvalid, setIsInputInvalid] = useState<boolean>(false);
useEffect(() => {
getUser();
}, []);
return (
<React.Fragment>
<div className="mt-3 container border border-light">
<h5 className="secondary-heading font-weight-bold pt-3 pl-5">User info</h5>
<div className="row">
<AccountInput
textInput={nameInput}
typeInput="text"
labelInput="Your name & surename"
isInputInvalid={isInputInvalid}
/>
</div>
</div>
</React.Fragment>
);
};
const mapStateToProps = (state: IApplicationState) => state.acc;
export default connect(mapStateToProps, actionCreators)(Account as any);
AccointInput.tsx
import React, { Fragment, useEffect, useState } from 'react';
import { TextInput } from '../../../hooks';
import { MDBInput } from 'mdbreact';
type AccountInputProps = {
readonly textInput: TextInput;
readonly isInputInvalid: boolean;
readonly typeInput: string;
readonly labelInput: string;
};
const AccountInput = React.memo<AccountInputProps>(({ textInput, isInputInvalid, typeInput, labelInput }) => {
const { hasValue, bindToInput } = textInput;
return (
<Fragment>
<div className="col-sm w-100 px-5">
<MDBInput hint={textInput.value} {...bindToInput} type={typeInput} label={labelInput} />
</div>
</Fragment>
);
});
AccountInput.displayName = 'AccountInput';
export default AccountInput;
useTextInput.ts
import { useState, useCallback, useMemo, FormEvent } from 'react';
export type TextInputType = 'text' | 'password';
export type TextInput = {
value: string;
hasValue: boolean;
clear: () => void;
bindToInput: {
value: string;
type: TextInputType;
onChange: (e: FormEvent<HTMLInputElement>) => void;
};
};
export const useTextInput = (initial: string = '', type: TextInputType = 'text'): TextInput => {
const [value, setValue] = useState<string>(initial);
const clear = useCallback((): void => setValue(''), []);
const onChange = useCallback((e: FormEvent<HTMLInputElement>): void => setValue(e.currentTarget.value), []);
return useMemo<TextInput>(
() => ({
value,
clear,
hasValue: !!(value && value.trim()),
bindToInput: {
type,
value,
onChange,
},
}),
[value, type, onChange, clear],
);
};
I don't know for sure how custom hooks handle promises, but normally when you want to pass an async value to useState you do something like this:
const useTextInput = (initial) => {
const [value, setValue] = useState(initial);
useEffect(() => {
setValue(initial);
}, [initial]);
};
You can use useEffect to update your stateful value when initial changes. Would this work in your case?

Getting undefined for useContext() hook when functional Context is used

BookContext.js
import React, { createContext, useState } from 'react'
export const BookContext = createContext()
export default function BookContextProvider(props) {
const [books, setbooks] = useState([
{ title: 'the way of kings' , id:1 },
{ title: 'the name of the wind', id:2 },
{ title: 'the final empire', id:3 }
])
return (
<BookContext.Provider value={{books}}>
{ props.children }
</BookContext.Provider>
)
}
Booklist.js
import React, { useContext } from 'react'
import { ThemeContext } from '../contexts/ThemeContext'
import { BookContext } from '../contexts/BookContext'
export default function Booklist() {
// Object destructuring
const { isLightTheme, light, dark } = useContext(ThemeContext)
const theme = isLightTheme ? light : dark
const { books } = useContext(BookContext)
return (
<div className="book-list" style={{color: theme.syntax, background: theme.bg}}>
<ul>
<li style={{background: theme.ui}}>the way of kings</li>
<li style={{background: theme.ui}}>the name of the wind</li>
<li style={{background: theme.ui}}>the final empire</li>
</ul>
</div>
)
}
Getting undefined for useContext().
When destructered the context it throws typeerror.
TypeError: Cannot destructure property 'books' of 'Object(...)(...)' as it is undefined.
How can I solve this?
By default the context has an undefined value. You can provide an initial value to avoid this issue when the component is mounted outside the provider. The useContext hook will use the default context value when no provider is found.
export const BookContext = createContext({ books: [] });

TypeError: Invalid attempt to destructure non-iterable instance with CONTEXT API value state on reactjs

Hi i try to make a simply state managment with context api and hooks on react js.
I have a context named UIcontext and one component container called home.
when i do the new instance of useUIcontextStateValue in home, the app crash and throw this error
TypeError: Invalid attempt to destructure non-iterable instance
I have no idea this is happen, in other app i have the same code and work, please help friend.
this is the context
import React , {createContext, useContext, useReducer} from 'react';
export const UIcontext = createContext();
export const UIcontextProvider = ({reducer, initialState, children}) => {
return (
<UIcontext.Provider>
{children}
</UIcontext.Provider>
);
};
export const useUIcontextStateValue = () => useContext(UIcontext);
and this is the component where i use it
import React, {useEffect, useState} from 'react';
import { UIcontextProvider, useUIcontextStateValue } from '../../Store/UiContext';
import { Header, Logo, LandingModule, Menu } from '../../Components';
const Home = props => {
const [showHeader, setShowHeader] = useState(false);
const [menuOpen, setMenuOpen] = useState(false);
const [{ submenu }, dispatch] = useUIcontextStateValue();
const initialState = {
submenu: false
};
const reducer = (state, action) => {
switch (action.type) {
case 'toogleSubmenu':
return {
...state,
submenu: action.submenuState
};
default:
return state;
}
};
useEffect(()=>{
window.onscroll = function() {
if(window.pageYOffset === 0) {
setShowHeader(false);
} else {
setShowHeader(true);
}
}
});
const toogleMenu = () => {
console.log("ANTES", menuOpen);
setMenuOpen(!menuOpen);
console.log("DESPUES", menuOpen);
};
return (
<UIcontextProvider>
<section className="home" >
<Header show={showHeader}/>
<Menu open={menuOpen}/>
<section name="landing" className="home__landing" >
<Logo/>
<div className="home__landing__container-text">
<h1>Welcome</h1>
<div>
<h5><span>TO ADAM'S GRAY</span><br></br> WEBSITE</h5>
<div></div>
</div>
</div>
</section>
<LandingModule
title="HELLA SLINGSHOTS"
subtitle="In this project I make over 35,000 slingshot each year in a variety of colors and designs."
titleBg={hellaBG}
productBg={hellaProductBG}
color='#00c691'
/>
<LandingModule
title="BICYCLE BOLTS"
subtitle="Here i make and sell metric security bolts to help
keep components on your bicycle safe from
opportunistic thievery."
titleBg={bicycleBG}
productBg={bicycleProductBG}
color='#6140FF'
/>
<LandingModule
title="SURF BRAIN"
subtitle="In this project I make over 35,000 slingshot each year in a variety of colors and designs."
titleBg={hellaBG}
productBg={hellaProductBG}
color='#2AD9B1'
/>
</section>
</UIcontextProvider>
);
};
export default Home;

Resources