I need to get a suma of the total amount of customers, the customer amount is entered through a button onClick and in total I have three shops all as different components, that are then called, how do I do it?
Component Shop
render(props) {
//##viewOn:private
//const [isOn, setIsOn] = useState(props.isOn);
const [count, setCount] = useState(0);
//##viewOff:private
//##viewOn:interface
//##viewOff:interface
//##viewOn:render
return (
<>
<div>
<h1>Kaufland</h1>
<p>Customers {count}</p>
<Uu5Elements.Button onClick={() => setCount((current) => current + 1)< 10}>+1</Uu5Elements.Button>
</div>
</>
//##viewOff:render
)},
});
//##viewOn:exports
export { Shop };
export default Shop;
//##viewOff:exports
and then I have pretty much copy-pastes of the component 2 more times for 2 more shops, fe.:
Component Shop2
render(props) {
//##viewOn:private
const [count, setCount] = useState(0);
//##viewOff:private
//##viewOn:interface
//##viewOff:interface
//##viewOn:render
return (
<>
<div>
<h1>Lidl</h1>
<p>Customers {count}</p>
<Uu5Elements.Button onClick={() => setCount((current) => current + 1)< 10}>+1</Uu5Elements.Button>
</div>
</>
//##viewOff:render
)},
});
//##viewOn:exports
export { Shop2 };
export default Shop2;
//##viewOff:exports
And then I cant piece together how to get the amount of the Shop and add it to the amount of the Shop2...
return (
<>
<RouteBar />
<div className={Css.mainContainer()}>
<div>{currentTime.toLocaleString("cs")}</div>
<h1>Total Customers</h1>
<Total sum={Shop + Shop2 />
<Shop />
<Shop2 />
</div>
</>
);
You need to define the state in the common parent component of the two shop components (Lifting State Up) so that data can be shared through props. I created an example for your reference:
Parent.js
import { useState } from "react";
import Shop1 from "./Shop1";
import Shop2 from "./Shop2";
const Parent = () => {
const [count, setCount] = useState(0);
return (
<>
<h1>Sum: {count}</h1>
<Shop1 setCount={setCount} />
<Shop2 setCount={setCount} />
</>
);
};
export default Parent;
Shop1.js
const Shop1 = ({ setCount }) => {
const add = () => {
setCount((prevCount) => prevCount + 1);
};
return <button onClick={add}>Click</button>;
};
export default Shop1;
Shop2.js
const Shop2 = ({ setCount }) => {
const add = () => {
setCount((prevCount) => prevCount + 1);
};
return <button onClick={add}>Click</button>;
};
export default Shop2;
App.js
import "./styles.css";
import Parent from "./Parent";
export default function App() {
return (
<div className="App">
<Parent />
</div>
);
}
View the online example here.
Related
I have two components App and MyComponent, where MyComponent is used in App.
import { useState } from "react";
import { MyComponent } from "./myComponent";
export const App = () => {
const [state, setState] = useState(0);
return (
<>
<MyComponent
render={() => (
<button onClick={() => setState((prev) => prev + 50)}>{state}</button>
)}
/>
</>
);
}
export const MyComponent = (props) => {
const Content = props.render;
return (
<div>
<Content/>
</div>
);
};
Is it ok to use state in the return value of the render prop? Is it considered anti-pattern?
Is it ok to use react state in render prop?
Yes, but... why? children prop was created to achieve exactly what you want here.
<MyComponent>
<button onClick={() => setState((prev) => prev + 50)}>{state}.</button>
</MyComponent>
export const MyComponent = ({ children }) => (
<div>
{children}
</div>
);
I tried to increment the count whenever i click the button. When click the button it is getting rendered twice. But it should be render only once.
Here is my code
https://codesandbox.io/s/async-pine-3z2ty3?file=/src/App.js
import { useCallback, useMemo, useState } from "react";
import Button from "./Button";
export default function App() {
const [count, setCount] = useState(0);
const [count1, setCount1] = useState(0);
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]);
const MyButton1 = useMemo(
() => <Button handleClick={handleClick} title="Increment Count" />,
[handleClick]
);
const MyButton2 = useMemo(
() => (
<Button handleClick={() => setCount1(count1 + 1)} title="Click here" />
),
[count1]
);
return (
<div className="App">
<div>count : {count}</div>
{MyButton1}
<div>count1 : {count1}</div>
{MyButton2}
</div>
);
}
import React from "react";
const Button = React.memo(({ handleClick, title }) => {
console.log(title);
return <button onClick={handleClick}>{title}</button>;
});
export default Button;
Problem
Your handleClick function changes count
If count changes new handleClick is created
If handleClick changes you create new <Button>
Solution
Remove redundant useMemos
Pass a function to setCount
Remove dependency from useCallback
export default function App() {
const [count, setCount] = useState(0);
const [count1, setCount1] = useState(0);
const handleClick = useCallback(() => {
setCount((count) => count + 1);
}, []);
return (
<div className="App">
<div>count : {count}</div>
<Button handleClick={handleClick} title="Increment Count" />
</div>
);
}
Now your component will be rendered once at the beginning and never again
If you want to have two buttons, you have to have two callbacks
export default function App() {
const [count, setCount] = useState(0);
const [count1, setCount1] = useState(0);
const handleClick = useCallback(() => {
setCount((count) => count + 1);
}, []);
const handleClick1 = useCallback(() => {
setCount1((count) => count + 1);
}, []);
return (
<div className="App">
<div>count : {count}</div>
<Button handleClick={handleClick} title="Increment Count" />
<div>count : {count}</div>
<Button handleClick={handleClick1} title="Click here" />
</div>
);
}
sandbox
Remove <StrictMode></StrictMode> from your index.js file
When I increment the state from Page1,it gets upadated for Page1. But when I go to Page2 the state becomes 0 again.I don't get the upadated state from Page1. Is there any way to create a global state across routes?
context.js
import React, { useState } from "react";
export const Context = React.createContext();
export const ContextProvider = ({ children }) => {
const [state, setstate] = useState(0);
return (
<Context.Provider value={{ state, setstate }}>{children}</Context.Provider>
);
};
Page1.js
const Page1 = () => {
const { state, setstate } = useContext(Context);
console.log("Page1: ", state);
return (
<div>
<h1>PAGE 1</h1>
{state}
<button onClick={() => setstate(state + 1)}>INC</button>
</div>
);
};
Page2.js
const Page2 = () => {
const { state, setstate } = useContext(Context);
return (
<div>
<h1>PAGE 2</h1>
{state}
<button onClick={() => setstate(state + 1)}>INC</button>
</div>
);
};
Here is a link https://codesandbox.io/s/fast-architecture-45u4m?file=/src/Todo.js
I added comments showing which code I am trying to move into a separate component.
So I have this todo app and the original code has all of my functions and logic in one component called TodoList.js
I'm trying to refactor my code, so that all the logic for the todo is in a separate component called Todo.js
Here is what the code looks like
<>
{todos.map(todo => (
<div className='todo-row'>
<div
key={todo.id}
className={todo.isComplete ? 'complete' : ''}
key={todo.id}
onClick={() => completeTodo(todo.id)}
>
{todo.text}
</div>
<FaWindowClose onClick={() => removeTodo(todo.id)} />
</div>
))}
</>
So I have this TodoList.js component with all of my state and functions, but when I tried to remove my todo code, I can't seem to figure out how to refactor it, so that the same data still gets passed in and I am able to use all of my functions again
function TodoList() {
const [todos, setTodos] = useState([]);
const addTodo = todo => {
if (!todo.text || /^\s*$/.test(todo.text)) {
return;
}
const newTodos = [todo, ...todos];
setTodos(newTodos);
console.log(newTodos);
};
const removeTodo = id => {
const removedArr = [...todos].filter(todoId => todoId.id !== id);
setTodos(removedArr);
};
const completeTodo = id => {
let updatedTodos = todos.map(todo => {
if (todo.id === id) {
todo.isComplete = !todo.isComplete;
}
return todo;
});
setTodos(updatedTodos);
};
return (
<>
<TodoForm onSubmit={addTodo} />
<Todo />
</>
);
}
export default TodoList;
Originally, I replaced the component <Todo /> with the code I showed above in the first block. But now I wanna move all of that code into it's own component called Todo.js and then pass it in from there, but I'm running into errors because I have all my functions and state logic inside of the TodoList.js component
As per your sandbox . You just need to pass Props:
Todo.js
// I want to move this code into this component
import React from "react";
import { FaWindowClose } from "react-icons/fa";
const Todo = ({ todos, completeTodo, removeTodo }) => {
return todos.map((todo) => (
<div className="todo-row">
<div
key={todo.id}
className={todo.isComplete ? "complete" : ""}
onClick={() => completeTodo(todo.id)}
>
{todo.text}
</div>
<FaWindowClose onClick={() => removeTodo(todo.id)} />
</div>
));
};
export default Todo;
TodoList.js
import React, { useState } from "react";
import TodoForm from "./TodoForm";
import Todo from "./Todo";
import { FaWindowClose } from "react-icons/fa";
function TodoList({ onClick }) {
const [todos, setTodos] = useState([]);
const addTodo = (todo) => {
if (!todo.text || /^\s*$/.test(todo.text)) {
return;
}
const newTodos = [todo, ...todos];
setTodos(newTodos);
console.log(newTodos);
};
const removeTodo = (id) => {
const removedArr = [...todos].filter((todoId) => todoId.id !== id);
setTodos(removedArr);
};
const completeTodo = (id) => {
let updatedTodos = todos.map((todo) => {
if (todo.id === id) {
todo.isComplete = !todo.isComplete;
}
return todo;
});
setTodos(updatedTodos);
};
return (
<>
<TodoForm onSubmit={addTodo} />
{/* I want to move this code below into a new component called Todo.js */}
<Todo todos={todos} completeTodo={completeTodo} removeTodo={removeTodo} />
</>
);
}
export default TodoList;
Here is the demo : https://codesandbox.io/s/nostalgic-silence-idm21?file=/src/TodoList.js:0-1039
You can pass data and required functions to Todo component through props from TodoList component and Todo component should represent only on Todo item as per name so map should stay in TodoList component so after changes
TodoList.js
function TodoList() {
const [todos, setTodos] = useState([]);
const addTodo = todo => {
if (!todo.text || /^\s*$/.test(todo.text)) {
return;
}
const newTodos = [todo, ...todos];
setTodos(newTodos);
console.log(newTodos);
};
const removeTodo = id => {
const removedArr = [...todos].filter(todoId => todoId.id !== id);
setTodos(removedArr);
};
const completeTodo = id => {
let updatedTodos = todos.map(todo => {
if (todo.id === id) {
todo.isComplete = !todo.isComplete;
}
return todo;
});
setTodos(updatedTodos);
};
return (
<>
<TodoForm onSubmit={addTodo} />
{
todos.map(todo => <Todo to do = {todo} removeTodo={removeTodo} completeTodo={completeTodo}/>)
}
</>
);
}
export default TodoList;
And Todo.js
const {todo} = props;
return (
<div className='todo-row'>
<div
key={todo.id}
className={todo.isComplete ? 'complete' : ''}
key={todo.id}
onClick={() => props.completeTodo(todo.id)}
>
{todo.text}
</div>
<FaWindowClose onClick={() => props.removeTodo(todo.id)} />
</div>
);
I have the main component as below.
const MainApp: React.FC = () => {
return (
<div>
<DummyComp/>
<ComponentA />
<ComponentB />
</div>
);
}
export default App;
"Component B" has some form elements and when it's value changes, I require all the form values to be passed to the "Component A".
All the examples that I found are not using Functional Components. So I am not sure how to create callback functions and use states in Functional Components.
const MainApp: React.FC = () => {
const [formData, setFormData ] = useState({});
return (
<div>
<DummyComp/>
<ComponentA formData={formData} />
<ComponentB onClick={setFormData} />
</div>
);
}
export default App;
You can edit formData object as you wish in ComponentB with setFormData method, and you can read the formData object in ComponentA.
You can either use Redux (Global State) to do this, OR use a state manage in the Main Component.
const MainApp: React.FC = () => {
const [data, setData] = useState();
useEffect(() => {
setData() //anyhting initial
},[formdata]);
const handleData = (data) => {
setData(data)
}
return (
<div>
<DummyComp/>
<ComponentA data={data} />
<ComponentB onformclick={handleData} />
</div>
);
}
export default App;
Lets Suppose component A is like
const ComponentA: React.FC = ({props}) => {
const data = props.data;
return (
<div>
//any html here
//example
<div>
{
data ?
data.property //any property existing on data
:null
}
</div>
</div>
);
}
export default ComponentB;
Lets Suppose component A is like
const ComponentA: React.FC = ({props}) => {
const [formdata, SetformData] = useState();
return (
<div>
//form here
//any button or any element to submit form
<button onClick={()=>props.onformclick(formdata)}> submit </button>
</div>
);
}
export default ComponentB;