diff --git a/README.md b/README.md index 790c79e..20fca65 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ A customizable countdown component for React. * [Props](#props) * [API Reference](#api-reference) * [Helpers](#helpers) +* [FAQ](#faq) * [Contributing](#contributing) * [License](#license) @@ -31,7 +32,7 @@ As part of a small web app at first, the idea was to separate the countdown comp Here are some examples which you can try directly online. You can also clone this repo and explore some more examples in there by running `yarn start` within the `examples` folder. ### Basic Usage -A very simple and minimal example of how to set up a countdown which counts down from 10 seconds. +A very simple and minimal example of how to set up a countdown that counts down from 10 seconds. ```js import React from 'react'; import ReactDOM from 'react-dom'; @@ -45,7 +46,7 @@ ReactDOM.render( [Live Demo](https://codesandbox.io/s/cool-fermat-uk0dq) ### Custom & Conditional Rendering -In case you want to change the output of the component, or want to signal that the countdown's work is done, you can do this by either using the [`onComplete`](#oncomplete) callback, a +In case you want to change the output of the component or want to signal that the countdown's work is done, you can do this by either using the [`onComplete`](#oncomplete) callback, a custom [`renderer`](#renderer), or by specifying a React child within ``, which will only be shown once the countdown is complete. #### Using a React Child for the Completed State @@ -124,8 +125,8 @@ ReactDOM.render( |Name|Type|Default|Description| |:--|:--:|:-----:|:----------| -|[**date**](#date)|Date|string|number|`required`|Date or timestamp in the future| -|[**key**](#key)|string|number|`undefined`|React [**key**](https://reactjs.org/docs/lists-and-keys.html#keys); can be used to restart the countdown| +|[**date**](#date)|Date|string|number|required|[`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) or timestamp in the future| +|[**key**](#key)|string|number|`undefined`|React [`key`](https://reactjs.org/docs/lists-and-keys.html#keys); can be used to restart the countdown| |[**daysInHours**](#daysinhours)|`boolean`|`false`|Days are calculated as hours| |[**zeroPadTime**](#zeropadtime)|`number`|`2`|Length of zero-padded output, e.g.: `00:01:02`| |[**zeroPadDays**](#zeropaddays)|`number`|`zeroPadTime`|Length of zero-padded days output, e.g.: `01`| @@ -153,8 +154,8 @@ Valid values can be _(and more)_: * `new Date(1580518923000)` // [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) object ### `key` -This is one of React's internal component props and is used to identify the component. However, we can leverage this behavior and use it to, for example, restart the countdown by -passing in a new `string` or `number`. +This is one of React's internal component props to help identify elements throughout the reconciliation process. It can be used to restart the countdown by +passing in a new `string` or `number` value. Please see [official React docs](https://reactjs.org/docs/lists-and-keys.html#keys) for more information about keys. @@ -169,7 +170,7 @@ provided [`date`](#date) will be treated as the countdown's actual time differen This option defaults to `2` in order to display the common format `00:00:00` instead of `0:0:0`. If the value is higher than `2`, only the hours part _(see [`zeroPadDays`](#zeropaddays) for days)_ will be zero-padded while it stays at `2` for minutes as well as seconds. If the value is lower, the output won't be zero-padded like the example before is showing. ### `zeroPadDays` -Defaults to `zeroPadTime`. Works the same way as [`zeroPadTime`](#zeropadtime) does, just for days. +Defaults to `zeroPadTime`. It works the same way as [`zeroPadTime`](#zeropadtime) does, just for days. ### `intervalDelay` Since this countdown is based on date comparisons, the default value of `1000` milliseconds is probably enough for most scenarios and doesn't need to be changed. @@ -177,7 +178,7 @@ Since this countdown is based on date comparisons, the default value of `1000` m However, if it needs to be more precise, the `intervalDelay` can be set to something lower - down to `0`, which would, for example, allow showing the milliseconds in a more fancy way (_currently_ only possible through a custom [`renderer`](#renderer)). ### `precision` -In certain cases, you might want to base off the calculations on a millisecond basis. The `precision` prop, which defaults to `0`, can be used to refine this calculation. While the default value simply strips the milliseconds part (e.g.: `10123`ms => `10000`ms), a precision of `3` leads to `10123`ms. +In certain cases, you might want to base off the calculations on a millisecond basis. The `precision` prop, which defaults to `0`, can be used to refine this calculation. While the default value simply strips the milliseconds part (e.g., `10123`ms => `10000`ms), a precision of `3` leads to `10123`ms. ### `autoStart` Defines whether the countdown should start automatically or not. Defaults to `true`. @@ -187,7 +188,7 @@ Defines whether the countdown can go into overtime by extending its lifetime pas When set to `true`, the countdown timer won't stop when hitting 0, but instead becomes negative and continues to run unless paused/stopped. The [`onComplete`](#oncomplete) callback would still get triggered when the initial countdown phase completes. -> Please note that the [`children`](#children) prop will be ignored if `overtime` is `true`. +> Please note that the [`children`](#children) prop will be ignored if `overtime` is `true`. Also, when using a custom [`renderer`](#renderer), you'll have to check one of the [render props](#render-props), e.g., `total`, or `completed`, to render the overtime output. ### `children` This component also considers the child that may live within the `` element, which, in case it's available, replaces the countdown's component state once it's complete. Moreover, an additional prop called `countdown` is set and contains data similar to what the [`renderer`](#renderer) callback would receive. Here's an [example](#using-a-react-child-for-the-completed-state) that showcases its usage. @@ -224,26 +225,26 @@ The render props object consists of the current time delta object, the countdown If the current date and time (determined via a reference to `Date.now`) is not the right thing to compare with for you, a reference to a custom function that returns a similar dynamic value could be provided as an alternative. ### `onMount` -`onMount` is a callback and triggered when the countdown mounts. It receives the time delta object, which is returned by [`calcTimeDelta`](#calctimedelta). +`onMount` is a callback and triggered when the countdown mounts. It receives a [time delta object](#calctimedelta) as the first argument. ### `onStart` -`onStart` is a callback and triggered whenever the countdown is started (including first-run). It receives the time delta object, which is returned by [`calcTimeDelta`](#calctimedelta). +`onStart` is a callback and triggered whenever the countdown is started (including first-run). It receives a [time delta object](#calctimedelta) as the first argument. ### `onPause` -`onPause` is a callback and triggered every time the countdown is paused. It receives the time delta object, which is returned by [`calcTimeDelta`](#calctimedelta). +`onPause` is a callback and triggered every time the countdown is paused. It receives a [time delta object](#calctimedelta) as the first argument. ### `onStop` -`onStop` is a callback and triggered every time the countdown is stopped. It receives the time delta object, which is returned by [`calcTimeDelta`](#calctimedelta). +`onStop` is a callback and triggered every time the countdown is stopped. It receives a [time delta object](#calctimedelta) as the first argument. ### `onTick` -`onTick` is a callback and triggered every time a new period is started, based on what the [`intervalDelay`](#intervaldelay)'s value is. It only gets triggered when the countdown's [`controlled`](#controlled) prop is set to `false`, meaning that the countdown has full control over its interval. It receives the time delta object, which is returned by [`calcTimeDelta`](#calctimedelta). +`onTick` is a callback and triggered every time a new period is started, based on what the [`intervalDelay`](#intervaldelay)'s value is. It only gets triggered when the countdown's [`controlled`](#controlled) prop is set to `false`, meaning that the countdown has full control over its interval. It receives a [time delta object](#calctimedelta) as the first argument. ### `onComplete` -`onComplete` is a callback and triggered whenever the countdown ends. In contrast to [`onTick`](#ontick), the [`onComplete`](#oncomplete) callback gets also triggered in case [`controlled`](#controlled) is set to `true`. It receives the time delta object, which is returned by [`calcTimeDelta`](#calctimedelta). +`onComplete` is a callback and triggered whenever the countdown ends. In contrast to [`onTick`](#ontick), the [`onComplete`](#oncomplete) callback also gets triggered in case [`controlled`](#controlled) is set to `true`. It receives a [time delta object](#calctimedelta) as the first argument. ## API Reference -The countdown component exposes a simple API through the `getApi()` function that can be accessed via component `ref`. It is also part (`api`) of the render props passed into [`renderer`](#renderer) if needed. +The countdown component exposes a simple API through the `getApi()` function that can be accessed via component `ref`. It is also part (`api`) of the [render props](#render-props) passed into [`renderer`](#renderer) if needed. Here's an [example](https://github.com/ndresx/react-countdown/blob/master/examples/src/CountdownApi.tsx) of how to use it. ### `start()` Starts the countdown in case it is paused/stopped or needed when [`autoStart`](#autostart) is set to `false`. @@ -274,7 +275,7 @@ import Countdown, { zeroPad, calcTimeDelta, formatTimeDelta } from 'react-countd ``` ### `zeroPad(value, [length = 2])` -The `zeroPad` function works similarly to other well-known pad-functions and takes two arguments into account. A `value` which can be a `string` or `number`, as well as a `length` parameter which defaults to `2` as you are most likely only going to use this function if you actually want to pad one of your values. Either returns a `number` if `length` equals `0`, or the zero-padded `string`. +The `zeroPad` function works similarly to other well-known pad-functions and takes two arguments into account. A `value` which can be a `string` or `number`, as well as a `length` parameter that defaults to `2` as you are most likely only going to use this function if you actually want to pad one of your values. Either returns a `number` if `length` equals `0`, or the zero-padded `string`. ```js const renderer = ({ hours, minutes, seconds }) => ( @@ -292,7 +293,9 @@ const renderer = ({ hours, minutes, seconds }) => ( { total, days, hours, minutes, seconds, milliseconds, completed } ``` -This function accepts two arguments in total; only the first one is required. +The `total` value is the absolute time difference in milliseconds, whereas the other time-related values contain their relative portion of the current time difference. The `completed` value signalizes whether the countdown reached its initial end or not. + +The `calcTimeDelta` function accepts two arguments in total; only the first one is required. **`date`** Date or timestamp representation of the end date. See [`date`](#date) prop for more details. @@ -312,7 +315,7 @@ Defines whether the calculated value is provided in a [`controlled`](#controlled Defines the offset time that gets added to the start time; only considered if controlled is false. - **`overtime = false`** -Defines whether the time delta can go into [`overtime`](#overtime) and become negative or not. When set to `true`, the `total` could become negative at which point `completed` will still be set to `true`. +Defines whether the time delta can go into [`overtime`](#overtime) and become negative or not. When set to `true`, the `total` could become negative, at which point `completed` will still be set to `true`. ### `formatTimeDelta(timeDelta, [options])` @@ -338,6 +341,25 @@ The `options` object consists of the following three component props and is used * [`zeroPadTime`](#zeropadtime) * [`zeroPadDays`](#zeropaddays) +## FAQ + +### Why does my countdown reset on every re-render? + +A common reason for this is that the [`date`](#date) prop gets passed directly into the component without persisting it in any way. + +In order to avoid this from happening, it should be stored in a place that persists throughout lifecycle changes, for example, in the component's local `state`. + + +### Why aren't my values formatted when using the custom [`renderer`](#renderer)? + +The [`renderer`](#renderer) callback gets called with a [time delta object](#calctimedelta) that also consists of a `formatted` object which holds these formatted values. + +### Why do I get this error `"Warning: Text content did not match..."`? + +This could have something to do with server-side rendering and that the countdown already runs on the server-side, resulting in a timestamp discrepancy between the client and the server. In this case, it might be worth checking https://reactjs.org/docs/dom-elements.html#suppresshydrationwarning. + +Alternatively, you could try to set [`autoStart`](#autostart) to `false` and start the countdown through the [API](#api-reference) once it's available on the client. Here are some related [issues](https://github.com/ndresx/react-countdown/issues/152) that might help in fixing this problem. + ## Contributing Contributions of any kind are very welcome. Read more in our [contributing guide](https://github.com/ndresx/react-countdown/blob/master/CONTRIBUTING.md) about how to report bugs, create pull requests, and other development-related topics.