How to declare default props in react functional component - reactjs

I want to declare default props in a functional component
normally we would do this
function Body() {
static defaultProps = {
counter: 0
}
return (
<div>
body
</div>
);
}
this obviously doesn't work

This is usually called a functional component, not a hook component.
For the defaultProps you can do it like this:
const Body = ({ counter }) => {
return (
<div>
{counter}
</div>
);
}
Body.defaultProps = {
counter: 0
}

function Body({counter=0}) {
return (
<div>
body
</div>
);
}
counter now has initial value of 0

You can do that simply like this
const Body = (props) => {
const {foo = 'defaultValue'} = props;
return <div>{foo}</div> // It will show defaultValue if props foo is undefined
}

Hooks are just regular functions, you can define default values the same way as you define them in regular functions
function useNameHook(initialName = "Asaf") {
const [name, setName] = useState(initialName);
// you can return here whatever you want, just the name,
// just the setter or both
return name;
}
function Comp({ name }) {
const myName = useNameHook(name);
return <h1>Hello {myName}</h1>;
}
function App() {
return (
<div className="App">
{/* without name prop */}
<Comp />
{/* with name prop */}
<Comp name="angry kiwi" />
</div>
);
}

You can declare defaultProps outside the functional component. In your case it would be something like this -
function Body(props) {
return (
<div>
{props.counter}
</div>
);
}
Body.defaultProps = {
counter: 0
}

This is for hooks but is not intended for functional components.
const defaultProps = {
position: "end"
};
const Dropdown = (props) => {
// The order of arguments is important.
props = Object.assign({}, defaultProps, props);
...
}

Related

How to pass component constructor in React props?

How to pass component constructor in React props?
I'm a beginner for React. So below example may make it clear what I am trying to achieve.
Example:
PorkComponent:
const PorkComponent = ({ children, variant }: PorkComponentProps) => {
return (
<motion.div>
...
</motion.div>
);
};
DuckComponent:
const DuckComponent = ({ children, variant }: DuckComponentProps) => {
return (
<motion.div>
...
</motion.div>
);
};
The Lunch component will contains PorkComponent or DuckComponent Based on LunchProps.meatType.
type LunchProps = {
meatType: React.ComponentType;
};
const Lunch = (props: LunchProps) => {
return (
<props.meatType> // Here it will return PorkComponent or DuckComponent based on what props.meatType.
);
}
Sure I can use something like:
const Lunch = (props: LunchProps) => {
return (
if (props.isPork) {
< PorkComponent >
} else(props.isDuck) {
< DuckComponent >
}
....
);
}
But I don't want to have multiple IFELSE check in Lunch component. Instead, I want the caller to specify the "meat type", like <Lunch meatType= PorkComponent>.
I recently saw this someone shared on Twitter,
const MeatComponents = {
'pork': <PorkComponent />
'duck': <DuckComponent />
}
const Lunch = (props) => {
return MeatComponent[props.meatType]
}
// usage
<Lunch meatType='pork' />
<Lunch meatType='duck' />
Or you could just use,
const Lunch = ({ children }) => {
return <>{children}</>
}
// usage
<Lunch>
<PorkComponent />
</Lunch>
You can simply write :
const Lunch = (props: LunchProps) => <props.meatType />;
<Lunch meatType={DuckComponent} />
Here is an example on stackblitz
You can pass the rendered component directly instead:
// in Lunch component
return (<div>{props.meatType}</div>);
<Lunch meatType={<PorkComponent />} />
Playground
If you just want to return the given component, simply use:
return props.meatType;
you can pass component functions as props. You almost got the answer.
const Lunch = (props) => {
return (
// React.createElement(props.meatType, {}, null)
<props.meatType /> // this is valid, I have tested before replying.
);
}
// then
<Lunch meatType={PorkComponent} />
// or
<Lunch meatType={DuckComponent} />
// even this works and renders a div
<Lunch meatType="div" />

Passing state up from child component and call a function with React

