Problem:
I am making a simple bulletin board after learning with the official document of react.
I created the alert component because I wanted to manage all actions that happen on the bulletin board as one alert component.
If the title is true in the Form on Submit if statement
Send messages and state data to AlertC components
When console.log(props) is performed on the AlertC component
Nothing is output.
What’s the problem?
let test = [];
return (
<>
<Form
onSubmit={(e) => {
e.preventDefault();
if (props.title !== "") {
test.push(<AlertC message="thankyou" state={true} />);
props.listHandler({ title: props.title, content: props.content });
} else {
}
}}
>
...
{test}
</Form>
</>
)
export default function AlertC(props) {
console.log(props); Nothing is output. why?
const [show, setShow] = useState(false);
return (
<Alert show={show} variant="success">
<p>{props.message}</p>
<div className="d-flex justify-content-ed">
<Button variant="secondary" onClick={() => setShow(false)}>
close
</Button>
</div>
</Alert>
);
}
export default function ListForm(props) {
let list = [];
let i = 0;
if (props.list.length !== 0) {
for (const board of props.list) {
list.push(<List data={board} key={i} />);
i++;
}
}
export default function List(props) {
console.log(props) //Output to console
return (
<tr>
<td>{props.data.title}</td>
<td>{props.data.content}</td>
</tr>
);
}
Why isn’t the above case printed?
Solution:
For data to be displayed the component needs to re-render with the updated state. React re-renders when you tell it that some state has changed. As you can see in the example below the array gets filled, however React doesn’t know that the contents of the array have changed and therefore it does not re-render => nothing is displayed.
function MyComponent() {
const test = ["first item"];
return (
<React.Fragment>
<button onClick={() => {
console.log(test);
test.push("another item")
}}>
Add to list without triggering a re-render by state update
</button>
<ul>
{test.map(x => <li>{x}</li>)}
</ul>
</React.Fragment>
)
}
ReactDOM.render(<MyComponent />, document.getElementById("root"));
/* prevent console from overlapping rendered content */
.as-console-wrapper {
max-height: 60% !important;
bottom: 0;
}
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<div id="root"></div>
To make this work you need to use the useState()
hook. You can then use setTest()
to update the state and thereby tell React that the component should be re-rendered to display the changes.
It is absolutely crucial to create a new Array here and not just push()
a new value in since otherwise the reference doesn’t change and React won’t be able to detect the change.
function MyComponent() {
const [test, setTest] = React.useState(["first item"]);
return (
<React.Fragment>
<button onClick={() => setTest([...test, "another item"])}>
Add to list with triggering a re-render by state update
</button>
<ul>
{test.map(x => <li>{x}</li>)}
</ul>
</React.Fragment>
)
}
ReactDOM.render(<MyComponent />, document.getElementById("root"));
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I suggest you go back to the Tutorial again. All of the above is mentioned there as well since this is the most important concept to understand when developing in React.