'text' is missing in props validation react/prop-types - javascript

as you may get from the title, passing props in react is not working. And i donĀ“t get why.
function Global(props) {
return (
<div>
<ResponsiveAppBar />
<Grid sx={{ mt: 7 }}>
{props.text}
<HorizontalLinearStepper />
</Grid>
</div>
);
}
return (
<div className="App">
<Card>
<CvContext.Provider value={value}>
<Global text="kk" />
</CvContext.Provider>
</Card>
</div>
);

Try destructuring the props like this:
function Global({text}) {
return (
<div>
<ResponsiveAppBar />
<Grid sx={{ mt: 7 }}>
{text}
<HorizontalLinearStepper />
</Grid>
</div>
);
}
You can keep the rest the same. Destructuring props is (usually) a better practice.

Does it work if you do
import PropTypes from 'prop-types';
function Global(props) {
return (
<div>
<ResponsiveAppBar />
<Grid sx={{ mt: 7 }}>
{props.text}
<HorizontalLinearStepper />
</Grid>
</div>
);
}
Global.propTypes = {
text: PropTypes.string
}
But your code should work even without it, although you would be getting that warning.

this correction work for me
import PropTypes from "prop-types";
function Global(props) {
Global.propTypes = {
text: PropTypes.string,
};
return (
<div>
<ResponsiveAppBar />
<Grid sx={{ mt: 7 }}>
{props.text}
<HorizontalLinearStepper />
</Grid>
</div>
);
}
export default Global;

Related

Trying to pass props to an axios call

I am building a forum and I want to make it where: Once you click on the title, it displays a page (which I already created) that displays the post title and then the post body. I used MUI to build the page as well. However, the Axios call fails when I call the backend and appending the "this.state.props." to the end.
My "All Questions" page code in which the user which select which post to open:
export default class DisplayPosts extends Component {
constructor(props) {
super(props);
this.state = {
posts: [],
selectedPostId: null,
};
}
componentDidMount (){
axios.get('http://localhost:6006/api/v1/posts')
.then(res=> {
const posts = [];
for (let key in res.data.data) {
posts.push({...res.data.data[key], id: key});
}
this.setState({
posts: posts,
})
console.log('Pulling From:: ', res.data.data)
})
.catch(err => console.log('err:: ', err)
)
}
onPostClickHandler = (_id) => {
this.setState({
selectedPostId: _id,
})
console.log(_id)
}
render() {
const posts = this.state.posts.map((post) => {
return <Posts
key ={post._id}
post={post}
postclicked = {this.onPostClickHandler.bind(
this,
post._id,
)} />;
})
return (
<Box component="main"
sx={{ flexGrow: 1, p: 3, marginLeft: "300px",marginTop:"-40px" }}>
<Toolbar />
<Stack spacing={2}>
<Typography>
<h1> All Questions </h1>
</Typography>
<Button
sx={{}}
variant = 'outlined'
size = 'medium'
color = 'secondary'>
Ask New Question
</Button>
<Divider />
<div>
{posts}
</div>
</Stack>
{this.state.selectedPostId && (
<ViewPosts _id={this.state.selectedPostId} />
)}
</Box>
)
}
}
My "View Posts" page, the page where the user will see the information of the post they just clicked
export default class ViewPosts extends Component {
constructor(props){
super(props);
this.state = {
post: null,
}
}
componentDidMount (){
axios.get(`http://localhost:6006/api/v1/posts/${this.props._id}`)
.then(res=> {
this.setState({
posts: {...res.data.data, _id: this.props._id}
})
console.log('Pulling From:: ', res.data.data)
})
.catch(err => console.log('err:: ', err)
)
}
render() {
return (
<div>
<><Box component="main"
sx={{ flexGrow: 1, p: 3, marginLeft: "300px", marginTop: "-40px" }}>
<Toolbar />
<Typography>
<h1>{this.state.post.title} </h1>
<p>Added: Today ..........Viewed: -- times </p>
</Typography>
<Divider />
<Stack direction='row' spacing={3}>
<Stack
direction="column"
spacing={2}>
<IconButton>
<KeyboardDoubleArrowUpIcon color='primary' />
</IconButton>
<IconButton sx={{ marginTop: '2px' }}>
<KeyboardDoubleArrowDownIcon color='primary' />
</IconButton>
</Stack>
<Typography>
<h6> </h6>
</Typography>
<Typography>
<p>
{this.state.post.body}
</p>
</Typography>
</Stack>
<Divider />
<TextField
sx={{ marginTop: "20px", marginLeft: "0px", width: '950px' }}
id="filled-multiline-static"
label="Enter Answer Here..."
multiline
rows={8}
variant="filled" />
<Button
sx={{ marginTop: "15px" }}
variant='contained'
size='large'
color='primary'
>
Post Your Answer
</Button>
</Box>
</>
</div>
)
}
}
From my understanding, componentDidMount is called after the component is mounted.
And by that, I mean the axios call will happen immediately, while the DOM content will take more time to load.
So, chances are, what happens is that you're not going to see anything, even if the axios call is finished and the state of the ViewPost is no longer null.
What you may wanna do now is to create a logic that prevents the DOM of the post from being displayed until the state is populated.
Like, for example...
render() {
return this.state.post && (
<div>
<><Box component="main"
sx={{ flexGrow: 1, p: 3, marginLeft: "300px", marginTop: "-40px" }}>
<Toolbar />
<Typography>
<h1>{this.state.post.title} </h1>
<p>Added: Today ..........Viewed: -- times </p>
</Typography>
<Divider />
<Stack direction='row' spacing={3}>
<Stack
direction="column"
spacing={2}>
<IconButton>
<KeyboardDoubleArrowUpIcon color='primary' />
</IconButton>
<IconButton sx={{ marginTop: '2px' }}>
<KeyboardDoubleArrowDownIcon color='primary' />
</IconButton>
</Stack>
<Typography>
<h6> </h6>
</Typography>
<Typography>
<p>
{this.state.post.body}
</p>
</Typography>
</Stack>
<Divider />
<TextField
sx={{ marginTop: "20px", marginLeft: "0px", width: '950px' }}
id="filled-multiline-static"
label="Enter Answer Here..."
multiline
rows={8}
variant="filled" />
<Button
sx={{ marginTop: "15px" }}
variant='contained'
size='large'
color='primary'
>
Post Your Answer
</Button>
</Box>
</>
</div>
)
}
}