So this is my parent component - it's getting a state set from it's child component when what component is updated which works great
const [filteredCases, setFilteredCases] = useState([]);
<ChildComponent
setFilteredCases={setFilteredCases}
/>
What I would like to do is also call a function whenever the setFilteredCases state is updated .. so something like this
const [filteredCases, setFilteredCases] = useState([]);
function foo() {
// do things
}
<ChildComponent
setFilteredCases={setFilteredCases, foo}
/>
But doing so seems to break the state update and never calls the function .. have I got the syntax wrong here or is there a better way to implement what I'm doing here?
Any advice / guidance / links to documentation welcome!
Try using useEffect instead
const [filteredCases, setFilteredCases] = useState([]);
const foo = () => {
// some changes
}
useEffect(() => {
// filteredCases has been updated
foo();
}, [filteredCases]);
<
ChildComponent setFilteredCases = {
setFilteredCases
}
/>
const ChildComponent = ({
setFilteredCases
}) => {
// Call the function this way:
setFilteredCases([]);
return (
// Send out something.
);
};
export default ChildComponent;
You can pass a state or method as a props from parent to child component.
Example:
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
function App() {
return (
<div>
<Welcome name="Sara" />
<Welcome name="Cahal" />
<Welcome name="Edite" />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
Another Example :
const [filteredCases, setFilteredCases] = useState([]);
function foo() {
// do things
}
<ChildComponent
setFilteredCases={setFilteredCases}
foo={foo}
/>
const ChildComponent = ({ foo, setFilteredCases }) => {
const onHandleClick = () => {
// Call the functions
setFilteredCases('value');
foo();
}
return (
<button onClick={onHandleClick} >Click me</button>
);
};
export default ChildComponent;

Convert a React Class Component to a Function Component

Did I convert class component to function component correctly btw?
I don't know how to convert these code to function component tho, plz correct me
I don't necessarily need a code, just explain what I was wrong and what react knowledge I was missing
This is an original class component
const eventNames = ['onDragStart', 'onDrag', 'onDragEnd'];
function round5(value) {
return (Math.round(value * 1e5) / 1e5).toFixed(5);
}
export default class ControlPanel extends PureComponent {
renderEvent = eventName => {
const {events = {}} = this.props;
const lngLat = events[eventName];
return (
<div key={eventName}>
<strong>{eventName}:</strong> {lngLat ? lngLat.map(round5).join(', ') : <em>null</em>}
</div>
);
};
render() {
return (
<div className="control-panel">
<h3>Draggable Marker</h3>
<p>Try dragging the marker to another location.</p>
<div>{eventNames.map(this.renderEvent)}</div>
</div>
);
}
}
This is a code I converted to function component
function round5(value) {
return (Math.round(value * 1e5) / 1e5).toFixed(5);
}
const ControlPanel = (props) => {
const eventNames = ["onDragStart", "onDrag", "onDragEnd"];
function renderEvent(eventName) {
const { events } = props;
const lngLat = events[eventName];
return (
<div key={eventName}>
<strong>{eventName}:</strong>{" "}
{lngLat ? lngLat.map(round5).join(", ") : <em>null</em>}
</div>
);
}
return (
<div className="control-panel">
<h3>Draggable Marker</h3>
<p>Try dragging the marker to another location.</p>
<div>{eventNames.map(eventName => renderEvent(eventName))}</div>
</div>
);
};
export default ControlPanel;
you can reduce code a bit:
<div>{eventNames.map(renderEvent)}</div>
but renderEvent is not a react component so it has no this.props
You have to write renderEvent inside your functional component to access its props.
so in this case your component will be something like:
function round5(value) {
return (Math.round(value * 1e5) / 1e5).toFixed(5);
}
const ControlPanel = (props) => {
const renderEvent = (eventName) => {
const { events } = props;
const lngLat = events[eventName];
return (
<div key={eventName}>
<strong>{eventName}:</strong>{" "}
{lngLat ? lngLat.map(round5).join(", ") : <em>null</em>}
</div>
);
}
const eventNames = ["onDragStart", "onDrag", "onDragEnd"];
return (
<div className="control-panel">
<h3>Draggable Marker</h3>
<p>Try dragging the marker to another location.</p>
<div>{eventNames.map(eventName => renderEvent(eventName))}</div>
</div>
);
};
export default ControlPanel;

React-Redux : re render child component on mapStateToProps in parent change doesn't work

I'm new to redux, and I'm trying to make a component reactive.
I want to re-render the MoveList component when the prop I'm passing down from parent mapStateToProps changes and it's not working.
I tried giving a key to the movelist component but it didn't work, and Im not sure how else to approach this
Parent component:
async componentDidMount() {
this.loadContact();
this.loadUser();
}
loadContact() {
const id = this.props.match.params.id;
this.props.loadContactById(id);
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.match.params.id !== this.props.match.params.id) {
this.loadContact();
}
}
transferCoins = (amount) => {
const { contact } = this.props
console.log('amount', amount);
this.props.addMove(contact, amount)
console.log(this.props.user);
}
get filteredMoves() {
const moves = this.props.user.moves
return moves.filter(move => move.toId === this.props.contact._id)
}
render() {
const { user } = this.props;
const title = (contact) ? 'Your Moves:' : ''
if (!user) {
return <div> <img src={loadingSvg} /></div>;
}
return (
<div className="conact-deatils">
{ <MoveList className="move-list-cmp" title={title} moveList={this.props.user.moves} />}
</div>
)
}
}
const mapStateToProps = (state) => {
return {
user: state.user.currUser
};
};
const mapDispatchToProps = {
loadContactById,
saveContact,
addMove
};
export default connect(mapStateToProps, mapDispatchToProps)(ContactDetailsPage);
Child component: moveList
export const MoveList = (props) => {
return (
<div className="moves-list">
<div className="title">{props.title}</div>
<hr/>
{props.moveList.map(move => {
return (
<ul className="move" key={move._id}>
{props.isFullList && <li>Name: {move.to}</li>}
<li>Amount: {move.amount}</li>
</ul>
)
})}
</div>
)
}
at the end the problem was that the parent component didn't re-render when i called the addMove dispatch. i didn't deep copied the array of moves object, and react don't know it need to re-render the component. i made a JSON.parse(JSON.stringify deep copy and the component.

