I wrote this code for a todo list. It generates li elements but doesn't load the text stored in state.text.
class TodoWholistic extends React.Component {
constructor(props) {
super(props);
this.state = { tasks: [], text: "" };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input
id="new-task"
onChange={this.handleChange}
value={this.state.text}
placeholder="Type some text here..."
/>
<button>+</button>
</form>
<TodoList tasks={this.state.tasks} />
</div>
);
}
handleChange(e) {
this.setState({ text: e.target.value });
}
handleSubmit(e) {
e.preventDefault();
if (this.state.text.length === 0) {
return;
}
const newTask = {
tasks: this.state.text,
id: Date.now(),
};
this.setState((state) => ({
tasks: state.tasks.concat(newTask),
text: "",
}));
}
}
class TodoList extends React.Component {
render() {
return (
<ul>
{this.props.tasks.map((task) => (
<li key={task.id}>{task.text}</li>
))}
</ul>
);
}
}
ReactDOM.render(<TodoWholistic />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Well, you are iterating over the state.tasks, which includes two property (tasks and id) and you are storing the state.text value in the tasks property which is kinda messy and I don't know your intention about it. But to solve your current problem you can either refer to the tasks property or change the tasks object key to text, where both of them are fine.
Refer to tasks:
class TodoWholistic extends React.Component {
constructor(props) {
super(props);
this.state = { tasks: [], text: "" };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input
id="new-task"
onChange={this.handleChange}
value={this.state.text}
placeholder="Type some text here..."
/>
<button>+</button>
</form>
<TodoList tasks={this.state.tasks} />
</div>
);
}
handleChange(e) {
this.setState({ text: e.target.value });
}
handleSubmit(e) {
e.preventDefault();
if (this.state.text.length === 0) {
return;
}
const newTask = {
tasks: this.state.text,
id: Date.now(),
};
this.setState((state) => ({
tasks: state.tasks.concat(newTask),
text: "",
}));
}
}
class TodoList extends React.Component {
render() {
return (
<ul>
{this.props.tasks.map((task) => (
<li key={task.id}>{task.tasks}</li>
))}
</ul>
);
}
}
ReactDOM.render(<TodoWholistic />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Change tasks to text:
class TodoWholistic extends React.Component {
constructor(props) {
super(props);
this.state = { tasks: [], text: "" };
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<input
id="new-task"
onChange={this.handleChange}
value={this.state.text}
placeholder="Type some text here..."
/>
<button>+</button>
</form>
<TodoList tasks={this.state.tasks} />
</div>
);
}
handleChange(e) {
this.setState({ text: e.target.value });
}
handleSubmit(e) {
e.preventDefault();
if (this.state.text.length === 0) {
return;
}
const newTask = {
text: this.state.text,
id: Date.now(),
};
this.setState((state) => ({
tasks: state.tasks.concat(newTask),
text: "",
}));
}
}
class TodoList extends React.Component {
render() {
return (
<ul>
{this.props.tasks.map((task) => (
<li key={task.id}>{task.text}</li>
))}
</ul>
);
}
}
ReactDOM.render(<TodoWholistic />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
There is a typo in your TodoList class. task.text should be task.tasks
class TodoList extends React.Component {
render() {
return (
<ul>
{this.props.tasks.map((task) => (
<li key={task.id}>{task.tasks}</li>
))}
</ul>
);
}
}
Related
When the inputs are focused, I make the borders red with css.
But I can't autofocus on the most recently added input.
codesandbox: Example
import React from "react";
import "../src/styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = { values: [] };
}
createUI() {
return this.state.values.map((el, i) => (
<div key={i} style={{ marginBottom: "1rem" }}>
<style>
{`
.test:focus-within{
border:3px solid red !important;
}
`}
</style>
<div className="test">
<input type="text" />
</div>
</div>
));
}
addClick() {
this.setState((prevState) => ({ values: [...prevState.values, ""] }));
}
render() {
return (
<>
{this.createUI()}
<input
type="button"
value="add more"
onClick={this.addClick.bind(this)}
/>
</>
);
}
}
export default App;
How can I autofocus the last added input and make its borders red?
can create a useRef and assign it to the latest input
class App extends React.Component {
constructor(props) {
super(props);
this.state = { values: [] };
this.lastRef = React.createRef();
}
createUI() {
return this.state.values.map((el, i) => (
.....
<input ref={i === this.state.values.length - 1 ? this.lastRef : undefined} type="text" />
....
));
}
addClick() {
this.setState((prevState) => ({ values: [...prevState.values, ""] }),
() => {
this.lastRef.current.focus()
});
}
Demo
I am making a Kanban board project. It comes with a log in screen. I've added a button that I want to use to add new tasks to the board. However, when I submit to that button, it resets my email state to blank, which is my condition to returning to the log-in screen. Why? How can I prevent this? Any advice would be appreciated.
App.js
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
initialData,
email: this.props.email,
password: this.props.password,
showForm: false,
};
this.logOut = this.logOut.bind(this);
}
logOut() {
console.log("Logged out");
this.setState({ email: "", password: "" });
}
render() {
const { showForm } = this.state;
if (this.state.email == "") {
return <LogIn />;
} else {
{
//console.log(this.state);
}
return (
<DragDropContext onDragEnd={this.onDragEnd}>
<Container>
{this.state.initialData.columnOrder.map((columnId) => {
const column = this.state.initialData.columns[columnId];
const tasks = column.taskIds.map(
(taskId) => this.state.initialData.tasks[taskId]
);
return <Column key={column.id} column={column} tasks={tasks} />;
})}
</Container>
<button onClick={this.logOut}>Log Out</button>
<button onClick={() => this.hideComponent("showForm")}>
Create Task
</button>
<Container>
<div>
{showForm && <CreateForm onSubmit={(value) => alert(value)} />}
</div>
</Container>
</DragDropContext>
);
}
}
}
CreateForm.js
class CreateForm extends React.Component {
render() {
console.log("form");
return (
<form onSubmit={this.handleSubmit}>
<label>
Content:
<input type="text" />
</label>
<input type="submit" value="Submit" onSubmit={event => props.onSubmit(event.target.value)}/>
</form>
);
}
}
export default CreateForm;
handleSubmit(event) {
event.preventDefault(); // we need this for avoid your behavior
// your code
}
Diet.js
export class Diet extends Component {
constructor(props) {
super(props);
this.state = {
list: [],
};
this.addToList = this.addToList.bind(this);
}
addToList(item) {
const list = [...this.state.list, item];
this.setState({ list });
}
render() {
<FoodCreate addToList={this.addToList} />
return (
<FoodList items={this.state.list} />
)}
FoodCreate
export class FoodCreate extends Component {
constructor(props) {
super(props);
this.state = {
FoodName: "",
calories: 0,
};
}
render() {
return (
<Button transparent>
<Icon
name="checkmark"
style={{ fontSize: 25, color: "red" }}
onPress={() => this.props.addToList(FoodName, calories)}
/>
</Button>
<TextInput
placeholder="Food Name"
placeholderTextColor="white"
style={styles.inptFood}
value={FoodName}
onChangeText={(FoodName) => this.setState({ FoodName: FoodName })}
/>
<TextInput
placeholder="Calories"
placeholderTextColor="white"
style={styles.inptMacros}
keyboardType="numeric"
value={calories}
maxLength={5}
onChangeText={(calories) => this.setState({ calories: calories })}
/>
FoodList
export class FoodList extends Component {
render() {
return (
<Content>
<List>
<ListItem itemDivider>
<Text>Food</Text>
{this.props.items.map((item, index) => {
return (
<ListItem key={index}>
<Text>{item.FoodName}</Text>
<Text>{item.calories}</Text>
</ListItem>
);
})}
</ListItem>
</List>
</Content>
);
}
}
export default FoodList;
Hi, I'm new to programming and React Native, so I'm trying to create a Grocery List by letting the user type FoodName and Calories and pressing the Icon: Check in FoodCreate page, and List it in the FoodList page, at the moment when I run the code gives me back an error: _this2.props.addToList is not a function, I've tried many solutions but I'm not sure where the error is.
class FoodCreate extends Component {
render() {
return (
<Button title="aaa" onPress={() => this.props.addToList('name')}></Button>
);
}
}
export default class Diet extends Component {
constructor(props) {
super(props);
this.state = {
list: [],
};
this.addToList = this.addToList.bind(this);
}
addToList(item) {
const list = [...this.state.list, item];
this.setState({list});
}
render() {
return <FoodCreate addToList={this.addToList} />;
}
}
I use the above code and didn't get the error
But I think you can have a better code
Don't use this.addToList = this.addToList.bind(this);, you can convert addToList to arrow function and remove this line
addToList = item => {
const list = [...this.state.list, item];
this.setState({list});
};
I have a form that has 10+ input fields that update the state of the class. To make things look cleaner I moved all input fields with labels into a separate component so I could re-use it for each input instead. This component takes 2 parameters and serves as a child in my main class.
child component:
const Input = ({ name, placeholder }) => {
return (
<div className="wrapper">
<Row className="at_centre">
<Col sm="2" style={{ marginTop: "0.5%" }}><Form.Label>{ name }</Form.Label></Col>
<Col sm="5"><Form.Control placeholder={ placeholder }/></Col>
</Row>
</div>
)
}
parent:
state = { name: '', description: '' }
handleSubmit = (e) => {
e.preventDefault()
console.log(this.state);
}
render(){
return(
<Form style={{ marginBottom: "5%", padding: 10 }} onSubmit={ this.handleSubmit } >
<Input name="Name: " placeholder="How is it called?" onChange={ (event) => this.setState({name: event.target.value}) }/>
<Input name="Description: " placeholder="Please describe how does it look like?" onChange={ (event) => this.setState({description: event.target.value}) }/>
<Button variant="outline-success" size="lg" type="submit" >SUBMIT</Button>
</Form>
)
}
After I did that I can't find the way how to update the state from my child components when the text is changed. All my attempts to do so either crashed the website or did nothing. I am still new to React.js so any feedback is appreciated.
Pass onChange event to your child component and wire it with Form.Control control.
Your Input component will be,
const Input = ({ name, placeholder, onChange }) => {
return (
<div className="wrapper">
<Row className="at_centre">
<Col sm="2" style={{ marginTop: "0.5%" }}>
<Form.Label>{name}</Form.Label>
</Col>
<Col sm="5">
<Form.Control onChange={onChange} placeholder={placeholder} />
</Col>
</Row>
</div>
);
};
And your Parent component is,
class Parent extends React.Component {
state = { name: "", description: "" };
handleSubmit = e => {
e.preventDefault();
console.log(this.state);
};
render() {
return (
<Form
style={{ marginBottom: "5%", padding: 10 }}
onSubmit={this.handleSubmit}
>
<Input
name="Name: "
placeholder="How is it called?"
onChange={event => this.setState({ name: event.target.value })}
/>
<Input
name="Description: "
placeholder="Please describe how does it look like?"
onChange={event => this.setState({ description: event.target.value })}
/>
<Button variant="outline-success" size="lg" type="submit">
SUBMIT
</Button>
</Form>
);
}
}
Working Codesandbox here.
In React, properties flow from the parent component to the child component, so you cannot directly "pass" the state from the child to the parent.
What you can do however is to have the parent pass a callback function to the child that will be called to update the parent's state.
Here is an example:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '',
};
}
updateName(name) {
if (name === this.state.name) return;
this.setState({ name });
}
render() {
return (
<div>
<p>The name is {this.state.name}</p>
<ChildComponent handleNameUpdate={name => this.updateName(name)} />
</div>
);
}
}
class ChildComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: '',
};
}
handleInputChange(e) {
this.setState({ name: e.target.value });
this.props.handleNameUpdate(e.target.value)
}
render() {
return <input type="text" value={this.state.name} onChange={e => this.handleInputChange(e)} />;
}
}
You have to build what is known as a controlled component.
const Input = ({ label, name, onChange, placeholder }) => (
<div className="wrapper">
<Row className="at_centre">
<Col sm="2" style={{ marginTop: "0.5%" }}>
<Form.Label>{ label }</Form.Label></Col>
<Col sm="5">
<Form.Control name={ name }
value={ value }
placeholder={ placeholder }
onChange={ onChange }
/>
</Col>
</Row>
</div>
)
And in your parent,
state = { name: '', description: '' }
handleChange = ({ target: { name, value } }) => this.setState({ [name]: value })
render() {
const { name, description } = this.state
<Form style={{ marginBottom: "5%", padding: 10 }} onSubmit={ this.handleSubmit } >
<Input label="Name: " name="name" value={name} onChange={handleChange}/>
<Input label="Description: " description="description" value={description} onChange={handleChange}/>
<Button variant="outline-success" size="lg" type="submit" >SUBMIT</Button>
</Form>
}
Advice
Try to avoid manufacturing lambda methods inside the render function as much as possible and have a class property as a lambda method so that lambdas do not need to be manufactured on every render cycle.
I want to do a field input where it takes a icon from IconPicker. How can I do ?
I've tried to do something like that:
in hardware.js
export const HardwareCreate = (props) => (
<Create title={<AppTitle />} {...props}>
<SimpleForm>
...other normal fields...
<TextField label="Ícone"/>
<IconPicker source="icon"/>
</SimpleForm>
</Create>
);
And in iconpicker.js
class IconPicker extends Component {
constructor(props) {
super(props);
this.state = {
value: 'fipicon-angle-left',
};
}
handleChange = (value) => {
this.setState({ value });
console.log(this);
}
render() {
const props = {
icons: ['fipicon-angle-left', 'fipicon-angle-right', 'fipicon-angle-up', 'fipicon-angle-down'],
theme: 'bluegrey',
renderUsing: 'class',
value: this.state.value,
onChange: this.handleChange,
isMulti: false,
};
return (
<FontIconPicker {...props}/>
);
}
export default IconPicker;