I am working on a react Movie app and I want to pass a showModal useState to another component as I have a conditional on the component with my showModal like this:
const [showModal, setShowModal] = useState(false);
return (
<>
{showModal ? (<div>Modal..</div>) : null}
</>
Then on the other component I want to pass the hook to is something like this:
... <button
type="button"
onClick={() => setShowModal(true)}
className="my-4 border text-white py-2 px-4 rounded-3xl border-blue-600 bg-blue-600 shadow-lg"
>
Watch trailer
</button>...
Thanks in advance!
You can totally pass the setShowModal as a props.
const [showModal, setShowModal] = useState(false);
return (
<>
{showModal ? (<Modal setShowModal={setShowModal} />) : null}
</>
Write this hook : const [showModal, setShowModal] = useState(false); in the parent component.
And then pass the function setShowModal in props of your modal componant.
export default function YourModal({setShowModal}){
}
Your modal component should look like this and you can use the function setShowModal inside this component
Well the best approach here is to use react context api , since you don't write your code base and components tree it's very difficult to guess which one belongs to other!
Anyway you could have this kind of context called modalContext :
import React from 'react';
import { useState, createContext, useContext } from 'react';
const ModalContext = createContext(null);
function ModalProvider({ children }) {
const [showModal, setShowModal] = useState(false);
return (
<ModalContext.Provider value={{ showModal, setShowModal }}>
{children}
</ModalContext.Provider>
);
}
function useModal() {
const context = useContext(ModalContext);
if (context === undefined) {
throw new Error('useModal must be used within a ModalProvider');
}
return context;
}
export { ModalProvider, useModal };
and then wrap your application with the provider :
import {ModalProvider} from '...(path to context)/modalContext';
<ModalProvider>
<App />
</ModalProvider>
then where ever you need modalShow or the setter setModalShow you can call it like this easily :
import {useModal} from '..path to /modalContext';
function SomeComponent() {
const {showModal, setShowModal} = useModal();
return (
<>
{showModal ? (
<ChildComponent toggleModal={setShowModal}>Modal..
</ChildComponent>
) : null}
</>
);
}
Related
I'm getting a bad setState() error inside the sidebar bootstrap offcanvas component. It says it can't update the component App() while rendering a different component sidebar() Sorry but I had to delete a large section of my code which was a fetch call. Thanks.
Error: index.js:1 Warning: Cannot update a component (App) while rendering a different component (Sidebar). To locate the bad setState() call inside Sidebar, follow the stack trace as described in
App.js
import React, {useEffect, useState} from 'react';
import './App.css';
import MovieTag from "./components/MovieTag/MovieTag";
import Sidebar from "./components/MovieTag/Sidebar";
interface Movie {
id: number,
poster_path: string,
title: number
}
function App() {
const [movies, setMovies] = useState([]);
const [genre, setGenre] = useState(0);
const [sort, setSort] = useState("popularity.desc");
const [page, setPage] = useState(1);
function GetMovies(genreId: number, sortBy: string, page: number){
setGenre(genreId);
setSort(sortBy);
setPage(page);
return (
<div className={'container'}>
<Sidebar filterByGenre={(genre: number) => {
setGenre(genre);
}}
sortBy={(sort: string) => {
setSort(sort);
}}
pageFilter={(page: number) => {
setPage(page);
}}
/>
<div className={'row'}>
{movies.map((movie: Movie) => {
return (
<MovieTag key={movie.id}
poster={movie.poster_path}
title={movie.title}
/>
)
})}
</div>
</div>
);
}
export default App;
Sidebar.js
function Sidebar(props: any) {
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<React.Fragment>
<Button variant={'secondary'} onClick={handleShow}>Filter</Button>
<Offcanvas show={show} onHide={handleClose}>
<Offcanvas.Header>
<Offcanvas.Title>Filter</Offcanvas.Title>
</Offcanvas.Header>
<Offcanvas.Body>
<DropdownButton title={'Genre'} drop={'end'}>
<Dropdown.Item eventKey={1}><button onClick={props.filterByGenre(28)}>Action</button></Dropdown.Item>
</DropdownButton>
</Offcanvas.Body>
</Offcanvas>
</React.Fragment>
)
}
The problem is on Sidebar component which is calling a setState of its parent component while rendering on <Dropdown.Item eventKey={1}><button onClick={props.filterByGenre(28)}>Action</button></Dropdown.Item>
If you need to call the function onClick, then create an arrow function to it.
onClick={() => props.filterByGenre(28)}
I have created a component, and I want to make it easy to use. I don't wanna call my component like the following way,
<myComponent {...someProps} />
I'm looking for a specific way, I'm still new in React I don't know exactly the name and how can I do it.
is there any way to call my component as a hook or something similar, let's take this component as an example?
export const useComponent = (props) => {
const [show, setShow] = useState('show');
const onShow = (value) => { setShow(value); }
return (
// Content
<div className={show}>
Component
<button onClick={onShow(hide)}>Hide</button>
</div>
);
}
I need to show what inside content using a function, like that
const onShow = useComponent();
//if I want to show it I will call onShow function
<button onClick={onShow('show')}>Show Component</button>
What I want basically is when I clicked on the 'Show Component' button I want to show the useComponent, without calling it inside HTML like .
it's like it gonna be easy for to everyone use my component.
One solution is to use a wrapper component using the Context API.
This is, in my opinion, one of the advanced features of React. Super useful of course, but if you are a beginner, try creating just a component :)
Anyway, you missed in your description, what the custom hook should do. If you need a more precise answer, try giving some more info :)
import React, { useState, createContext, useContext} from 'react';
export const ComponentContext = createContext(null);
/**
* Don't forget to describe your provider
* #param {object} args react args
* #param {JSX.Element | Array<JSX.Element>} args.children children to show
*/
export const ComponentProvider = ({ children }) => {
const [show, setShow] = useState('show');
const onShow = (value) => { setShow(value); }
return (
<ComponentContext.Provider
value={{ show, onShow }}
children={children}
/>
);
};
/**
* Hook used to get the props you defined
* #returns {{show:boolean,onShow:function}}
*/
export const useComponent = () => {
const res = useContext(UserContext);
return res;
};
You can then use your component in the wrapper or App.jsx
import React from 'react';
import { ComponentProvider } from './custom-hooks/ComponentProvider';
function App() {
return (
<ComponentProvider>
<YourComponent />
</ComponentProvider >
);
}
and finally, use the functions you need in the element
import { useComponent } from './custom-hooks/ComponentProvider';
...
const { onShow } = useComponent();
return (
<button onClick={()=>onShow('show')}>Show Component</button>
);
Edited:
I think you're looking for HOC. You can create Higher Order Component using useComponent and serve/provide wherever you need it by following example.
const withComponent = (WrappedComponent) => {
const WithComponent = (props) => {
function useComponent(status, setStatus) {
return (
<div>
{status == "show" && <div>
<p>Inner Contents</p>
<button onClick={() => setStatus("hide")}>Hide Inner Contents</button>
</div>}
</div>
);
}
return <WrappedComponent {...props} useCompoent={useComponent} />;
};
return WithComponent;
};
const App = (props) => {
const { useCompoent } = props;
const [status, setStatus] = useState("hide");
const getUseComponent = useCompoent(status, setStatus);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
{getUseComponent}
<button onClick={() => setStatus("show")}>
Show Component
</button>
</div>
);
};
export default withComponent(App);
You can move withComponent into separate file as well.
I am trying to create a simple quiz app, using react context and multistep form. Here is my code handling form data
import { useState, createContext, useContext } from "react";
export const QuizContext = createContext();
export default function QuizProvider({ children }) {
const [data, setData] = useState({});
const setQuizValues = (values) => {
setData((prevValues) => ({
...prevValues,
...values,
}));
};
return (
<QuizContext.Provider value={{ data, setQuizValues }}>
{children}
</QuizContext.Provider>
);
}
export const useQuizData = () => useContext(QuizContext);
Now, going into my first step component, I get a TypeError: Cannot read property 'setQuizValues' of undefined:
import { Card } from "../../stories/Card";
import { useQuizData } from "../../context/index"
export const TacoCathegories = ({quizStep, prevQuizStep, nextQuizStep}) => {
const { setQuizValues } = useQuizData();
const handleSubmit = (values) => {
setQuizValues(values);
prevQuizStep();
nextQuizStep();
};
return (
<div className="max-w-7xl mx-auto py-24 px-4 bg-white sm:px-6 lg:px-8">
<div className="mt-24 space-y-12 lg:space-y-0 lg:grid lg:grid-cols-3 lg:gap-x-8">
<Card />
</div>
</div>
);
}
I am actually going through the same steps I went through for a similar project.
It looks like the useQuizData hook is returning undefined. This would be the case if you haven't included a provider for the quiz context above the component calling this hook, because the default value for the context is undefined. Try wrapping your component tree inside <QuizProvider> ... </QuizProvider>.
I have one base hook(baseHook.js) which has some functions in it. Using composition I am trying to call those functions in child hook(formHook.js).
baseHook.js is as follow
import React, { Fragment, useEffect, useState} from "react";
import PropTypes from "prop-types";
const BaseHook = ({ ...props }) => {
const [show, setshow] = useState(false);
//when hovered on activeInput -->'activeInput' is declared but its value is never read.
const activeInput = (input) => {
setshow(true);
}
return (
<Fragment>
{props.children}
{show ? <div>
<p>Div is visible</p>
</div> : null}
</Fragment>
);
};
BaseHook.propTypes = {
activeInput:PropTypes.func,
};
export default BaseHook;
Now I am trying to use baseHook.js in another formHook.js where onFocus of input activeInput should get called.
import React, { Fragment, useEffect, useState} from "react";
import BaseHook from "components/BaseHook";
const FormHook = ({ ...props }) => {
return (
<BaseHook>
<Fragment>
<input
title= {"Input"}
onFocus={() => activeInput(InputValue)}
value={InputValue}
className="required-field"
/>
</Fragment>
<BaseHook>
);
};
export default FormHook;
activeInput function is not getting called from baseHook hence not able to setshow(true).
I am able to achieve this with react-class components using inheritance but is there way to call functions in composition in react-hooks?
On function call i was changing the image style using useState hooks
I was sending these property as an props
basically i want to a function which should contain style property for img and pass it to another component as propsstyle = {{opacity: ".3"}}
import React, { useState } from 'react';
import BackgroundImage from '../Image/Homepage/background.png'
const HomePage = () => {
const [modalShow, setModalShow] = useState(false);
const [image, setImage] = useState(BackgroundImage)
return (
<div>
<img src={image} className="first-image" alt="backGroundImage" />
</div>
<Modals
show={modalShow}
onhide={() => setModalShow(false)}
sendImages = {() => setImage( style = {{opacity: ".3"}} )}
/>
)}
this is throwing an error
sendImages = {() => setImage( style = {{opacity: ".3"}} )}
I think this not right approach
It looks like you want to make opacity dynamic, instead you manipulate image src...
import React, { useState } from 'react';
import BackgroundImage from '../Image/Homepage/background.png'
const HomePage = () => {
const [modalShow, setModalShow] = useState(false);
const [image, setImage] = useState(BackgroundImage);
const [opacity, setOpacity] = useState(1);
return (
<>
<div>
<img src={image} className="first-image" style={{opacity}} alt="backGroundImage" />
</div>
<Modals
show={modalShow}
onhide={() => setModalShow(false)}
sendImages = {() => setOpacity(0.3)}
/>
</>
)}
If you only wish to update the style property on function call, you must store the style property in state and not the image. Also the syntax for setImage is incorrect in your code
import React, { useState } from 'react';
import BackgroundImage from '../Image/Homepage/background.png'
const HomePage = () => {
const [modalShow, setModalShow] = useState(false);
const [imageStyle, setImageStyle] = useState({})
return (
<>
<div>
<img src={BackgroundImage} style={imageStyle} className="first-image" alt="backGroundImage" />
</div>
<Modals
show={modalShow}
onhide={() => setModalShow(false)}
sendImages = {() => setImageStyle({opacity: ".3"})}
/>
</>
)}
NOTE: Also please note that state updaters with hooks do not merge the values but override it. So if you wish to update only certain properties make use of state updater callback method to return the merged values yourself