React DnD, could not find "store" in the context of Connect(Droppable)

Hello I am trying to make a drag and drop in my application but I have got a huge error. I have no idea if property is missing because in IDE it is an error free code.
Uncaught Error: Could not find "store" in the context of "Connect(Droppable)". Either wrap the root component in a <Provider>, or pass a custom React context provider to <Provider> and the corresponding React context consumer to Connect(Droppable) in connect options.
Down here are my components
3 Columns ( I want drag and drop across one column each, not between them)
const DetailColumns: React.FC<funcProps> = (props) => {
const onDragEnd = () => {};
return (
<Box
h="80vh"
borderWidth="0.5rem"
borderColor="orange.300"
borderRadius="1rem"
w="80%"
marginTop="2rem"
>
<Grid w="100%" h="100%" templateColumns="60% 20% 20%">
<Box border="0.5rem" borderColor="orange.300">
<RecipeDescriptionBox recipe={props.recipe} />
</Box>
<Box borderLeftWidth="0.5rem" borderColor="orange.300">
<RecipeStepsBox recipe={props.recipe} />
</Box>
<DragDropContext onDragEnd={onDragEnd}>
<Box borderLeftWidth="0.5rem" borderColor="orange.300">
<RecipeIngredientsBox recipe={props.recipe} />
</Box>
</DragDropContext>
</Grid>
</Box>
);
};
List of Items
<Box>
<ColumnHeader title="Steps" />
<Droppable droppableId="unique">
{(provided) => (
<Box {...provided.droppableProps} innerRef={provided.innerRef}>
{steps.map((step, index) => (
<DetailListItem
key={step}
itemName={step}
indexOfItem={index}
id={Math.random().toString()}
/>
))}
{provided.placeholder}
</Box>
)}
</Droppable>
</Box>
Item element
<Draggable draggableId={props.id} index={props.indexOfItem}>
{(provided) => (
<Box
{...provided.draggableProps}
{...provided.dragHandleProps}
innerRef={provided.innerRef}
>
<Flex margin="1rem">
<Box
bgGradient="linear(to-r, orange.200, orange.400)"
height="6vh"
width="6vh"
boxShadow="md"
rounded="md"
>
<Grid w="100%" h="100%" placeItems="center">
<Text color="white" fontWeight="700" fontSize="140%">
{props.amount && props.amount + props.unit}
{!props.amount && props.indexOfItem}
</Text>
</Grid>
</Box>
<Grid placeItems="center">
<Text marginLeft="1rem" fontWeight="500" fontSize="1.8rem">
{props.itemName}
</Text>
</Grid>
</Flex>
</Box>
)}
</Draggable>
If you want I can put this code to some sandbox to make it easier to debug.

