I am trying to pass a search filter function into my search bar components. But i keep getting this error TypeError: Cannot read property 'search' of undefined
the search function is not recognized my context file is here
https://github.com/CodingOni/Ecommerce-Store/blob/master/src/context.js
import React, { useContext, useEffect, useRef } from 'react';
import ProductContext from '../../src/context';
const ProductFilter = () => {
const productConsumer = useContext(ProductContext);
const text = useRef('');
const { search, searchResults } = productConsumer;
useEffect(() => {
console.log(` product context ${productConsumer}`)
});
const onChange = e => {
if (text.current.value !== '') {
search(e.target.value);
} else {
}
};
return (
<form>
<input
ref={text}
type="text"
placeholder="Search Keywords..."
onChange={onChange}
id=""
/>
</form>
);
};
export default ProductFilter;
useContext accepts a context object (the value returned from
React.createContext) and returns the current context value for that
context.
You pass react component to useContext which is default export from '../../src/context'.
In context file you need export PoductContext
export { ProductProvider, ProductConsumer, ProductContext };
..
import {ProductContext} from '../../src/context';
Related
I have a context, I import it into my functional component:
import { TaskContexts } from "../../../contexts";
The context stores data and functions.
The data comes from the context and is displayed on the site.
const {
editTodo,
setEditID,
toggleTodoCompletion,
editID,
editTodoHandler,
removeTodo,
state,
text,
isEditError,
} = useContext(TaskContexts);
But!
<button onClick={() => editTodo(todo.id)}>
<img src={editIcon} alt="edit button"></img>
</button>
When I try to call the editTodo function, It fails with the following error:
Uncaught TypeError: editTodo is not a function
How to fix this error?
UPD.
Full component code
import React, { useState } from 'react';
import ACTION_TYPES from '../ToDo/reducer/actionTypes';
import RenderedTable from './RenderedTable';
import styles from './TaskList.module.scss';
import allIcon from '../../icons/all.svg';
import completedIcon from '../../icons/completed.svg';
import notCompletedIcon from '../../icons/notCompleted.svg';
import mona from '../../icons/mona.gif';
import { TODO_TASK_CHEMA } from '../../utils/validationSchemas';
import { TaskContexts } from '../../contexts';
const TaskList = props => {
const {
reducerData: [state, dispatch],
} = props;
const [editID, setEditID] = useState(null);
const [editText, setEditText] = useState(null);
const [isEditError, setIsEditError] = useState(false);
const [mode, setMode] = useState('All');
const removeTodo = id => {
dispatch({ type: ACTION_TYPES.REMOVE, id });
};
const toggleTodoCompletion = id => {
dispatch({ type: ACTION_TYPES.TOGGLE, id });
};
const editTodo = id => {
const text = editText.trim();
try {
TODO_TASK_CHEMA.validateSync({ text });
} catch (e) {
setIsEditError(true);
throw new Error(e);
}
setIsEditError(false);
setEditID(null);
dispatch({ type: ACTION_TYPES.EDIT, id, text });
setEditText(null);
};
const editTodoHandler = ({ target: { value } }) => {
setEditText(value);
};
const contextsValues = {
editID,
setEditID,
editText,
setEditText,
isEditError,
setIsEditError,
mode,
setMode,
state
};
return (
<TaskContexts.Provider value={contextsValues}>
<div className={styles.container}>
{state.todos.length === 0 ? (
<div>
<h2 className={styles.noTask}>No tasks =)</h2>
<img src={mona} alt='mona gif' />
</div>
) : (
<>
<button
className={styles.section}
onClick={() => {
setMode('All');
}}
>
<img src={allIcon} alt='all button' />- All
</button>
<button
className={styles.section}
onClick={() => {
setMode('Completed');
}}
>
<img src={completedIcon} alt='completed button' />- Completed
</button>
<button
className={styles.section}
onClick={() => {
setMode('NotCompleted');
}}
>
<img src={notCompletedIcon} alt='not completed button' />- Not
completed
</button>
<RenderedTable
editTodo={editTodo}
setEditID={setEditID}
toggleTodoCompletion={toggleTodoCompletion}
editID={editID}
editTodoHandler={editTodoHandler}
removeTodo={removeTodo}
state={state}
mode={mode}
isEditError={isEditError}
/>
</>
)}
</div>
</TaskContexts.Provider>
);
};
export default TaskList;
All functions on this component do not work. But these are functions. I don't understand why React doesn't think so.
You need to do 3 things to pass the context values successfully:
Place the Context Provider at least one level above the Consuming Component.
Create Your Context, Declare all variables and methods within the Context, and Export the Context's Provider after passing the value Prop.
Consume the Context Values by importing the useContext() hook in TaskList.jsx/TaskList.js and calling it on the Provider object.
Place the Context Provider at least one level above the Consuming Component
The reason JavaScript thinks editTodo is not a function or is undefined is that you are trying to consume it in React within the <TaskList/> component before it (<TaskList/>) is even made aware of the context. By the time <TaskList/> has been rendered by React, it is too late to pass any context values. So we need to place the context, somewhere higher up the component tree where React will be made aware of the context and its values ahead of time before rendering (and passing the context values to) child components down the tree.
To fix this, place the context provider wrapper at least one level above the component that is consuming the values of the context provider. If more than one component needs values from the provider, the best place to place the provider wrapper would be in your App.jsx/App.js or your index.jsx/index.js file.
Inside App.jsx/App.js:
import { TaskProvider } from 'path/to/context';
function App() {
<TaskProvider>
{/* All your code/rendered elements/rendered route elements go here */}
</TaskProvider>
}
export default App;
or Inside index.jsx/index.js:
import React from "react";
import ReactDOM from "react-dom";
import { ToastProvider } from "path/to/context";
import "./index.css";
import App from "./App";
ReactDOM.render(
<React.StrictMode>
<ToastProvider>
<App />
</ToastProvider>
</React.StrictMode>,
document.getElementById("root")
);
I'll show you a better way to pass those context values.
Create Your Context, Declare all variables and methods within the Context, and Export the Context's Provider after passing the value Prop:
Inside TaskContexts.jsx/TaskContexts.js:
import {useContext, createContext } from "react";
// ...All your necessary imports
// Create context first
const TaskContexts = createContext();
export const TaskProvider = ({ children }) => {
const [editID, setEditID] = useState(null);
const [editText, setEditText] = useState(null);
const [isEditError, setIsEditError] = useState(false);
const [mode, setMode] = useState('All');
const removeTodo = id => {
dispatch({ type: ACTION_TYPES.REMOVE, id });
};
const toggleTodoCompletion = id => {
dispatch({ type: ACTION_TYPES.TOGGLE, id });
};
const editTodo = id => {
const text = editText.trim();
try {
TODO_TASK_CHEMA.validateSync({ text });
} catch (e) {
setIsEditError(true);
throw new Error(e);
}
setIsEditError(false);
setEditID(null);
dispatch({ type: ACTION_TYPES.EDIT, id, text });
setEditText(null);
};
// ...and the rest of the methods
// Prepare your contextValues object here
const contextValues = {
editID,
setEditID,
// ...and the rest
};
// Notice that we have called the provider here
// so that we don't have to do it within the `App.jsx` or `index.jsx`.
// We have also passed the default values here so we can that
// we don't have to export them and pass them in `App.jsx`.
// We used component composition to create a `hole` where the rest of
// our app, i.e, `{children}` will go in and returned the
// composed component from here, i.e, `<TaskProvider/>`.
// This is so that all the preparation of the context Provider object
// gets done in one file.
return (<TaskContexts.Provider value={contextValues}>
{children}
</TaskContexts.Provider>);
};
// Now, use the context, we will export it in a function called `useTask()`
// so that we don't have to call `useContext(TaskContexts)` every time we need values from the context.
// This function will call `useContext()` for us and return the values
// in the provider available as long as we wrap our app components
// with the provider (which we have already done).
export function useTask() {
return useContext(TaskContexts);
}
Consume the Context Values by importing the useContext() hook in TaskList.jsx/TaskList.js and calling it on the Provider object.
Since we've already called useContext on the provider object, we just need to import useTask() from earlier in TaskList.jsx, run it and it will return the contextValues object which we can destructure.
import React, { useState } from 'react';
import ACTION_TYPES from '../ToDo/reducer/actionTypes';
import RenderedTable from './RenderedTable';
import styles from './TaskList.module.scss';
import allIcon from '../../icons/all.svg';
import completedIcon from '../../icons/completed.svg';
import notCompletedIcon from '../../icons/notCompleted.svg';
import mona from '../../icons/mona.gif';
import { TODO_TASK_CHEMA } from '../../utils/validationSchemas';
// Import `useTask` only.
import { useTask } from '../../contexts';
const TaskList = props => {
// Values from context
const {editID, setEditID,...} = useTask();
const {
reducerData: [state, dispatch],
} = props;
const [editID, setEditID] = useState(null);
const [editText, setEditText] = useState(null);
const [isEditError, setIsEditError] = useState(false);
const [mode, setMode] = useState('All');
const removeTodo = id => {
dispatch({ type: ACTION_TYPES.REMOVE, id });
};
const toggleTodoCompletion = id => {
dispatch({ type: ACTION_TYPES.TOGGLE, id });
};
const editTodo = id => {
const text = editText.trim();
try {
TODO_TASK_CHEMA.validateSync({ text });
} catch (e) {
setIsEditError(true);
throw new Error(e);
}
setIsEditError(false);
setEditID(null);
dispatch({ type: ACTION_TYPES.EDIT, id, text });
setEditText(null);
};
const editTodoHandler = ({ target: { value } }) => {
setEditText(value);
};
return (
<div className={styles.container}>
{/*...everything else */}
<RenderedTable
editTodo={editTodo}
setEditID={setEditID}
toggleTodoCompletion={toggleTodoCompletion}
editID={editID}
editTodoHandler={editTodoHandler}
removeTodo={removeTodo}
state={state}
mode={mode}
isEditError={isEditError}
/>
</>
)}
</div>
);
};
export default TaskList;
In summary, scope everything about the context object to its own component, within its own file, export it and wrap all the children components in the root component (or wrap the root component itself), and call useContext() on the provider object in the component that needs the context values.
State returned from custom hook is undefined. Why is it undefined I do not understand, in the form below I am trying to get states from the custom useInput hook but they are undefined, what is the problem here?
Custom hook
import {useState} from 'react';
export default function useInput (checkValidity){
const [value,setValue] = useState('');
const[isTouched,setIstouched]= useState(false);
let isThisInValid = !checkValidity(value) && isTouched;
var changeValidity = (event)=>{
setValue(event.target.value);
}
var submitTheValue = (event)=>{
event.preventDefault();
setIstouched(true);
}
return {
isThisInValid :isThisInValid,
changeValidity:changeValidity,
submitTheValue:submitTheValue,
}
}
Form in which i am trying to get value from custom hook
import React from 'react'
import Button from '../Button/Button.js'
import {useState,useRef} from 'react'
import Style from '../Input.module.css'
import useInput from '../hooks/useInput'
function BasicInput (props){
const {validityofInput, //trying to get state from custom hook
changeInputValidity,
submitTheValue,
}
= useInput((value) => value.trim()=='');
const {validityofEmail,
changeEmailValidity,
submitTheSecondValue,
}
= useInput((value) => value.includes('#'));
console.log(validityofEmail) //undefined
console.log(validityofInput) //undefined
return <>
<form className = {Style.form}
onSubmit = {submitTheValue}
noValidate
>
<div>Name</div>
<input
id="input"
className = { validityofInput && Style.wrong}
onChange = {
changeInputValidity
}/>
<div>Email</div>
<input
type = "email"
id="input"
className = {validityofEmail && Style.wrong}
onChange = {
changeEmailValidity
}/>
<Button name = "CLICK ME"> </Button>
</form>
</>
}
export default BasicInput;
You are returning an object in the hook with these properties:
return {
isThisInValid :isThisInValid,
changeValidity:changeValidity,
submitTheValue:submitTheValue,
}
and you are destructing from the hook these properties:
const {validityofInput, // notice here it must be "isThisInValid"
changeInputValidity, // and here must be "changeValidity"
submitTheValue,
}
You can rename the properties like this if you want:
const {isThisInValid: validityofInput,
changeValidity:changeInputValidity,
submitTheValue,
}
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
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;
I am working on ReactJS modal dialog and bind the values from redux slice through the useSelector hook. Currently I have two functions which are already dispatching using useDispatch hook and setting the props with 2 functions(onCancelHandler, submitHandler). Here I need to keep one more field which is string value(userName) and tried to keep that and usig the string value approvedUser in DeleteUserModalContent through the props. Initially I am able to get the value from props in DeleteUserModalContent
component but when submitHandler is executed the following error is occured.
Can't read property 'userName' which is undefined
Error at this line:
const approvedUser: string = selectedUser.userName;
Can any one tell me what is wrong here?
Thanks in Advance
Code Snippet:
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Modal } from '#material-ui/core';
import { AppState } from 'store/rootReducer';
import { hideModal } from 'store/common/modalSlice';
import { submitAction } from 'store/user-actions';
import { DeleteUserModalContent } from './DeleteUserModalContent';
export const DeleteUserModal: React.FC<{}> = () => {
const dispatch = useDispatch();
const selectedUser = useSelector((state: AppState) => {
const selectedUserId =
state.selectUserSlice.selectedUsers[0];
return state.userState[selectedUserId];
});
const onCancelHandler = () => {
dispatch(hideModal());
};
const submitHandler = () => {
dispatch(
submitAction(selectedUser.userName)
);
};
const approvedUser: string = selectedUser.userName;
console.log(selectedUser.userName);
const props = {
onResetHandler,
submitHandler,
approvedUser
};
return (
<Modal>
<>
<DeleteUserModalContent {...props} />
</>
</Modal>
);
};
When we use the returned value from the useSelector hook and use the same in other component DeleteUserModalContent by setting into props. Here we are able to use the approvedUser value initially but when submitHandler function is dispatched selectedUser.userName value becomes undefined, So we can put the condition check below:
const approvedUser: string = selectedUser?.userName
to avoid the above mentioned error.