Any better way to pass data from a child component to a function in a parent component without using bind()?

I'm using React.Context to pass a function down to child component which is inside another component. See figure down below.
I'm quite not comfortable using .bind() in order to pass the name variable to the function in the parent component.
Is there a better way to pass data from a child component to a function in a parent component without using .bind()?
See my code below:
import React, { Component, createContext } from 'react'
const AppContext = createContext()
class ComponentOne extends Component {
const handleClick = (name)=> {
alert(`Hello ${name}`)
}
render(){
return(
<AppContext.Provider
value={{
handleClick: this.handleClick
}}>
<p>Hello from ComponentOne</p>
</AppContext.Provider>
)
}
}
const ComponentThree = props=> {
const name = "lomse"
return(
<AppContext.Consumer>
(context=>(
<button onClick={context.handleClick.bind(null, name)}>Click me!</button>
))
</AppContext.Consumer>
)
}
You can always do:
const ComponentThree = props=> {
const name = "lomse"
return(
<AppContext.Consumer>
(context=> {
const handleClick = () => context.handleClick(name)
return <button onClick={handleClick}>Click me!</button>
})
</AppContext.Consumer>
)
}
What you're doing is about binding arguments so when a function is called later it will be called with those arguments. It has nothing to do with React Context.
and onClick needs to be a function, that will be called - well - on click.
Or you could do:
const handleClick = (name) => () => {
alert(`Hello ${name}`)
}
and then:
const ComponentThree = props=> {
const name = "lomse"
return(
<AppContext.Consumer>
(context=> (
<button onClick={context.handleClick(name)}>Click me!</button>
))
</AppContext.Consumer>
)
}

Resources