ANT Design Form.List wont remove element

My plan is to build a step-by-step from where users can add more sections as needed to a form. This plan is divided into several files to achieve the result, including a parent, a child, and the Quill editor component. I've followed the examples found in the ANT Design website: https://ant.design/components/form/#components-form-demo-dynamic-form-items but failed to replicate the example.
This is the setup for the React-Quill Editor component:
//texteditorsimple.js
import React from 'react';
import ReactQuill, {Quill} from 'react-quill';
import MagicUrl from 'quill-magic-url';
import 'react-quill/dist/quill.snow.css';
//registering magic url module
Quill.register('modules/magicUrl', MagicUrl);
const modules = {
toolbar: [
...
clipboard: {
...
},
magicUrl: true,
};
const formats = [
'list',
'bullet',
'link',
];
const TextEditorSimple = ({ value, onChange, placeholder }) => {
return (
<>
<ReactQuill
theme="snow"
value={value || ''}
modules={modules}
formats={formats}
onChange={onChange}
placeholder={placeholder} >
</ReactQuill>
<div id='counter'></div>
</>
)
}
export default TextEditorSimple;
This is the Parent component:
//parent.js
import React, { useState, useEffect } from 'react';
...
import Child from '../child';
const Parent = () => {
const [ formValue2, setFormValue2 ] = useState([]);
const deleteY = (data) => {
const newArrayFormValue = formValue2.filter(obj => !obj.name.includes(data));
setFormValue2(newArrayFormValue);
}
return (
<Row justify='center' align="top" style={{ padding: 10}}>
<Col>
<Child deleteX={deleteY} fields={formValue2} onChange={(newFields) => {setFormValue2(newFields)}}/>
</Col>
</Row>
)
}
export default Parent;
And this is the Child component:
//child.js
import React from 'react';
...
import TextEditorSimple from './texteditorsimple';
const Child = ({fields, onChange, deleteX}) => {
<Row justify='start' align="top">
<Col xs={24} style={{ padding: 10}}>
<h2>Pengenalan & Langkah-langkah</h2>
<Form
fields={fields}
name="dynamic_form_nest_item"
onFinish={onFinish}
autoComplete="off"
onFieldsChange={(_,allFields) =>{
onChange(allFields);
}} >
<Form.List name="tutorial" style={{ width: '100%'}}>
{(fields, { add, remove }) => (
<>
{fields.map(({ index, key, name, fieldKey, ...restField }) => (
<div key={key}>
<Row>
{
name === 0 ?
<Col xs={24} style={{ padding: 5 }}>
<Form.Item
{...restField}
label='Pengenalan'
name={[name, 'pengenalan']}
fieldKey={[fieldKey, 'pengenalan']}
>
<TextEditorSimple/>
</Form.Item>
</Col>
:
<Col xs={24} md={16} style={{ padding: 5 }}>
<Form.Item
{...restField}
label={'Langkah '+ name}
name={[name, 'langkah'+name]}
fieldKey={[fieldKey, 'langkah'+name]}
>
<TextEditorSimple/>
</Form.Item>
</Col>
}
<Col xs={24} md={8} style={{ padding: 5 }}>
{
name === 0 ?
''
:
<Space align="start">
<Form.Item
{...restField}
name={[name, 'image'+name]}
fieldKey={[fieldKey, 'image'+name]}
valuePropName="fileList"
getValueFromEvent={normFile}
extra="Muat naik satu (1) imej/tangkap layar">
<Upload
name="image"
customRequest={dummyRequest}
listType="picture-card"
maxCount={1} >
{/* <Button icon={<UploadOutlined />}>Muat naik imej/ tangkap layar</Button> */}
{uploadButton}
</Upload>
</Form.Item>
<DeleteOutlined onClick={() => { remove(name); }} style={{ color: 'red'}}/>
</Space>
}
</Col>
</Row>
<Divider/>
</div>
))}
<Form.Item>
<Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
Tambah Langkah
</Button>
</Form.Item>
</>
)}
</Form.List>
</Form>
</Col>
</Row>
}
export default Child;
I'm trying to catch the changes made on the Form.List 'tutorial' (add, delete) in the Child and uplift the value to the Parent and store them in the state. But the deletion somehow not reflected in the UI, as the element that should have been deleted persisted on page. Tried to manually change the state through poorly coded deleteX and deleteY function, but not successful.
The GIF showing the error can be viewed here: http://www.giphy.com/gifs/HsIjHuckIflqOr3gOO

Call a Function on Tab click based on key?

These are my tabs and tables and I want to call different functions based on tabKey when you click on the tab.
<Paper className={classes.root} style = {{paddingTop:50}}>
<Grid>
<TabSelector
displayType="button"
showTab={"Org Details"}
showCount={false}
highlightTab={true}
onClick={() => this.getTabs.bind(this)}
>
<Tab name="Org Details" tabKey="gridOrgDetails">
<p>
<Grid>
<Paper className={classes.root} style={{ paddingTop: 50 }}>
<a href="#gridOrgDetails" id="gridOrgDetails" />
<Paper style={{ backgroundColor: "#759FEB" }}>
<Typography> ORG Details </Typography>
</Paper>
<EnhancedTable
checkBoxEnabled={false}
Data={{ rows: this.getOrg(), headCells: orgDeatils }}
rowsPerPage={5}
orderBy="Call_Date_vod__c"
order="desc"
/>
</Paper>
</Grid>
</p>
</Tab>
<Tab
name="License Details"
tabKey="gridLicenseDetails"
onClick={() => this.getLicenseDetails()}
>
<p>
<Grid>
<Paper className={classes.root} style={{ paddingTop: 50 }}>
<a href="#gridLicenseDetails" id="gridLicenseDetails" />
<Paper style={{ backgroundColor: "#759FEB" }}>
<Typography> License Details </Typography>
</Paper>
<EnhancedTable
checkBoxEnabled={false}
Data={{ rows: this.getOrg(), headCells: licenseDetails }}
rowsPerPage={5}
orderBy="Call_Date_vod__c"
order="desc"
/>
</Paper>
</Grid>
</p>
</Tab>
</TabSelector>;
This is my function where I am switching keys but this seems to be not working.
Can anyone please help me?
getTabs(f){
console.log(f.tabKey)
switch(f.tabKey) {
case "gridOrgDetails":
return this.getOrgDetails();
break;
case "gridLicenseDetails":
return this.getLicenseDetails();
break;
}
}
Here's a basic representation of what you're trying to achieve jsFiddle
link
const User = (props) => <div onClick = {props.onClick}>I am user
{props.user}</div>
class App extends React.Component {
constructor(props) {
super(props)
}
getTabs(f){
switch(f) {
case "gridOrgDetails":
console.log("gridOrgDetails")
break;
case "gridLicenseDetails":
console.log("gridLicenseDetails")
break;
}
}
render() {
return (
<div>
<div >
<User user="a" onClick={this.getTabs.bind(this,"gridOrgDetails")} />
<User user="b" onClick={this.getTabs.bind(this, "gridLicenseDetails")}/>
</div>
</div>
)
}
}
ReactDOM.render(<App />, document.querySelector("#app"))

react I get an error when trying to add a new post with a form

I am creating a react app like a blog I have a home page with all my posts displayed when you click on a post it takes to a detail page with comments and a button that opens a modal to add a comment.
I am trying to add a button on my home page to add a new post but I am getting this error:
TypeError: this.props.postNewPost is not a function
NewPostForm.handleNewPost
D:/Documents/job_application/tasks/interfell/src/components/MenuComponent.js:98
95 | handleNewPost(values){
96 | console.log("Current state: "+JSON.stringify(values));
97 | this.toggleNewPost();
> 98 | this.props.postNewPost( values.userId, values.title, values.body);
| ^ 99 | }
100 |
101 | render() {
This is my Menu or Home component
import React,{Component} from 'react';
import { Button,Modal,ModalBody,ModalHeader,Row,Label} from 'reactstrap';
import { Control, LocalForm, Errors } from 'react-redux-form';
import Grid from '#material-ui/core/Grid';
import { Link } from 'react-router-dom';
import { Loading } from './LoadingComponent';
function RenderMenuItem({post , onClick, postNewPost }){
return(
<Link to={`/menu/${post.id}`} >
<Grid xs={12}>
<h3>{post.title}</h3>
<p>{post.body}</p>
</Grid>
</Link>
);
}
function RenderPost({postNewPost }){
return(
<>
<NewPostForm postNewPost={postNewPost} />
</>
);
}
const Menu = (props) => {
const menu = props.posts.posts.map((post) => {
return (
<Grid key={post.id} xs={12}>
<RenderMenuItem post={post} />
</Grid>
);
});
if(props.posts.isLoading){
return(
<Grid container >
<Loading />
</Grid>
);
}
else if (props.posts.errMess){
return(
<Grid container >
<Grid xs={12} >
<h4>{props.posts.errMess}</h4>
</Grid>
</Grid>
);
}
else{
return(
<Grid container >
<Grid xs={6}>
<h3>Posts</h3>
</Grid>
<Grid xs={6}>
<RenderPost />
</Grid>
<Grid>
{menu}
</Grid>
</Grid>
);
}
}
const minLength = (len) => (val) => (val) && (val.length >= len );
const maxLength = (len) => (val) => !(val) || (val.length <= len );
class NewPostForm extends Component{
constructor(props){
super(props);
this.state = {
isNewPostOpen:false
}
this.toggleNewPost = this.toggleNewPost.bind(this);
this.handleNewPost = this.handleNewPost.bind(this);
}
toggleNewPost(){
this.setState({
isNewPostOpen : !this.state.isNewPostOpen
});
}
handleNewPost(values){
console.log("Current state: "+JSON.stringify(values));
this.toggleNewPost();
this.props.postNewPost( values.userId, values.title, values.body);
}
render() {
return(
<>
<Button outline onClick={this.toggleNewPost}>
<span className="fa fa-pencil fa-lg">Add Post</span>
</Button>
<Modal isOpen={this.state.isNewPostOpen} toggle={this.toggleNewPost}>
<ModalHeader toggle={this.toggleNewPost}>Submit Comment</ModalHeader>
<ModalBody>
<LocalForm className="container" onSubmit={(values)=>this.handleNewPost(values)}>
<Grid md={12}>
<Label htmlFor="userId">Your User Id</Label>
<Control.text model=".userId" id="userId" name="userId" className="form-control" placeholder="Your userId"validators={{ minLength:minLength(1),maxLength:maxLength(15) }} />
<Errors className="text-danger" model=".userId" show="touched" messages={{
minLength:'At least 3 characters',
maxLength:'Must be 15 characters or less'
}} />
</Grid> <Grid md={12}>
<Label htmlFor="title">Post title</Label>
<Control.text model=".title" id="title" name="title" className="form-control" placeholder="Post title"validators={{ minLength:minLength(3) }} />
</Grid>
<Grid className="form-group" md={12}>
<Label htmlFor="body"> Comment</Label>
<Control.textarea model=".body" id="body" name="body" rows="6" className="form-control"/>
</Grid>
<Grid className="form-group" md={12}>
<Button type="submit" color="primary">Submit</Button>
</Grid>
</LocalForm>
</ModalBody>
</Modal>
</>
);
}
}
export default Menu;
I am new to react and I am a bit lost. Sorry for it being in material UI and bootstrap I am trying to convert it from bootstrap to material UI
You need to propagate your function postNewPost from your <MainComponent />.
return (
<div>
<TransitionGroup>
<CSSTransition key={this.props.location.key} classNames="page" timeout={300}>
<Switch location={this.props.location} >
// You are not passing the prop into <Menu /> here
<Route exact path="/menu" component={() => <Menu posts={this.props.posts} /> } />
<Route path="/menu/:postId" component={PostWithId} />
<Redirect to="/menu" />
</Switch>
</CSSTransition>
</TransitionGroup>
</div>
);
That line needs to be:
<Route exact path="/menu" component={() => <Menu posts={this.props.posts} postNewPost={this.props.postNewPost} /> } />
From there, your <Menu /> component is propagating the component correctly.
I suggest using the prop-types library: https://www.npmjs.com/package/prop-types
It will give you helpful warning messages if your components are missing required props or getting incorrect props.
You need to bind this
In your constructor in NewPostForm, do this.handleNewPost = this.handleNewPost.bind(this);
You can use this to help you with that: https://www.npmjs.com/package/react-autobind

Categories