Can't destructure props in child component - javascript

I'm getting some trouble on destructuring props when passing an object to a child component. This object has 2 values, one is another object and the second is a function.
This is my code in case any of you can help:
Parent cmp:
export const AdminScreen = () => {
const {kolorTokenContract, setContract} = useContext(ContractsContext);
console.log("contract from admin: ", kolorTokenContract);
return (
<div className="container">
<h1>Administrator Screen</h1>
<hr />
<div className="row align-items-center">
<ERC20AdminForm props={{kolorTokenContract, setContract}} />
<TokenInfo {...kolorTokenContract} />
</div>
<hr />
<Balance />
</div>
);
};
Child cmp:
export const ERC20AdminForm = ({kolorTokenContract, setContract}) => {
//console.log("props from erc20admin form: ", props);
console.log("contract from erc20admin form: ", kolorTokenContract);
return (
<div className="col-8 col-md-6 col-sm-4 ">
<MintingForm props={kolorTokenContract} />
<SetVaultForm />
<TransferOwnershipForm />
</div>
);
};
If i log the "kolorTokenContract" i just get undefined :(. Thanks in advance for any help!

For Child component:
const ERC20AdminForm = (props:any) => {
console.log("contract from erc20admin form: ", props.kolorTokenContract);
return (
<div className="col-8 col-md-6 col-sm-4 ">
<MintingForm kolorTokenContract={props.kolorTokenContract} />
<SetVaultForm />
<TransferOwnershipForm />
</div>
);
};
Call from a parent with separate props:
<ERC20AdminForm kolorTokenContract={kolorTokenContract} setContract={setContract} />
This is a sandbox code for proof of concept.

In your admin screen you should check for the kolorTokenContract . what does the context return?
from where do you get the value of kolorTokenContract, does it come from an api?
if it is then I am guessing while defining the state you didn't give it a value.
For solution, you can give an empty object while defining the kolorTokenContract in your context or you can set a default value
const {kolorTokenContract = {}, setContract} = useContext(ContractsContext);

Related

useState hook is breaking activePost as useEffect is triggered by selectedPost

My goal was to fetch posts from Graphcms and populate an array - posts, and populate it into postlist, then the main component will change according to what the user clicks on a post from postlist, I can see the posts array is populated , but when i click on a post on postlist I get the following error
Main.js:22 Uncaught TypeError: Cannot read properties of undefined (reading 'featuredImage')
Below my files
App.js
function App() {
const [selectedPost,setSelectedPost] = useState(0);
const [posts, setPosts] = useState([]);
useEffect(() => {
const fetchPosts = async () => {
const { posts } = await request(
'https://api-ap-southeast-2.graphcms.com/v2/ckxo1np9m5kw601xpccps4lrn/master',
`
{
posts {
id
title
slug
excerpt
featuredImage
{
url
}
}
}
`
);
console.log("print posts " , posts)
setPosts(posts);
};
fetchPosts();
}, []);
return ( <div className='app'>
<Header/>
{
posts.length>0 && (<>
<Main posts={posts} selectedPost={selectedPost}/>
<PostList posts={posts} setSelectedPost={setSelectedPost} />
</>
)
}
</div>
)
}
export default App;
And the Main.js Component
const Main = ({selectedPost,posts}) => {
const[activePost,setActivePost] =useState(posts[0])
console.log("activePost ", activePost)
useEffect(()=>{
setActivePost(posts[selectedPost])
},[posts,selectedPost])
return (
<div className='main'>
<div className='mainContent'>
<div className='postHighlight'>
<div className='postContainer'>
<img
className='selectedPost'
src= {activePost.featuredImage.url}
alt=''
/>
</div>
</div>
<div className='postDetails' style={{color:'#fff'}}>
<div className='title'>
{activePost.title} </div>
<span className='itemNumber'></span>
<span className='postExcerpt'>{activePost.excerpt}</span>
</div>
<div className='otherDetails'>
</div>
</div>
</div>
)
}
export default Main
And then we have postList.js file
const PostList = ({posts,setSelectedPost}) => {
return (
<div className='postList'>
{posts.map(post=>(
<div onClick={()=>setSelectedPost(post.id)}>
<CollectionCard key={post.slug} title={post.title} excerpt={post.excerpt} imageSrc={post.featuredImage.url}/>
</div>
)) })
</div>
)
}
export default PostList
Based on your app, you are using the index of the selected post.
The error arises from your onclick function. You are passing post.id to setSelectedPost() so you are accessing the posts array incorrectly. Hence, the undefined.
Just use the current index on your map function:
<div className='postList'>
{posts.map((post, index) => (
<div onClick={() => setSelectedPost(index)} key={post.slug}>
<CollectionCard
title={post.title}
excerpt={post.excerpt}
imageSrc={post.featuredImage.url}
/>
</div>
))
}
</div>

