I'm finding I'm having to duplicate the same function over again when I could write one function that handles different parameters. I can't get it to work so I wondered if someone could point me in the right direction? Below shows only 2 functions but in reality I've got many that all do the same thing.
import React, {useState} from "react"
const Section = ({ children }) => {
return (
<>
<Wrapper children = {children} />
</>
);
};
const HandlePages = () => {
const [showPageFooDialogue, setShowPageFooDialogue] = useState(false);
const [showPageBarDialogue, setShowPageBarDialogue] = useState(false);
const [currentDialogue, setCurrentDialogue] = useState(0);
{showPageFooDialogue && (
<Section>
<Headers heading = {"Foo Title"} currentDialogue = {currentDialogue} pages = {fooContents.length} />
{fooContents[currentDialogue]}
</Section>
)}
)
{showPageBarDialogue && (
<Section>
<Headers heading = {"Bar Title"} currentDialogue = {currentDialogue} pages = {barContents.length} />
{barContents[currentDialogue]}
</Section>
)}
)
}
const fooContents = [
//Lots of functionality specific to foo listed as the children of this function
];
const barContents = [
//Lots of functionality specific to bar listed as the children of this function
];
return (
<button onClick={() => setShowPageFooDialogue(true)}>Click for Page Foo</button>
<button onClick={() => setShowPageBarDialogue(true)}>Click for Page Bar</button>
)
}
export default HandlePages
Basically where I've got
const [showPageFooDialogue, setShowPageFooDialogue] = useState(false);
const [showPageBarDialogue, setShowPageBarDialogue] = useState(false);
I need just one function such as this but somehow pass 2 parameters to it:
const [showPageGenericDialogue, setShowPageGenericDialogue] = useState(false);
and where I've got:
{showPageFooDialogue && (
<Section>
<Headers heading = {"Foo Title"} currentDialogue = {currentDialogue} pages = {fooContents.length} />
{fooContents[currentDialogue]}
</Section>
)}
)
{showPageBarDialogue && (
<Section>
<Headers heading = {"Bar Title"} currentDialogue = {currentDialogue} pages = {fooContents.length} />
{barContents[currentDialogue]}
</Section>
)}
)
}
I need just one function with 2 parameters for "Foo or Bar Title" (param1) and fooContents or barContents (param2):
{showPageGenericDialogue && (
<Section>
<Headers heading = {param1} currentDialogue = {currentDialogue} pages = {param2.length} />
{param2[currentDialogue]}
</Section>
)}
)
}
And then finally the buttons:
<button onClick={() => setShowPageFooDialogue(true)}>Click for Page Foo</button>
<button onClick={() => setShowPageBarDialogue(true)}>Click for Page Bar</button>
should just pass the parameters something like:
<button onClick={() => setShowPageGenericDialogue(true, fooParam1, fooParam2)}>Click for Page Foo</button>
<button onClick={() => setShowPageGenericDialogue(true, barParam1, barParam2)}>Click for Page Bar</button>
I've looked at various solutions but due to my limitations, I cannot apply them to this.
Any ideas?
Thanks.
You can use reducer:
https://reactjs.org/docs/hooks-reference.html#usereducer
It is a simple example, you can modify as per your needs.
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
Or Take a look at react-redux.
EDIT
If you need functionality like you asked one function with multiple parameter
you can try to give a default state to your useState hook:
const [myFooBar, setFoo] = useState(
{
'foo': false,
'bar': false
}
);
Changing the values:
setFoo(
prevState => ({
...prevState,
'foo': true
})
);
setFoo(
prevState => ({
...prevState,
'bar': true
})
);
Access it like this:
myFooBar.foo
myFooBar.bar
Related
I'm new to React.js.
and I tried to use a function that has boolean, and I want to reuse just one function to another button.
so I tried like this:
function App() {
const [show, setShow] = useState(false);
const [show1, setShow1] = useState(false);
const [show2, setShow2] = useState(false);
const handleOnClick = () => {
setShow(true);
setShow1(false);
setShow2(false);
};
const handleOnClick1 = () => {
setShow1(true);
setShow(false);
setShow2(false);
};
const handleOnClick2 = () => {
setShow2(true);
setShow(false);
setShow1(false);
};
const panels = ["1+3", "2+2", "3+1"];
const getPanel = e => {
switch (e) {
case "1+3":
handleOnClick();
break;
case "2+2":
handleOnClick1();
break;
case "3+1":
handleOnClick2();
break;
default:
break;
}
};
const panelList = panels.map(panel => (
<div onClick={() => getPanel(panel)}>
<h1>Panel{panel}</h1>
</div>
));
return (
<div>
<Main>{panelList}</Main>
{show && <One />}
{show1 && <Two />}
{show2 && <Three />}
</div>
the question is
How can i use one useState instead of making another one ??
If you're always just going to have one button showing, you can go ahead and just maintain the ID of that button in state rather than a bunch of boolean states.
Furthermore, it might be nice to make just one click handler. You could do this a bunch of ways, such as using the clicked button's ID, or you could do a higher-order function and pass the clicked ID directly. Here is the latter approach:
function App() {
const [show, setShow] = useState();
const handleOnClick = (id) => () => {
setShow(id);
}
return (
<>
<button onClick={handleClick(1)}>Show 1</button>
<button onClick={handleClick(2)}>Show 2</button>
<button onClick={handleClick(3)}>Show 3</button>
{show === 1 && <One />}
{show === 2 && <Two />}
{show === 3 && <Three />}
</>
);
}
I'm having trouble accessing the state in one of my components. I have a component where a user adds a name and a weight and then submits it. They are then redirected to the Home Page. What I want to happen is for the name that was inputed to be displayed on the Home Page. I can see the state updating, but I can't figure out how to access the state and have the name show on the Home Page. Any help would be appreciated.
Here is my Home Page component:
const HomePage = () => {
const classes = useStyles();
const name = useSelector(state => state.move.name);
const displayMovementButtons = () => {
if (name) {
return (
<Button
className={classes.movementButtons}
onClick={() => history.push('/movement/:id')}
>
<div className={classes.movementName} >{name}</div>
</Button>
)
};
return <div className={classes.noMovementsMessage} >Click add button to begin</div>
};
return (
<div className={classes.homePageContent} >
<Header title={"Home Page" }/>
<div>{displayMovementButtons()}</div>
<div className={classes.fabDiv}>
<Fab
className={classes.fab}
onClick={() => history.push(`/add`)}>
<AddIcon />
</Fab>
</div>
</div>
);
};
const mapStateToProps = (state) => {
return {
name: state.move.name,
}
};
const withConnect = connect(
mapStateToProps,
);
export default compose(withConnect)(HomePage);
Here is my reducer, where I think the problem is:
const initialState = []
const addMovementReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_MOVEMENT:
return [ ...state, {name: action.name, weight: action.weight} ]
default:
return state;
}
};
export default addMovementReducer;
Here is a screenshot showing the state (note: I added multiple names and weights, I would eventually like each 'name' to appear on the Home Page):
Your move branch of state is an array. You can't access the name by state.move.name. Instead of this you can get an array of movements from redux store and render them with Array.map() method.
const MovementButtons = ({ movements }) => {
return (
<div>
{
movements.map(({ name, weight }) => {
if (name) {
<Button
className={classes.movementButtons}
onClick={() => history.push('/movement/:id')}
key={name}
>
<div className={classes.movementName}>{name}</div>
</Button>
}
return (
<div className={classes.noMovementsMessage}>Click add button to begin</div>
)
})
}
</div>
);
}
const HomePage = () => {
const classes = useStyles();
const movements = useSelector(state => state.move);
return (
<div className={classes.homePageContent} >
<Header title={"Home Page" }/>
<MovementButtons movements={movements} />
<div className={classes.fabDiv}>
<Fab
className={classes.fab}
onClick={() => history.push(`/add`)}>
<AddIcon />
</Fab>
</div>
</div>
);
};
const mapStateToProps = (state) => {
return {
name: state.move.name,
}
};
const withConnect = connect(
mapStateToProps,
);
I want to use Switch case in React js functional component.
I want an efficient way of using it.
I have this code snippet:
import React, { useState } from 'react';
import FormUserDetails from './FormUserDetails';
function UserForm() {
const [step, setStep] = useState(1);
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const nextStep = () => {
setStep(prevState => prevState + 1)
}
const previousStep = () => {
setStep(prevState => prevState - 1)
}
switch (step) {
case 1:
return (
<div>
<FormUserDetails
/>
<button onClick={() => nextStep()}>
next
</button>
<button onClick={() => previousStep()}>
previous
</button>
</div>
)
case 2:
return (
<div>
<h1>{step}</h1>
<button onClick={() => nextStep()}>
next
</button>
<button onClick={() => previousStep()}>
previous
</button>
</div>
)
default:
return (
<div>
<h1>Final</h1>
</div>
)
}
}
export default UserForm
This code is working fine.
However, in the return
<button onClick={() => nextStep()}>
next
</button>
<button onClick={() => previousStep()}>
previous
</button>
I am repeating this code. I just want to know an efficient code structure to reuse these lines with every switch case.
You can apply a little more code refactoring, factoring the buttons into a reusable component.
const StepButtons = () => (
<Fragment>
<button onClick={nextStep}>next</button>
<button onClick={previousStep}>previous</button>
</Fragment>
);
Result
import React, { Fragment, useState } from 'react';
import FormUserDetails from './FormUserDetails';
function UserForm() {
const [step, setStep] = useState(1);
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const nextStep = () => {
setStep(prevState => prevState + 1);
};
const previousStep = () => {
setStep(prevState => prevState - 1);
};
const StepButtons = () => (
<Fragment>
<button onClick={nextStep}>next</button>
<button onClick={previousStep}>previous</button>
</Fragment>
);
switch (step) {
case 1:
return (
<div>
<FormUserDetails />
<StepButtons />
</div>
);
case 2:
return (
<div>
<h1>{step}</h1>
<StepButtons />
</div>
);
default:
return (
<div>
<h1>Final</h1>
</div>
);
}
}
export default UserForm
You can create a new component in a different file (if you want to distinguish this component of course)
import React from 'react'
export default function NavigationButtons(props) {
return (
<>
<button onClick={props.nextStep}>
next
</button>
<button onClick={props.previousStep}>
previous
</button>
</>
)}
and then use it like this
<NavigationButtons nextStep={nextStep} previousStep={previousStep}/>
App won't re render different data on state change.
State does change in the dev tools but doesn't show on page.
Using button to filter.
export const StoriesPage = () => {
const [storyIds, setStoryIds] = useState([]);
const [storyUrl, setStoryUrl] = useState('top');
useEffect(() => {
let url = baseUrl;
storyUrl === 'top'
? (url += 'topstories.json')
: (url += 'newstories.json');
getStoryIds(url).then(data => setStoryIds(data));
}, [storyUrl]);
return (
<div>
<div>
<button onClick={() => setStoryUrl('new')}>New</button>
<button onClick={() => setStoryUrl('top')}>Top</button>
</div>
{storyIds.map((storyId, index) => (
<Story key={index} storyId={storyId} />
))}
</div>
);
};
Added a function that clears storyIds before setStoryUrl
const handleFilter = tag => {
setStoryIds([]);
setStoryUrl(tag);
};
Editing my question to make it a bit clearer
I don’t want the button to re-rendering when I type in the field and when I click on the button I want to update a state object
Here I have 2 components
const mainState = {
title: '',
};
const ButtonComponent = ({ confirmTitleName }) => {
return (
<>
<TestReRender label={'Button Container'}/>
<button style={{backgroundColor: 'red', outline: 'none'}} onClick={() => confirmTitleName('confirmTitleName >>>')}>CLICK ME</button>
</>
)
};
const InputComponent = ({ state, setState }) => {
return (
<>
<TestReRender label={'Input Container'}/>
<input
type="text"
value={state}
onChange={(e) => setState(e.target.value)}
/>
</>
)
};
Then I have created a component made up of the previous two
const InputAndButtonComponent = memo(({ confirmTitleName }) => {
const [state, setState] = useState('');
const Btn = () => <ButtonComponent confirmTitleName={() => confirmTitleName(state)}/>;
return (
<>
<InputComponent state={state} setState={setState} />
<Btn />
</>
)
});
The last component InputAndButtonComponent is then imported in the Main component
const Main = () => {
const [confirmTitle, setConfirmTitle] = useState(mainState);
const confirmTitleName = useCallback((value) => {
setConfirmTitle((prevState) => (
{
...prevState,
title: value
}
))
}, []);
return (
<main className={styles.CreateWorkoutContainer}>
<>
<TestReRender label={'Main Container'}/>
<div>
<InputAndButtonComponent confirmTitleName={confirmTitleName} />
</div>
</>
</main>
)
};
Now the problem is that when I write the component InputAndButtonComponent as follow it re-renders when I type in the input field
const InputAndButtonComponent = memo(({ confirmTitleName }) => {
const [state, setState] = useState('');
return (
<>
<InputComponent state={state} setState={setState} />
// This re-renders when typing
<ButtonComponent confirmTitleName={() => confirmTitleName(state)}/>;
</>
)
});
But the original version does not re-render when I type in the field
const InputAndButtonComponent = memo(({ confirmTitleName }) => {
const [state, setState] = useState('');
// This makes the <Btn /> below not re-rendering. I don't understand why
const Btn = () => <ButtonComponent confirmTitleName={() => confirmTitleName(state)}/>;
return (
<>
<InputComponent state={state} setState={setState} />
// This does not re-render
<Btn />
</>
)
});