Hooks Callback- react

I'm currently building a react app which has a component slider and I need to pass the data back to the parent, the only fact is that the child is a little bit complex hook and I've been unable to find something similar that can help me to implement on my project, this is what I have:
The child
function valuetext(value) {
return `${value}`;
}
export default function RangeSlider() {
const classes = useStyles();
const [value, setValue] = React.useState([0, 100000]);
const handleChange = (event, newValue) => {
var val = setValue(newValue);
//I guess here is when I'm suposed to send the info to the parent
};
return (
<div className={classes.root}>
<Typography id="range-slider" gutterBottom>
Kilometers
</Typography>
<Slider
value={value}
max={500000}
min={0}
step={1000}
onChange={handleChange}
valueLabelDisplay="auto"
aria-labelledby="range-slider"
getAriaValueText={valuetext}
/>
<div id="seats-labes">
<span>0km</span>
<span>50.0000km</span>
</div>
</div>
);
}
The parent:
function WebFilter(props) {
return (
<div className="filter-web-section">
<Accordion className="filter-accordion">
<Card className="card-section">
<Card.Body>
<RangeSlider/>
</Card.Body>
</Card>
</Accordion>
</div>
)
}
export default WebFilter;
The grandfather:
class ResultModel extends Component {
render() {
return (
<div>
<h1>Texto de prueba + boton</h1> <button>+</button>
<div className="SiteHeader">
<Header/>
</div>
<div className="cars-result-content">
<div className="cars-result-content__filters">
<WebFilter
/>
</div>
<div className="car-result-content-list">
<div className="car-result-list__counter-cars">
<p>400 vehicles</p>
</div>
<div className="car-result-content-list__statBar">
<StatBar/>
</div>
<div className="cars-result-page-list__ListCars">
<ResultsView/>
</div>
</div>
</div>
</div>
)
}
}
I've been reading about declaring the hook constants at the very first component (grandfather) but I haven't been able to find a way to pass the data through the father. Thanks in advance for any hint or help.
The question is a bit short on specifics, but from what I can gather, you just need to pass down a function from component 1 through component 2 to component 3.
It's pretty straightforward actually.
In your grandpa component, create a function you want to pass:
class ResultModel extends Component {
const func1 = (data) => {console.log(data)}
render() {
...
Pass it down to father:
...
<WebFilter func1={func1} />
...
In the father component, get func1 and pass it down to child:
function WebFilter(props) {
const {func1} = props;
return (
<div className="filter-web-section">
<Accordion className="filter-accordion">
<Card className="card-section">
<Card.Body>
<RangeSlider func1={func1} />
</Card.Body>
</Card>
</Accordion>
</div>
)
}
Then in child call it like so:
export default function RangeSlider({func1}) {
const classes = useStyles();
const [value, setValue] = React.useState([0, 100000]);
const handleChange = (event, newValue) => {
var val = setValue(newValue);
//I guess here is when I'm suposed to send the info to the parent
func1("your data")
};
...
...
If you want to learn something read about react concept called lifting the state up.
Read about lifting state up in react documentation
Or just google it read one or two articles if still don't get it then post a comment I'll write full code.

How do I pass my click handler from parent to children correctly?

I'm stuck trying to refactor my scoreboard component because I can't pass click handlers from parent to child correctly. What am I doing wrong?
This is my component structure
import React, { useState } from "react";
import "./App.css";
function ScoreBoard(props) {
return (
<section className="scoreboard">
<div className="topRow">
<div className="home">
<h2 className="home__name">{props.data.home.name}</h2>
<div className="home__score">{props.data.home.score}</div>
</div>
<div className="timer">00:03</div>
<div className="away">
<h2 className="away__name">{props.data.away.name}</h2>
<div className="away__score">{props.data.away.score}</div>
</div>
</div>
<BottomRow />
</section>
);
}
function TDButton(props) {
return (
<button className={props.side + "Buttons__touchdown"}>
{props.side.toUpperCase() + " Touchdown"}
</button>
);
}
function FGButton(props) {
/* similar to TDButton */
}
function Buttons(props) {
let scoreCounter = props.scoreCounter;
return (
<section className="buttons">
<div className="homeButtons">
<TDButton side="home" onClick={scoreCounter("Lions", 7)} />
<TDButton side="away" onClick={scoreCounter("Tigers", 7)} />
</div>
<div className="awayButtons">
<FGButton side="home" onClick={scoreCounter("Lions", 3)} />
<FGButton side="away" onClick={scoreCounter("Tigers", 3)} />
</div>
</section>
);
}
function App() {
const data = {
home: { name: "Lions", score: 32 },
away: { name: "Tigers", score: 32 }
};
const [homeScore, sethomeScore] = useState(data.home.score);
const [awayScore, setawayScore] = useState(data.away.score);
const scoreCounter = (team, amount) => {
if (team === data.home.name) {
console.log("in");
sethomeScore(homeScore + amount);
} else {
console.log("out");
setawayScore(awayScore + amount);
}
};
return (
<div className="container">
<ScoreBoard data={data} />
<Buttons data={data} scoreCounter={() => scoreCounter} />
</div>
);
}
The initial component all lived in App so I am trying to break it into smaller components. I can't seem to get the click handler to work though. What am I doing wrong? Maybe my component breakdown could be improved? Thanks!
You adding props to TButton but you don't use it inside. Use something like this:
function TDButton(props) {
return (
<button className={props.side + "Buttons__touchdown"} onClick={props.onClick}>
{props.side.toUpperCase() + " Touchdown"}
</button>
);
}
so it's like:
<TDButton side="home" onClick={scoreCounter("Lions", 7)} />
<button className={props.side + "Buttons__touchdown"} onClick={scoreCounter("Lions", 7)}>
{props.side.toUpperCase() + " Touchdown"}
</button>
because you're passing the props to event from parent.
but this will only work if scoreCounter("Lions", 7) return a function, if it's regular function that do action you need:
<TDButton side="home" onClick={() => scoreCounter("Lions", 7)} />
so props is a function not the value that function return.
Also this maybe not what you want:
<Buttons data={data} scoreCounter={() => scoreCounter} />
scoreCounter will be function that return value of function (that answer previous consern but you really want normal function, because above function don't have params and you use 'Lion' that will be ignored):
<Buttons data={data} scoreCounter={scoreCounter} />
SO you've to call the function in your JSX
<Buttons data={data} scoreCounter={() => scoreCounter()} />
You can pass value into the function if required.

conditional component render without using location.pathname React

I'm currently attempting to render components based off of what page the user is currently on. One of the components remains the same and which one is rendered with it will be determined on page location. I'm currently using window.location.pathname but this is not a desired behavior for us.
const Container = reduxForm({ form: 'login' })(LoginForm)
const currentLocation = window.location.pathname
const ExtraButtons = () => (
<div className="row center-xs">
<div className="col-xs-6 center-xs">
<Link to={FORGOT_PASSWORD_ROUTE}>Forgot Password?</Link>
</div>
</div>
)
const Login = ({ loginStart, error, update, search, address,
currentUser }) => (
<div>
<div className="row center-xs">
<div className="col-xs-12">
<img src={logo} className="logo" />
</div>
</div>
<div className="row">
<div className="col-xs">
<Container onSubmit={loginStart} />
{error && (
<InputError
visible={error && error !== null}
errorMessage={error.message}
/>
)}
<ExtraButtons />
</div>
{currentLocation.includes('/login') ? (
<div className="row center-xs start-xs col-xs">
<LocSearch
updateAddress={update}
search={search}
address={address}
/>
</div>
) : currentLocation.includes('/home') ? (
<div className="col-xs">
<EmailSearch />
</div>
) : null}
</div>
</div>
)
const mapStateToProps = state => {
const { submitting, currentUser } = state.user
const { address = '' } = state.locationSearch
const error = state.error
return {
address,
submitting,
error,
currentUser
}
}
const mapDispatchToProps = {
loginStart,
resetError,
update,
search
}
export default connect(mapStateToProps, mapDispatchToProps)(Login)
I think currentLocation is assigned the value when the script is loaded instead of when your component is rendered. that is the root cause. you should define currentLocation in your component.

How do you create multiple forms on the same page with redux-forms v6?

I have a simple todo app in which my redux store contains an array of 'todos'. My 'Todo' component maps over every 'todo' in the store and renders a 'TodoForm' component that uses redux-forms v6.
As it is now, every 'todo' shares the same form name/key, so every time I input something in the 'title' Field, it changes the 'title' of every todo. I found a work around by using unique Field names, but I fear it's going to over complicate things as the app grows, and would prefer to use unique Form names so every field can have the same name without interfering with the other forms
(TodoForm1, TodoForm2, TodoForm3 can all have a unique 'title' Field instead of TodoForm containing 'title1', 'title2', 'title3' Fields).
I tried accessing the TodoForm's props so I could set each form's key as the component's unique id, but it doesn't seem like the component receives props that early.
I also tried making an immediately invoked function where it spits out a random number, and using that number as the form's name, but that also didn't work.
How can I can map through all my todos and render a v6 redux-form with a unique form key?
Here's a picture of the app, console, and redux devtools. There's 3 'todos', but there's only one form that connects them all, todo-926, even though each form key should have been randomly generated in an immediately invoked function:
HomePageMainSection.index.js
renderTodos(todo) {
if (!todo) {
return <div>No Todos</div>;
}
return (
<div key={todo.get('id')}>
<Todo
todo={todo}
updateTodo={this.props.updateTodo}
deleteTodo={this.props.deleteTodo}
/>
</div>
);
}
render() {
if (!this.props.todos) {
return <div>No Todos</div>;
}
return (
<div className={styles.homePageMainSection}>
<h1>Hey I'm the Main Section</h1>
<div>
{this.props.todos.get('todos').map(this.renderTodos)}
</div>
</div>
);
}
Todo.index.js:
renderTodo() {
if (this.state.editMode) {
return (
<TodoForm
todo={this.props.todo} changeTodoEditMode={this.changeTodoEditMode}
updateTodo={this.props.updateTodo}
/>
);
}
return (
<div className={styles.Todo} onClick={this.changeTodoEditMode}>
<div className="card card-block">
<h4 className="card-title">{this.props.todo.get('author')}</h4>
<p className="card-text">{this.props.todo.get('title')}</p>
<i
className={`${styles.deleteIcon} btn btn-danger fa fa-times`}
onClick={this.deleteTodo}
></i>
</div>
</div>
);
}
render() {
return (
<div className="col-xs-6 col-sm-4">
{this.renderTodo()}
</div>
);
}
TodoForm.index.js:
class TodoForm extends React.Component { // eslint-disable-line react/prefer-stateless-function
constructor(props) {
super(props);
this._handleSubmit = this._handleSubmit.bind(this);
}
_handleSubmit(formData) {
console.log('');
console.log('OG: ', this.props.todo)
console.log('formData: ', formData);
const data = this.props.todo.update('title', formData.get('title'));
console.log('data: ', data);
console.log('');
// this.props.updateTodo(data);
}
render() {
const { handleSubmit, pristine, submitting } = this.props;
return (
<form className={`${styles.todoForm} card`} onSubmit={handleSubmit(this._handleSubmit)}>
<div className="card-block">
<label htmlFor="title">{this.props.todo.get('title')}</label>
<div className={'form-group'}>
<Field name={`title`} component="input" type="text" placeholder="Enter new title" className="form-control" />
</div>
</div>
<div className="card-block btn-group" role="group">
<button
className="btn btn-success"
type="submit"
disabled={pristine || submitting}
>
Submit
</button>
<button
className="btn btn-danger fa fa-times"
onClick={this.props.changeTodoEditMode}
>
</button>
</div>
</form>
);
}
}
const randomNum = (() => {
const thing = Math.floor(Math.random() * 1000) + 1;
console.log('thing: ', thing);
console.log('notThing: ', TodoForm.props);
return thing;
})();
export default reduxForm({
form: `todo-${randomNum}`,
})(TodoForm);
For giving your forms dynamic key you should use form attribute on your TodoForm component:
renderTodo() {
if (this.state.editMode) {
return (
<TodoForm
form={'todo-' + this.props.todo.id}
todo={this.props.todo} changeTodoEditMode={this.changeTodoEditMode}
updateTodo={this.props.updateTodo}
/>
);
}
[...]
}
(Instead of this.props.todo.id could be your randomNum function call)
API reference: http://redux-form.com/6.0.2/docs/api/Props.md/

Categories